<commit_before>//! Tests and test utilities for completions.
//!
//! Most tests live in this module or its submodules. The tests in these submodules are "location"
//! oriented, that is they try to check completions for something like type position, param position
//! etc.
//! Tests that are more orientated towards specific completion types like visibility checks of path
//! completions or `check_edit` tests usually live in their respective completion modules instead.
//! This gives this test module and its submodules here the main purpose of giving the developer an
//! overview of whats being completed where, not how.

mod attribute;
mod expression;
mod flyimport;
mod fn_param;
mod item_list;
mod item;
mod pattern;
mod predicate;
mod proc_macros;
mod record;
mod special;
mod type_pos;
mod use_tree;
mod visibility;

use std::mem;

use hir::{db::DefDatabase, PrefixKind, Semantics};
use ide_db::{
    base_db::{fixture::ChangeFixture, FileLoader, FilePosition},
    imports::insert_use::{ImportGranularity, InsertUseConfig},
    RootDatabase, SnippetCap,
};
use itertools::Itertools;
use stdx::{format_to, trim_indent};
use syntax::{AstNode, NodeOrToken, SyntaxElement};
use test_utils::assert_eq_text;

use crate::{
    resolve_completion_edits, CallableSnippets, CompletionConfig, CompletionItem,
    CompletionItemKind,
};

/// Lots of basic item definitions
const BASE_ITEMS_FIXTURE: &str = r#"
enum Enum { TupleV(u32), RecordV { field: u32 }, UnitV }
use self::Enum::TupleV;
mod module {}

trait Trait {}
static STATIC: Unit = Unit;
const CONST: Unit = Unit;
struct Record { field: u32 }
struct Tuple(u32);
struct Unit;
#[macro_export]
macro_rules! makro {}
#[rustc_builtin_macro]
pub macro Clone {}
fn function() {}
union Union { field: i32 }
"#;

pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig {
    enable_postfix_completions: true,
    enable_imports_on_the_fly: true,
    enable_self_on_the_fly: true,
    enable_private_editable: false,
    callable: Some(CallableSnippets::FillArguments),
    snippet_cap: SnippetCap::new(true),
    insert_use: InsertUseConfig {
        granularity: ImportGranularity::Crate,
        prefix_kind: PrefixKind::Plain,
        enforce_granularity: true,
        group: true,
        skip_glob_imports: true,
    },
    snippets: Vec::new(),
};

pub(crate) fn completion_list(ra_fixture: &str) -> String {
    completion_list_with_config(TEST_CONFIG, ra_fixture, true, None)
}

pub(crate) fn completion_list_no_kw(ra_fixture: &str) -> String {
    completion_list_with_config(TEST_CONFIG, ra_fixture, false, None)
}

pub(crate) fn completion_list_no_kw_with_private_editable(ra_fixture: &str) -> String {
    let mut config = TEST_CONFIG.clone();
    config.enable_private_editable = true;
    completion_list_with_config(config, ra_fixture, false, None)
}

pub(crate) fn completion_list_with_trigger_character(
    ra_fixture: &str,
    trigger_character: Option<char>,
) -> String {
    completion_list_with_config(TEST_CONFIG, ra_fixture, true, trigger_character)
}

fn completion_list_with_config(
    config: CompletionConfig,
    ra_fixture: &str,
    include_keywords: bool,
    trigger_character: Option<char>,
) -> String {
    // filter out all but one builtintype completion for smaller test outputs
    let items = get_all_items(config, ra_fixture, trigger_character);
    let mut bt_seen = false;
    let items = items
        .into_iter()
        .filter(|it| {
            it.kind() != CompletionItemKind::BuiltinType || !mem::replace(&mut bt_seen, true)
        })
        .filter(|it| include_keywords || it.kind() != CompletionItemKind::Keyword)
        .filter(|it| include_keywords || it.kind() != CompletionItemKind::Snippet)
        .sorted_by_key(|it| (it.kind(), it.label().to_owned(), it.detail().map(ToOwned::to_owned)))
        .collect();
    render_completion_list(items)
}

/// Creates analysis from a multi-file fixture, returns positions marked with $0.
pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
    let change_fixture = ChangeFixture::parse(ra_fixture);
    let mut database = RootDatabase::default();
    database.set_enable_proc_attr_macros(true);
    database.apply_change(change_fixture.change);
    let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
    let offset = range_or_offset.expect_offset();
    (database, FilePosition { file_id, offset })
}

pub(crate) fn do_completion(code: &str, kind: CompletionItemKind) -> Vec<CompletionItem> {
    do_completion_with_config(TEST_CONFIG, code, kind)
}

pub(crate) fn do_completion_with_config(
    config: CompletionConfig,
    code: &str,
    kind: CompletionItemKind,
) -> Vec<CompletionItem> {
    get_all_items(config, code, None)
        .into_iter()
        .filter(|c| c.kind() == kind)
        .sorted_by(|l, r| l.label().cmp(r.label()))
        .collect()
}

fn render_completion_list(completions: Vec<CompletionItem>) -> String {
    fn monospace_width(s: &str) -> usize {
        s.chars().count()
    }
    let label_width =
        completions.iter().map(|it| monospace_width(it.label())).max().unwrap_or_default().min(22);
    completions
        .into_iter()
        .map(|it| {
            let tag = it.kind().tag();
            let var_name = format!("{} {}", tag, it.label());
            let mut buf = var_name;
            if let Some(detail) = it.detail() {
                let width = label_width.saturating_sub(monospace_width(it.label()));
                format_to!(buf, "{:width$} {}", "", detail, width = width);
            }
            if it.deprecated() {
                format_to!(buf, " DEPRECATED");
            }
            format_to!(buf, "\n");
            buf
        })
        .collect()
}

#[track_caller]
pub(crate) fn check_edit(what: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
    check_edit_with_config(TEST_CONFIG, what, ra_fixture_before, ra_fixture_after)
}

#[track_caller]
pub(crate) fn check_edit_with_config(
    config: CompletionConfig,
    what: &str,
    ra_fixture_before: &str,
    ra_fixture_after: &str,
) {
    let ra_fixture_after = trim_indent(ra_fixture_after);
    let (db, position) = position(ra_fixture_before);
    let completions: Vec<CompletionItem> =
        crate::completions(&db, &config, position, None).unwrap().into();
    let (completion,) = completions
        .iter()
        .filter(|it| it.lookup() == what)
        .collect_tuple()
        .unwrap_or_else(|| panic!("can't find {:?} completion in {:#?}", what, completions));
    let mut actual = db.file_text(position.file_id).to_string();

    let mut combined_edit = completion.text_edit().to_owned();

    resolve_completion_edits(
        &db,
        &config,
        position,
        completion.imports_to_add().iter().filter_map(|import_edit| {
            let import_path = &import_edit.import_path;
            let import_name = import_path.segments().last()?;
            Some((import_path.to_string(), import_name.to_string()))
        }),
    )
    .into_iter()
    .flatten()
    .for_each(|text_edit| {
        combined_edit.union(text_edit).expect(
            "Failed to apply completion resolve changes: change ranges overlap, but should not",
        )
    });

    combined_edit.apply(&mut actual);
    assert_eq_text!(&ra_fixture_after, &actual)
}

pub(crate) fn check_pattern_is_applicable(code: &str, check: impl FnOnce(SyntaxElement) -> bool) {
    let (db, pos) = position(code);

    let sema = Semantics::new(&db);
    let original_file = sema.parse(pos.file_id);
    let token = original_file.syntax().token_at_offset(pos.offset).left_biased().unwrap();
    assert!(check(NodeOrToken::Token(token)));
}

pub(crate) fn get_all_items(
    config: CompletionConfig,
    code: &str,
    trigger_character: Option<char>,
) -> Vec<CompletionItem> {
    let (db, position) = position(code);
    let res = crate::completions(&db, &config, position, trigger_character)
        .map_or_else(Vec::default, Into::into);
    // validate
    res.iter().for_each(|it| {
        let sr = it.source_range();
        assert!(
            sr.contains_inclusive(position.offset),
            "source range {sr:?} does not contain the offset {:?} of the completion request: {it:?}",
            position.offset
        );
    });
    res
}

#[test]
fn test_no_completions_required() {
    assert_eq!(completion_list(r#"fn foo() { for i i$0 }"#), String::new());
}

#[test]
fn regression_10042() {
    completion_list(
        r#"
macro_rules! preset {
    ($($x:ident)&&*) => {
        {
            let mut v = Vec::new();
            $(
                v.push($x.into());
            )*
            v
        }
    };
}

fn foo() {
    preset!(foo$0);
}
"#,
    );
}

#[test]
fn no_completions_in_comments() {
    assert_eq!(
        completion_list(
            r#"
fn test() {
let x = 2; // A comment$0
}
"#,
        ),
        String::new(),
    );
    assert_eq!(
        completion_list(
            r#"
/*
Some multi-line comment$0
*/
"#,
        ),
        String::new(),
    );
    assert_eq!(
        completion_list(
            r#"
/// Some doc comment
/// let test$0 = 1
"#,
        ),
        String::new(),
    );
}
<commit_msg>Be more explicit when filtering built-in completions<commit_after>//! Tests and test utilities for completions.
//!
//! Most tests live in this module or its submodules. The tests in these submodules are "location"
//! oriented, that is they try to check completions for something like type position, param position
//! etc.
//! Tests that are more orientated towards specific completion types like visibility checks of path
//! completions or `check_edit` tests usually live in their respective completion modules instead.
//! This gives this test module and its submodules here the main purpose of giving the developer an
//! overview of whats being completed where, not how.

mod attribute;
mod expression;
mod flyimport;
mod fn_param;
mod item_list;
mod item;
mod pattern;
mod predicate;
mod proc_macros;
mod record;
mod special;
mod type_pos;
mod use_tree;
mod visibility;

use hir::{db::DefDatabase, PrefixKind, Semantics};
use ide_db::{
    base_db::{fixture::ChangeFixture, FileLoader, FilePosition},
    imports::insert_use::{ImportGranularity, InsertUseConfig},
    RootDatabase, SnippetCap,
};
use itertools::Itertools;
use stdx::{format_to, trim_indent};
use syntax::{AstNode, NodeOrToken, SyntaxElement};
use test_utils::assert_eq_text;

use crate::{
    resolve_completion_edits, CallableSnippets, CompletionConfig, CompletionItem,
    CompletionItemKind,
};

/// Lots of basic item definitions
const BASE_ITEMS_FIXTURE: &str = r#"
enum Enum { TupleV(u32), RecordV { field: u32 }, UnitV }
use self::Enum::TupleV;
mod module {}

trait Trait {}
static STATIC: Unit = Unit;
const CONST: Unit = Unit;
struct Record { field: u32 }
struct Tuple(u32);
struct Unit;
#[macro_export]
macro_rules! makro {}
#[rustc_builtin_macro]
pub macro Clone {}
fn function() {}
union Union { field: i32 }
"#;

pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig {
    enable_postfix_completions: true,
    enable_imports_on_the_fly: true,
    enable_self_on_the_fly: true,
    enable_private_editable: false,
    callable: Some(CallableSnippets::FillArguments),
    snippet_cap: SnippetCap::new(true),
    insert_use: InsertUseConfig {
        granularity: ImportGranularity::Crate,
        prefix_kind: PrefixKind::Plain,
        enforce_granularity: true,
        group: true,
        skip_glob_imports: true,
    },
    snippets: Vec::new(),
};

pub(crate) fn completion_list(ra_fixture: &str) -> String {
    completion_list_with_config(TEST_CONFIG, ra_fixture, true, None)
}

pub(crate) fn completion_list_no_kw(ra_fixture: &str) -> String {
    completion_list_with_config(TEST_CONFIG, ra_fixture, false, None)
}

pub(crate) fn completion_list_no_kw_with_private_editable(ra_fixture: &str) -> String {
    let mut config = TEST_CONFIG.clone();
    config.enable_private_editable = true;
    completion_list_with_config(config, ra_fixture, false, None)
}

pub(crate) fn completion_list_with_trigger_character(
    ra_fixture: &str,
    trigger_character: Option<char>,
) -> String {
    completion_list_with_config(TEST_CONFIG, ra_fixture, true, trigger_character)
}

fn completion_list_with_config(
    config: CompletionConfig,
    ra_fixture: &str,
    include_keywords: bool,
    trigger_character: Option<char>,
) -> String {
    // filter out all but one builtintype completion for smaller test outputs
    let items = get_all_items(config, ra_fixture, trigger_character);
    let items = items
        .into_iter()
        .filter(|it| it.kind() != CompletionItemKind::BuiltinType || it.label() == "u32")
        .filter(|it| include_keywords || it.kind() != CompletionItemKind::Keyword)
        .filter(|it| include_keywords || it.kind() != CompletionItemKind::Snippet)
        .sorted_by_key(|it| (it.kind(), it.label().to_owned(), it.detail().map(ToOwned::to_owned)))
        .collect();
    render_completion_list(items)
}

/// Creates analysis from a multi-file fixture, returns positions marked with $0.
pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
    let change_fixture = ChangeFixture::parse(ra_fixture);
    let mut database = RootDatabase::default();
    database.set_enable_proc_attr_macros(true);
    database.apply_change(change_fixture.change);
    let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
    let offset = range_or_offset.expect_offset();
    (database, FilePosition { file_id, offset })
}

pub(crate) fn do_completion(code: &str, kind: CompletionItemKind) -> Vec<CompletionItem> {
    do_completion_with_config(TEST_CONFIG, code, kind)
}

pub(crate) fn do_completion_with_config(
    config: CompletionConfig,
    code: &str,
    kind: CompletionItemKind,
) -> Vec<CompletionItem> {
    get_all_items(config, code, None)
        .into_iter()
        .filter(|c| c.kind() == kind)
        .sorted_by(|l, r| l.label().cmp(r.label()))
        .collect()
}

fn render_completion_list(completions: Vec<CompletionItem>) -> String {
    fn monospace_width(s: &str) -> usize {
        s.chars().count()
    }
    let label_width =
        completions.iter().map(|it| monospace_width(it.label())).max().unwrap_or_default().min(22);
    completions
        .into_iter()
        .map(|it| {
            let tag = it.kind().tag();
            let var_name = format!("{} {}", tag, it.label());
            let mut buf = var_name;
            if let Some(detail) = it.detail() {
                let width = label_width.saturating_sub(monospace_width(it.label()));
                format_to!(buf, "{:width$} {}", "", detail, width = width);
            }
            if it.deprecated() {
                format_to!(buf, " DEPRECATED");
            }
            format_to!(buf, "\n");
            buf
        })
        .collect()
}

#[track_caller]
pub(crate) fn check_edit(what: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
    check_edit_with_config(TEST_CONFIG, what, ra_fixture_before, ra_fixture_after)
}

#[track_caller]
pub(crate) fn check_edit_with_config(
    config: CompletionConfig,
    what: &str,
    ra_fixture_before: &str,
    ra_fixture_after: &str,
) {
    let ra_fixture_after = trim_indent(ra_fixture_after);
    let (db, position) = position(ra_fixture_before);
    let completions: Vec<CompletionItem> =
        crate::completions(&db, &config, position, None).unwrap().into();
    let (completion,) = completions
        .iter()
        .filter(|it| it.lookup() == what)
        .collect_tuple()
        .unwrap_or_else(|| panic!("can't find {:?} completion in {:#?}", what, completions));
    let mut actual = db.file_text(position.file_id).to_string();

    let mut combined_edit = completion.text_edit().to_owned();

    resolve_completion_edits(
        &db,
        &config,
        position,
        completion.imports_to_add().iter().filter_map(|import_edit| {
            let import_path = &import_edit.import_path;
            let import_name = import_path.segments().last()?;
            Some((import_path.to_string(), import_name.to_string()))
        }),
    )
    .into_iter()
    .flatten()
    .for_each(|text_edit| {
        combined_edit.union(text_edit).expect(
            "Failed to apply completion resolve changes: change ranges overlap, but should not",
        )
    });

    combined_edit.apply(&mut actual);
    assert_eq_text!(&ra_fixture_after, &actual)
}

pub(crate) fn check_pattern_is_applicable(code: &str, check: impl FnOnce(SyntaxElement) -> bool) {
    let (db, pos) = position(code);

    let sema = Semantics::new(&db);
    let original_file = sema.parse(pos.file_id);
    let token = original_file.syntax().token_at_offset(pos.offset).left_biased().unwrap();
    assert!(check(NodeOrToken::Token(token)));
}

pub(crate) fn get_all_items(
    config: CompletionConfig,
    code: &str,
    trigger_character: Option<char>,
) -> Vec<CompletionItem> {
    let (db, position) = position(code);
    let res = crate::completions(&db, &config, position, trigger_character)
        .map_or_else(Vec::default, Into::into);
    // validate
    res.iter().for_each(|it| {
        let sr = it.source_range();
        assert!(
            sr.contains_inclusive(position.offset),
            "source range {sr:?} does not contain the offset {:?} of the completion request: {it:?}",
            position.offset
        );
    });
    res
}

#[test]
fn test_no_completions_required() {
    assert_eq!(completion_list(r#"fn foo() { for i i$0 }"#), String::new());
}

#[test]
fn regression_10042() {
    completion_list(
        r#"
macro_rules! preset {
    ($($x:ident)&&*) => {
        {
            let mut v = Vec::new();
            $(
                v.push($x.into());
            )*
            v
        }
    };
}

fn foo() {
    preset!(foo$0);
}
"#,
    );
}

#[test]
fn no_completions_in_comments() {
    assert_eq!(
        completion_list(
            r#"
fn test() {
let x = 2; // A comment$0
}
"#,
        ),
        String::new(),
    );
    assert_eq!(
        completion_list(
            r#"
/*
Some multi-line comment$0
*/
"#,
        ),
        String::new(),
    );
    assert_eq!(
        completion_list(
            r#"
/// Some doc comment
/// let test$0 = 1
"#,
        ),
        String::new(),
    );
}
<|endoftext|>


<commit_before>use std::{
    error::Error,
    io::{Read, Write},
    process::{Command, Stdio},
    sync::Arc,
};

use dot::Id;
use ide_db::{
    base_db::{CrateGraph, CrateId, Dependency, SourceDatabase},
    RootDatabase,
};

// Feature: View Crate Graph
//
// Renders the currently loaded crate graph as an SVG graphic. Requires the `dot` tool to be
// installed.
//
// |===
// | Editor  | Action Name
//
// | VS Code | **Rust Analyzer: View Crate Graph**
// |===
pub(crate) fn view_crate_graph(db: &RootDatabase) -> Result<String, String> {
    let mut dot = Vec::new();
    let graph = DotCrateGraph(db.crate_graph());
    dot::render(&graph, &mut dot).unwrap();

    render_svg(&dot).map_err(|e| e.to_string())
}

fn render_svg(dot: &[u8]) -> Result<String, Box<dyn Error>> {
    // We shell out to `dot` to render to SVG, as there does not seem to be a pure-Rust renderer.
    let child = Command::new("dot")
        .arg("-Tsvg")
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .spawn()
        .map_err(|err| format!("failed to spawn `dot -Tsvg`: {}", err))?;
    child.stdin.unwrap().write_all(&dot)?;

    let mut svg = String::new();
    child.stdout.unwrap().read_to_string(&mut svg)?;
    Ok(svg)
}

struct DotCrateGraph(Arc<CrateGraph>);

type Edge<'a> = (CrateId, &'a Dependency);

impl<'a> dot::GraphWalk<'a, CrateId, Edge<'a>> for DotCrateGraph {
    fn nodes(&'a self) -> dot::Nodes<'a, CrateId> {
        self.0.iter().collect()
    }

    fn edges(&'a self) -> dot::Edges<'a, Edge<'a>> {
        self.0
            .iter()
            .flat_map(|krate| self.0[krate].dependencies.iter().map(move |dep| (krate, dep)))
            .collect()
    }

    fn source(&'a self, edge: &Edge<'a>) -> CrateId {
        edge.0
    }

    fn target(&'a self, edge: &Edge<'a>) -> CrateId {
        edge.1.crate_id
    }
}

impl<'a> dot::Labeller<'a, CrateId, Edge<'a>> for DotCrateGraph {
    fn graph_id(&'a self) -> Id<'a> {
        Id::new("rust_analyzer_crate_graph").unwrap()
    }

    fn node_id(&'a self, n: &CrateId) -> Id<'a> {
        let name = self.0[*n].display_name.as_ref().map_or("_missing_name_", |name| &*name);
        Id::new(name).unwrap()
    }
}
<commit_msg>Distinguish crates with identical name<commit_after>use std::{
    error::Error,
    io::{Read, Write},
    process::{Command, Stdio},
    sync::Arc,
};

use dot::Id;
use ide_db::{
    base_db::{CrateGraph, CrateId, Dependency, SourceDatabase},
    RootDatabase,
};

// Feature: View Crate Graph
//
// Renders the currently loaded crate graph as an SVG graphic. Requires the `dot` tool to be
// installed.
//
// |===
// | Editor  | Action Name
//
// | VS Code | **Rust Analyzer: View Crate Graph**
// |===
pub(crate) fn view_crate_graph(db: &RootDatabase) -> Result<String, String> {
    let mut dot = Vec::new();
    let graph = DotCrateGraph(db.crate_graph());
    dot::render(&graph, &mut dot).unwrap();

    render_svg(&dot).map_err(|e| e.to_string())
}

fn render_svg(dot: &[u8]) -> Result<String, Box<dyn Error>> {
    // We shell out to `dot` to render to SVG, as there does not seem to be a pure-Rust renderer.
    let child = Command::new("dot")
        .arg("-Tsvg")
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .spawn()
        .map_err(|err| format!("failed to spawn `dot -Tsvg`: {}", err))?;
    child.stdin.unwrap().write_all(&dot)?;

    let mut svg = String::new();
    child.stdout.unwrap().read_to_string(&mut svg)?;
    Ok(svg)
}

struct DotCrateGraph(Arc<CrateGraph>);

type Edge<'a> = (CrateId, &'a Dependency);

impl<'a> dot::GraphWalk<'a, CrateId, Edge<'a>> for DotCrateGraph {
    fn nodes(&'a self) -> dot::Nodes<'a, CrateId> {
        self.0.iter().collect()
    }

    fn edges(&'a self) -> dot::Edges<'a, Edge<'a>> {
        self.0
            .iter()
            .flat_map(|krate| self.0[krate].dependencies.iter().map(move |dep| (krate, dep)))
            .collect()
    }

    fn source(&'a self, edge: &Edge<'a>) -> CrateId {
        edge.0
    }

    fn target(&'a self, edge: &Edge<'a>) -> CrateId {
        edge.1.crate_id
    }
}

impl<'a> dot::Labeller<'a, CrateId, Edge<'a>> for DotCrateGraph {
    fn graph_id(&'a self) -> Id<'a> {
        Id::new("rust_analyzer_crate_graph").unwrap()
    }

    fn node_id(&'a self, n: &CrateId) -> Id<'a> {
        let name = self.0[*n].display_name.as_ref().map_or("_missing_name_", |name| &*name);
        Id::new(format!("{}_{}", name, n.0)).unwrap()
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2015-2019 Benjamin Fry <benjaminfry@me.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

//! record type definitions

use std::cmp::Ordering;
use std::convert::From;
use std::fmt;
use std::fmt::{Display, Formatter};
use std::str::FromStr;

use error::*;
use serialize::binary::*;

#[cfg(feature = "dnssec")]
use rr::dnssec::rdata::DNSSECRecordType;

// TODO: adopt proper restrictions on usage: https://tools.ietf.org/html/rfc6895 section 3.1
//  add the data TYPEs, QTYPEs, and Meta-TYPEs
//

/// The type of the resource record.
///
/// This specifies the type of data in the RData field of the Resource Record
#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
#[allow(dead_code)]
pub enum RecordType {
    /// RFC 1035[1]	IPv4 Address record
    A,
    /// RFC 3596[2]	IPv6 address record
    AAAA,
    /// ANAME draft-ietf-dnsop-aname
    ANAME,
    //  AFSDB,      //	18	RFC 1183	AFS database record
    /// RFC 1035[1]	All cached records, aka ANY
    ANY,
    //  APL,        //	42	RFC 3123	Address Prefix List
    /// RFC 1035[1]	Authoritative Zone Transfer
    AXFR,
    /// RFC 6844 Certification Authority Authorization
    CAA,
    //  CERT,       //	37	RFC 4398	Certificate record
    /// RFC 1035[1]	Canonical name record
    CNAME,
    //  DHCID,      //	49	RFC 4701	DHCP identifier
    //  DNAME,      //	39	RFC 2672	Delegation Name
    //  HIP,        //	55	RFC 5205	Host Identity Protocol
    //  IPSECKEY,   //	45	RFC 4025	IPsec Key
    /// RFC 1996	Incremental Zone Transfer
    IXFR,
    //  KX,         //	36	RFC 2230	Key eXchanger record
    //  LOC,        //	29	RFC 1876	Location record
    /// RFC 1035[1]	Mail exchange record
    MX,
    //  NAPTR,      //	35	RFC 3403	Naming Authority Pointer
    /// RFC 1035[1]	Name server record
    NS,
    /// RFC 1035[1]	Null server record, for testing
    NULL,
    /// RFC 7929	OpenPGP public key
    OPENPGPKEY,
    /// RFC 6891	Option
    OPT,
    /// RFC 1035[1]	Pointer record
    PTR,
    //  RP,         //	17	RFC 1183	Responsible person
    /// RFC 1035[1] and RFC 2308[9]	Start of [a zone of] authority record
    SOA,
    /// RFC 2782	Service locator
    SRV,
    /// RFC 4255	SSH Public Key Fingerprint
    SSHFP,
    //  TA,         //	32768	N/A	DNSSEC Trust Authorities
    //  TKEY,       //	249	RFC 2930	Secret key record
    ///	RFC 6698	TLSA certificate association
    TLSA,
    //  TSIG,       //	250	RFC 2845	Transaction Signature
    /// RFC 1035[1]	Text record
    TXT,

    /// A DNSSEC- or SIG(0)- specific record type.
    ///
    /// These types are in `DNSSECRecordType` to make them easy to disable when
    /// crypto functionality isn't needed.
    #[cfg(feature = "dnssec")]
    DNSSEC(DNSSECRecordType),

    /// Unknown Record type, or unsupported
    Unknown(u16),

    /// This corresponds to a record type of 0, unspecified
    ZERO,
}

impl RecordType {
    /// Returns true if this is an ANY
    #[inline]
    pub fn is_any(self) -> bool {
        self == RecordType::ANY
    }

    /// Returns true if this is a CNAME
    #[inline]
    pub fn is_cname(self) -> bool {
        self == RecordType::CNAME
    }

    /// Returns true if this is an SRV
    #[inline]
    pub fn is_srv(self) -> bool {
        self == RecordType::SRV
    }

    /// Returns true if this is an A or an AAAA record
    #[inline]
    pub fn is_ip_addr(self) -> bool {
        match self {
            RecordType::A | RecordType::AAAA => true,
            _ => false,
        }
    }
}

impl FromStr for RecordType {
    type Err = ProtoError;

    /// Convert `&str` to `RecordType`
    ///
    /// ```
    /// use std::str::FromStr;
    /// use trust_dns_proto::rr::record_type::RecordType;
    ///
    /// let var: RecordType = RecordType::from_str("A").unwrap();
    /// assert_eq!(RecordType::A, var);
    /// ```
    fn from_str(str: &str) -> ProtoResult<Self> {
        // TODO missing stuff?
        match str {
            "A" => Ok(RecordType::A),
            "AAAA" => Ok(RecordType::AAAA),
            "ANAME" => Ok(RecordType::ANAME),
            "CAA" => Ok(RecordType::CAA),
            "CNAME" => Ok(RecordType::CNAME),
            "NULL" => Ok(RecordType::NULL),
            "MX" => Ok(RecordType::MX),
            "NS" => Ok(RecordType::NS),
            "OPENPGPKEY" => Ok(RecordType::OPENPGPKEY),
            "PTR" => Ok(RecordType::PTR),
            "SOA" => Ok(RecordType::SOA),
            "SRV" => Ok(RecordType::SRV),
            "SSHFP" => Ok(RecordType::SSHFP),
            "TLSA" => Ok(RecordType::TLSA),
            "TXT" => Ok(RecordType::TXT),
            "ANY" | "*" => Ok(RecordType::ANY),
            "AXFR" => Ok(RecordType::AXFR),
            _ => Err(ProtoErrorKind::UnknownRecordTypeStr(str.to_string()).into()),
        }
    }
}

impl From<u16> for RecordType {
    /// Convert from `u16` to `RecordType`
    ///
    /// ```
    /// use trust_dns_proto::rr::record_type::RecordType;
    ///
    /// let var = RecordType::from(1);
    /// assert_eq!(RecordType::A, var);
    /// ```
    fn from(value: u16) -> Self {
        match value {
            1 => RecordType::A,
            28 => RecordType::AAAA,
            // FIXME: wrong value here
            2005 => RecordType::ANAME,
            255 => RecordType::ANY,
            252 => RecordType::AXFR,
            257 => RecordType::CAA,
            5 => RecordType::CNAME,
            0 => RecordType::ZERO,
            15 => RecordType::MX,
            2 => RecordType::NS,
            10 => RecordType::NULL,
            61 => RecordType::OPENPGPKEY,
            41 => RecordType::OPT,
            12 => RecordType::PTR,
            6 => RecordType::SOA,
            33 => RecordType::SRV,
            44 => RecordType::SSHFP,
            52 => RecordType::TLSA,
            16 => RecordType::TXT,
            #[cfg(feature = "dnssec")]
            48/*DNSKEY*/ |
            43/*DS*/ |
            25/*KEY*/ |
            47/*NSEC*/|
            50/*NSEC3*/|
            51/*NSEC3PARAM*/|
            46/*RRSIG*/|
            24/*SIG*/ => RecordType::DNSSEC(DNSSECRecordType::from(value)),
            // all unknown record types
            _ => RecordType::Unknown(value),
        }
    }
}

impl BinEncodable for RecordType {
    fn emit(&self, encoder: &mut BinEncoder) -> ProtoResult<()> {
        encoder.emit_u16((*self).into())
    }
}

impl<'r> BinDecodable<'r> for RecordType {
    fn read(decoder: &mut BinDecoder) -> ProtoResult<Self> {
        decoder
            .read_u16()
            .map(|u| u.unverified(/*RecordType is safe with any u16*/))
            .map(Self::from)
    }
}

// TODO make these a macro...

/// Convert from RecordType to &str
///
/// ```
/// use std::convert::From;
/// use trust_dns_proto::rr::record_type::RecordType;
///
/// let var: &'static str = From::from(RecordType::A);
/// assert_eq!("A", var);
///
/// let var: &'static str = RecordType::A.into();
/// assert_eq!("A", var);
/// ```
impl From<RecordType> for &'static str {
    fn from(rt: RecordType) -> &'static str {
        match rt {
            RecordType::A => "A",
            RecordType::AAAA => "AAAA",
            RecordType::ANAME => "ANAME",
            RecordType::ANY => "ANY",
            RecordType::AXFR => "AXFR",
            RecordType::CAA => "CAA",
            RecordType::CNAME => "CNAME",
            RecordType::ZERO => "",
            RecordType::IXFR => "IXFR",
            RecordType::MX => "MX",
            RecordType::NULL => "NULL",
            RecordType::NS => "NS",
            RecordType::OPENPGPKEY => "OPENPGPKEY",
            RecordType::OPT => "OPT",
            RecordType::PTR => "PTR",
            RecordType::SOA => "SOA",
            RecordType::SRV => "SRV",
            RecordType::SSHFP => "SSHFP",
            RecordType::TLSA => "TLSA",
            RecordType::TXT => "TXT",
            #[cfg(feature = "dnssec")]
            RecordType::DNSSEC(rt) => rt.into(),
            RecordType::Unknown(_) => "Unknown",
        }
    }
}

/// Convert from RecordType to &str
///
/// ```
/// use std::convert::From;
/// use trust_dns_proto::rr::record_type::RecordType;
///
/// let var: u16 = RecordType::A.into();
/// assert_eq!(1, var);
/// ```
impl From<RecordType> for u16 {
    fn from(rt: RecordType) -> Self {
        match rt {
            RecordType::A => 1,
            RecordType::AAAA => 28,
            // FIXME: this value is not final
            RecordType::ANAME => 2005,
            RecordType::ANY => 255,
            RecordType::AXFR => 252,
            RecordType::CAA => 257,
            RecordType::CNAME => 5,
            RecordType::ZERO => 0,
            RecordType::IXFR => 251,
            RecordType::MX => 15,
            RecordType::NS => 2,
            RecordType::NULL => 10,
            RecordType::OPENPGPKEY => 61,
            RecordType::OPT => 41,
            RecordType::PTR => 12,
            RecordType::SOA => 6,
            RecordType::SRV => 33,
            RecordType::SSHFP => 44,
            RecordType::TLSA => 52,
            RecordType::TXT => 16,
            #[cfg(feature = "dnssec")]
            RecordType::DNSSEC(rt) => rt.into(),
            RecordType::Unknown(code) => code,
        }
    }
}

/// [Canonical DNS Name Order](https://tools.ietf.org/html/rfc4034#section-6)
impl PartialOrd<RecordType> for RecordType {
    fn partial_cmp(&self, other: &RecordType) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

/// [Canonical DNS Name Order](https://tools.ietf.org/html/rfc4034#section-6)
impl Ord for RecordType {
    fn cmp(&self, other: &Self) -> Ordering {
        u16::from(*self).cmp(&u16::from(*other))
    }
}

impl Display for RecordType {
    fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
        f.write_str(Into::<&str>::into(*self))
    }
}

#[test]
fn test_order() {
    let ordered = vec![
        RecordType::A,
        RecordType::NS,
        RecordType::CNAME,
        RecordType::SOA,
        RecordType::NULL,
        RecordType::PTR,
        RecordType::MX,
        RecordType::TXT,
        RecordType::AAAA,
        RecordType::SRV,
        RecordType::AXFR,
        RecordType::ANY,
    ];

    let mut unordered = vec![
        RecordType::ANY,
        RecordType::NULL,
        RecordType::AXFR,
        RecordType::A,
        RecordType::NS,
        RecordType::SOA,
        RecordType::SRV,
        RecordType::PTR,
        RecordType::MX,
        RecordType::CNAME,
        RecordType::TXT,
        RecordType::AAAA,
    ];

    unordered.sort();

    for rtype in unordered.clone() {
        println!("u16 for {:?}: {}", rtype, u16::from(rtype));
    }

    assert_eq!(ordered, unordered);
}
<commit_msg>pick a proper temp value for ANAME<commit_after>/*
 * Copyright (C) 2015-2019 Benjamin Fry <benjaminfry@me.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

//! record type definitions

use std::cmp::Ordering;
use std::convert::From;
use std::fmt;
use std::fmt::{Display, Formatter};
use std::str::FromStr;

use error::*;
use serialize::binary::*;

#[cfg(feature = "dnssec")]
use rr::dnssec::rdata::DNSSECRecordType;

// TODO: adopt proper restrictions on usage: https://tools.ietf.org/html/rfc6895 section 3.1
//  add the data TYPEs, QTYPEs, and Meta-TYPEs
//

/// The type of the resource record.
///
/// This specifies the type of data in the RData field of the Resource Record
#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
#[allow(dead_code)]
pub enum RecordType {
    /// RFC 1035[1]	IPv4 Address record
    A,
    /// RFC 3596[2]	IPv6 address record
    AAAA,
    /// ANAME draft-ietf-dnsop-aname
    ANAME,
    //  AFSDB,      //	18	RFC 1183	AFS database record
    /// RFC 1035[1]	All cached records, aka ANY
    ANY,
    //  APL,        //	42	RFC 3123	Address Prefix List
    /// RFC 1035[1]	Authoritative Zone Transfer
    AXFR,
    /// RFC 6844 Certification Authority Authorization
    CAA,
    //  CERT,       //	37	RFC 4398	Certificate record
    /// RFC 1035[1]	Canonical name record
    CNAME,
    //  DHCID,      //	49	RFC 4701	DHCP identifier
    //  DNAME,      //	39	RFC 2672	Delegation Name
    //  HIP,        //	55	RFC 5205	Host Identity Protocol
    //  IPSECKEY,   //	45	RFC 4025	IPsec Key
    /// RFC 1996	Incremental Zone Transfer
    IXFR,
    //  KX,         //	36	RFC 2230	Key eXchanger record
    //  LOC,        //	29	RFC 1876	Location record
    /// RFC 1035[1]	Mail exchange record
    MX,
    //  NAPTR,      //	35	RFC 3403	Naming Authority Pointer
    /// RFC 1035[1]	Name server record
    NS,
    /// RFC 1035[1]	Null server record, for testing
    NULL,
    /// RFC 7929	OpenPGP public key
    OPENPGPKEY,
    /// RFC 6891	Option
    OPT,
    /// RFC 1035[1]	Pointer record
    PTR,
    //  RP,         //	17	RFC 1183	Responsible person
    /// RFC 1035[1] and RFC 2308[9]	Start of [a zone of] authority record
    SOA,
    /// RFC 2782	Service locator
    SRV,
    /// RFC 4255	SSH Public Key Fingerprint
    SSHFP,
    //  TA,         //	32768	N/A	DNSSEC Trust Authorities
    //  TKEY,       //	249	RFC 2930	Secret key record
    ///	RFC 6698	TLSA certificate association
    TLSA,
    //  TSIG,       //	250	RFC 2845	Transaction Signature
    /// RFC 1035[1]	Text record
    TXT,

    /// A DNSSEC- or SIG(0)- specific record type.
    ///
    /// These types are in `DNSSECRecordType` to make them easy to disable when
    /// crypto functionality isn't needed.
    #[cfg(feature = "dnssec")]
    DNSSEC(DNSSECRecordType),

    /// Unknown Record type, or unsupported
    Unknown(u16),

    /// This corresponds to a record type of 0, unspecified
    ZERO,
}

impl RecordType {
    /// Returns true if this is an ANY
    #[inline]
    pub fn is_any(self) -> bool {
        self == RecordType::ANY
    }

    /// Returns true if this is a CNAME
    #[inline]
    pub fn is_cname(self) -> bool {
        self == RecordType::CNAME
    }

    /// Returns true if this is an SRV
    #[inline]
    pub fn is_srv(self) -> bool {
        self == RecordType::SRV
    }

    /// Returns true if this is an A or an AAAA record
    #[inline]
    pub fn is_ip_addr(self) -> bool {
        match self {
            RecordType::A | RecordType::AAAA => true,
            _ => false,
        }
    }
}

impl FromStr for RecordType {
    type Err = ProtoError;

    /// Convert `&str` to `RecordType`
    ///
    /// ```
    /// use std::str::FromStr;
    /// use trust_dns_proto::rr::record_type::RecordType;
    ///
    /// let var: RecordType = RecordType::from_str("A").unwrap();
    /// assert_eq!(RecordType::A, var);
    /// ```
    fn from_str(str: &str) -> ProtoResult<Self> {
        // TODO missing stuff?
        match str {
            "A" => Ok(RecordType::A),
            "AAAA" => Ok(RecordType::AAAA),
            "ANAME" => Ok(RecordType::ANAME),
            "CAA" => Ok(RecordType::CAA),
            "CNAME" => Ok(RecordType::CNAME),
            "NULL" => Ok(RecordType::NULL),
            "MX" => Ok(RecordType::MX),
            "NS" => Ok(RecordType::NS),
            "OPENPGPKEY" => Ok(RecordType::OPENPGPKEY),
            "PTR" => Ok(RecordType::PTR),
            "SOA" => Ok(RecordType::SOA),
            "SRV" => Ok(RecordType::SRV),
            "SSHFP" => Ok(RecordType::SSHFP),
            "TLSA" => Ok(RecordType::TLSA),
            "TXT" => Ok(RecordType::TXT),
            "ANY" | "*" => Ok(RecordType::ANY),
            "AXFR" => Ok(RecordType::AXFR),
            _ => Err(ProtoErrorKind::UnknownRecordTypeStr(str.to_string()).into()),
        }
    }
}

impl From<u16> for RecordType {
    /// Convert from `u16` to `RecordType`
    ///
    /// ```
    /// use trust_dns_proto::rr::record_type::RecordType;
    ///
    /// let var = RecordType::from(1);
    /// assert_eq!(RecordType::A, var);
    /// ```
    fn from(value: u16) -> Self {
        match value {
            1 => RecordType::A,
            28 => RecordType::AAAA,
            // TODO: wrong value here, see https://github.com/bluejekyll/trust-dns/issues/723
            65305 => RecordType::ANAME,
            255 => RecordType::ANY,
            252 => RecordType::AXFR,
            257 => RecordType::CAA,
            5 => RecordType::CNAME,
            0 => RecordType::ZERO,
            15 => RecordType::MX,
            2 => RecordType::NS,
            10 => RecordType::NULL,
            61 => RecordType::OPENPGPKEY,
            41 => RecordType::OPT,
            12 => RecordType::PTR,
            6 => RecordType::SOA,
            33 => RecordType::SRV,
            44 => RecordType::SSHFP,
            52 => RecordType::TLSA,
            16 => RecordType::TXT,
            #[cfg(feature = "dnssec")]
            48/*DNSKEY*/ |
            43/*DS*/ |
            25/*KEY*/ |
            47/*NSEC*/|
            50/*NSEC3*/|
            51/*NSEC3PARAM*/|
            46/*RRSIG*/|
            24/*SIG*/ => RecordType::DNSSEC(DNSSECRecordType::from(value)),
            // all unknown record types
            _ => RecordType::Unknown(value),
        }
    }
}

impl BinEncodable for RecordType {
    fn emit(&self, encoder: &mut BinEncoder) -> ProtoResult<()> {
        encoder.emit_u16((*self).into())
    }
}

impl<'r> BinDecodable<'r> for RecordType {
    fn read(decoder: &mut BinDecoder) -> ProtoResult<Self> {
        decoder
            .read_u16()
            .map(|u| u.unverified(/*RecordType is safe with any u16*/))
            .map(Self::from)
    }
}

// TODO make these a macro...

/// Convert from RecordType to &str
///
/// ```
/// use std::convert::From;
/// use trust_dns_proto::rr::record_type::RecordType;
///
/// let var: &'static str = From::from(RecordType::A);
/// assert_eq!("A", var);
///
/// let var: &'static str = RecordType::A.into();
/// assert_eq!("A", var);
/// ```
impl From<RecordType> for &'static str {
    fn from(rt: RecordType) -> &'static str {
        match rt {
            RecordType::A => "A",
            RecordType::AAAA => "AAAA",
            RecordType::ANAME => "ANAME",
            RecordType::ANY => "ANY",
            RecordType::AXFR => "AXFR",
            RecordType::CAA => "CAA",
            RecordType::CNAME => "CNAME",
            RecordType::ZERO => "",
            RecordType::IXFR => "IXFR",
            RecordType::MX => "MX",
            RecordType::NULL => "NULL",
            RecordType::NS => "NS",
            RecordType::OPENPGPKEY => "OPENPGPKEY",
            RecordType::OPT => "OPT",
            RecordType::PTR => "PTR",
            RecordType::SOA => "SOA",
            RecordType::SRV => "SRV",
            RecordType::SSHFP => "SSHFP",
            RecordType::TLSA => "TLSA",
            RecordType::TXT => "TXT",
            #[cfg(feature = "dnssec")]
            RecordType::DNSSEC(rt) => rt.into(),
            RecordType::Unknown(_) => "Unknown",
        }
    }
}

/// Convert from RecordType to &str
///
/// ```
/// use std::convert::From;
/// use trust_dns_proto::rr::record_type::RecordType;
///
/// let var: u16 = RecordType::A.into();
/// assert_eq!(1, var);
/// ```
impl From<RecordType> for u16 {
    fn from(rt: RecordType) -> Self {
        match rt {
            RecordType::A => 1,
            RecordType::AAAA => 28,
            // TODO: wrong value here, see https://github.com/bluejekyll/trust-dns/issues/723
            RecordType::ANAME => 65305,
            RecordType::ANY => 255,
            RecordType::AXFR => 252,
            RecordType::CAA => 257,
            RecordType::CNAME => 5,
            RecordType::ZERO => 0,
            RecordType::IXFR => 251,
            RecordType::MX => 15,
            RecordType::NS => 2,
            RecordType::NULL => 10,
            RecordType::OPENPGPKEY => 61,
            RecordType::OPT => 41,
            RecordType::PTR => 12,
            RecordType::SOA => 6,
            RecordType::SRV => 33,
            RecordType::SSHFP => 44,
            RecordType::TLSA => 52,
            RecordType::TXT => 16,
            #[cfg(feature = "dnssec")]
            RecordType::DNSSEC(rt) => rt.into(),
            RecordType::Unknown(code) => code,
        }
    }
}

/// [Canonical DNS Name Order](https://tools.ietf.org/html/rfc4034#section-6)
impl PartialOrd<RecordType> for RecordType {
    fn partial_cmp(&self, other: &RecordType) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

/// [Canonical DNS Name Order](https://tools.ietf.org/html/rfc4034#section-6)
impl Ord for RecordType {
    fn cmp(&self, other: &Self) -> Ordering {
        u16::from(*self).cmp(&u16::from(*other))
    }
}

impl Display for RecordType {
    fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
        f.write_str(Into::<&str>::into(*self))
    }
}

#[test]
fn test_order() {
    let ordered = vec![
        RecordType::A,
        RecordType::NS,
        RecordType::CNAME,
        RecordType::SOA,
        RecordType::NULL,
        RecordType::PTR,
        RecordType::MX,
        RecordType::TXT,
        RecordType::AAAA,
        RecordType::SRV,
        RecordType::AXFR,
        RecordType::ANY,
    ];

    let mut unordered = vec![
        RecordType::ANY,
        RecordType::NULL,
        RecordType::AXFR,
        RecordType::A,
        RecordType::NS,
        RecordType::SOA,
        RecordType::SRV,
        RecordType::PTR,
        RecordType::MX,
        RecordType::CNAME,
        RecordType::TXT,
        RecordType::AAAA,
    ];

    unordered.sort();

    for rtype in unordered.clone() {
        println!("u16 for {:?}: {}", rtype, u16::from(rtype));
    }

    assert_eq!(ordered, unordered);
}
<|endoftext|>


<commit_before>extern crate wm_daemons;
use wm_daemons::config::load_config;
use wm_daemons::dbus_listen::{CallbackMap, SignalInfo, match_signal, make_signal_info};

extern crate config;
use self::config::types::Config;

extern crate dbus;
use self::dbus::{Connection, BusType};

use std::error::Error;

fn run_program(action: &str, conf: &Config) -> () {
    // TODO
}

fn handle_signal(info: &SignalInfo, conf: &Config) -> () {
    if info.member == Some("Lock".to_string()) {
        run_program("lock", conf);
    } else if info.member == Some("Unlock".to_string()) {
        run_program("unlock", conf);
    }
}

fn try_main() -> Result<(), Box<Error>> {
    let conf = try!(load_config("wm-session-agent", "config"));
    let conn = try!(Connection::get_private(BusType::System));

    let cbs: CallbackMap<Config> = vec![
        (make_signal_info(
            "/org/freedesktop/login1/session/_33",
            "org.freedesktop.login1.Session",
            "Lock",
        ), handle_signal),
        (make_signal_info(
            "/org/freedesktop/login1/session/_33",
            "org.freedesktop.login1.Session",
            "Unlock",
        ), handle_signal),
    ];

    try!(conn.add_match("type='signal'"));

    for items in conn.iter(100) {
        match_signal(items, &cbs, &conf);
    }

    Ok(())
}

fn main() -> () {
    if let Err(err) = try_main() {
        panic!("{}", err.description());
    }
}
<commit_msg>wm-session-agent: use dbus' match rules<commit_after>extern crate wm_daemons;
use wm_daemons::config::load_config;
use wm_daemons::dbus_listen::{CallbackMap, SignalInfo, match_signal};

extern crate config;
use self::config::types::Config;

extern crate dbus;
use self::dbus::{Connection, BusType};

use std::error::Error;

fn run_program(action: &str, conf: &Config) -> () {
    // TODO
}

fn handle_signal(info: &SignalInfo, conf: &Config) -> () {
    if info.member == Some("Lock".to_string()) {
        run_program("lock", conf);
    } else if info.member == Some("Unlock".to_string()) {
        run_program("unlock", conf);
    }
}

fn try_main() -> Result<(), Box<Error>> {
    let conf = try!(load_config("wm-session-agent", "config"));
    let conn = try!(Connection::get_private(BusType::System));

    let cbs: CallbackMap<Config> = vec![
        (SignalInfo {
            path: None,
            object: None,
            member: Some("Lock".to_string()),
        }, handle_signal),
        (SignalInfo {
            path: None,
            object: None,
            member: Some("Unlock".to_string()),
        }, handle_signal),
    ];

    let match_str = format!("type='signal',interface='org.freedesktop.login1.Session',path='{}'", spath);
    try!(conn.add_match(&match_str[..]));

    for items in conn.iter(100) {
        match_signal(items, &cbs, &conf);
    }

    Ok(())
}

fn main() -> () {
    if let Err(err) = try_main() {
        panic!("{}", err.description());
    }
}
<|endoftext|>


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

//! Liberally derived from the [Firefox JS implementation]
//! (http://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/server/actors/inspector.js).

use devtools_traits::{DevtoolScriptControlMsg, NodeInfo};
use devtools_traits::DevtoolScriptControlMsg::{GetRootNode, GetDocumentElement, GetChildren};
use devtools_traits::DevtoolScriptControlMsg::{GetLayout, ModifyAttribute};

use actor::{Actor, ActorRegistry};
use protocol::JsonPacketStream;

use std::collections::BTreeMap;
use msg::constellation_msg::PipelineId;
use rustc_serialize::json::{self, Json, ToJson};
use std::cell::RefCell;
use std::net::TcpStream;
use std::sync::mpsc::{channel, Sender};

pub struct InspectorActor {
    pub name: String,
    pub walker: RefCell<Option<String>>,
    pub pageStyle: RefCell<Option<String>>,
    pub highlighter: RefCell<Option<String>>,
    pub script_chan: Sender<DevtoolScriptControlMsg>,
    pub pipeline: PipelineId,
}

#[derive(RustcEncodable)]
struct GetHighlighterReply {
    highligter: HighlighterMsg, // sic.
    from: String,
}

#[derive(RustcEncodable)]
struct HighlighterMsg {
    actor: String,
}

struct HighlighterActor {
    name: String,
}

pub struct NodeActor {
    pub name: String,
    script_chan: Sender<DevtoolScriptControlMsg>,
    pipeline: PipelineId,
}

#[derive(RustcEncodable)]
struct ShowBoxModelReply {
    from: String,
}

#[derive(RustcEncodable)]
struct HideBoxModelReply {
    from: String,
}

impl Actor for HighlighterActor {
    fn name(&self) -> String {
        self.name.clone()
    }

    fn handle_message(&self,
                      _registry: &ActorRegistry,
                      msg_type: &str,
                      _msg: &json::Object,
                      stream: &mut TcpStream) -> Result<bool, ()> {
        Ok(match msg_type {
            "showBoxModel" => {
                let msg = ShowBoxModelReply {
                    from: self.name(),
                };
                stream.write_json_packet(&msg);
                true
            }

            "hideBoxModel" => {
                let msg = HideBoxModelReply {
                    from: self.name(),
                };
                stream.write_json_packet(&msg);
                true
            }

            _ => false,
        })
    }
}

#[derive(RustcEncodable)]
struct ModifyAttributeReply{
    from: String,
}

impl Actor for NodeActor {
    fn name(&self) -> String {
        self.name.clone()
    }

    fn handle_message(&self,
                      registry: &ActorRegistry,
                      msg_type: &str,
                      msg: &json::Object,
                      stream: &mut TcpStream) -> Result<bool, ()> {
        Ok(match msg_type {
            "modifyAttributes" => {
                let target = msg.get(&"to".to_string()).unwrap().as_string().unwrap();
                let mods = msg.get(&"modifications".to_string()).unwrap().as_array().unwrap();
                let modifications = mods.iter().map(|json_mod| {
                    json::decode(&json_mod.to_string()).unwrap()
                }).collect();

                self.script_chan.send(ModifyAttribute(self.pipeline,
                                                      registry.actor_to_script(target.to_string()),
                                                      modifications))
                                .unwrap();
                let reply = ModifyAttributeReply{
                    from: self.name(),
                };
                stream.write_json_packet(&reply);
                true
            }

            _ => false,
        })
    }
}

#[derive(RustcEncodable)]
struct GetWalkerReply {
    from: String,
    walker: WalkerMsg,
}

#[derive(RustcEncodable)]
struct WalkerMsg {
    actor: String,
    root: NodeActorMsg,
}

#[derive(RustcEncodable)]
struct AttrMsg {
    namespace: String,
    name: String,
    value: String,
}

#[derive(RustcEncodable)]
struct NodeActorMsg {
    actor: String,
    baseURI: String,
    parent: String,
    nodeType: u16,
    namespaceURI: String,
    nodeName: String,
    numChildren: usize,

    name: String,
    publicId: String,
    systemId: String,

    attrs: Vec<AttrMsg>,

    pseudoClassLocks: Vec<String>,

    isDisplayed: bool,

    hasEventListeners: bool,

    isDocumentElement: bool,

    shortValue: String,
    incompleteValue: bool,
}

trait NodeInfoToProtocol {
    fn encode(self,
              actors: &ActorRegistry,
              display: bool,
              script_chan: Sender<DevtoolScriptControlMsg>,
              pipeline: PipelineId) -> NodeActorMsg;
}

impl NodeInfoToProtocol for NodeInfo {
    fn encode(self,
              actors: &ActorRegistry,
              display: bool,
              script_chan: Sender<DevtoolScriptControlMsg>,
              pipeline: PipelineId) -> NodeActorMsg {
        let actor_name = if !actors.script_actor_registered(self.uniqueId.clone()) {
            let name = actors.new_name("node");
            let node_actor = NodeActor {
                name: name.clone(),
                script_chan: script_chan,
                pipeline: pipeline.clone(),
            };
            actors.register_script_actor(self.uniqueId, name.clone());
            actors.register_later(box node_actor);
            name
        } else {
            actors.script_to_actor(self.uniqueId)
        };

        NodeActorMsg {
            actor: actor_name,
            baseURI: self.baseURI,
            parent: actors.script_to_actor(self.parent.clone()),
            nodeType: self.nodeType,
            namespaceURI: self.namespaceURI,
            nodeName: self.nodeName,
            numChildren: self.numChildren,

            name: self.name,
            publicId: self.publicId,
            systemId: self.systemId,

            attrs: self.attrs.into_iter().map(|attr| {
                AttrMsg {
                    namespace: attr.namespace,
                    name: attr.name,
                    value: attr.value,
                }
            }).collect(),

            pseudoClassLocks: vec!(), //TODO get this data from script

            isDisplayed: display,

            hasEventListeners: false, //TODO get this data from script

            isDocumentElement: self.isDocumentElement,

            shortValue: self.shortValue,
            incompleteValue: self.incompleteValue,
        }
    }
}

struct WalkerActor {
    name: String,
    script_chan: Sender<DevtoolScriptControlMsg>,
    pipeline: PipelineId,
}

#[derive(RustcEncodable)]
struct QuerySelectorReply {
    from: String,
}

#[derive(RustcEncodable)]
struct DocumentElementReply {
    from: String,
    node: NodeActorMsg,
}

#[derive(RustcEncodable)]
struct ClearPseudoclassesReply {
    from: String,
}

#[derive(RustcEncodable)]
struct ChildrenReply {
    hasFirst: bool,
    hasLast: bool,
    nodes: Vec<NodeActorMsg>,
    from: String,
}

impl Actor for WalkerActor {
    fn name(&self) -> String {
        self.name.clone()
    }

    fn handle_message(&self,
                      registry: &ActorRegistry,
                      msg_type: &str,
                      msg: &json::Object,
                      stream: &mut TcpStream) -> Result<bool, ()> {
        Ok(match msg_type {
            "querySelector" => {
                let msg = QuerySelectorReply {
                    from: self.name(),
                };
                stream.write_json_packet(&msg);
                true
            }

            "documentElement" => {
                let (tx, rx) = channel();
                self.script_chan.send(GetDocumentElement(self.pipeline, tx)).unwrap();
                let doc_elem_info = rx.recv().unwrap();
                let node = doc_elem_info.encode(registry, true, self.script_chan.clone(), self.pipeline);

                let msg = DocumentElementReply {
                    from: self.name(),
                    node: node,
                };
                stream.write_json_packet(&msg);
                true
            }

            "clearPseudoClassLocks" => {
                let msg = ClearPseudoclassesReply {
                    from: self.name(),
                };
                stream.write_json_packet(&msg);
                true
            }

            "children" => {
                let target = msg.get(&"node".to_string()).unwrap().as_string().unwrap();
                let (tx, rx) = channel();
                self.script_chan.send(GetChildren(self.pipeline,
                                                  registry.actor_to_script(target.to_string()),
                                                  tx))
                                .unwrap();
                let children = rx.recv().unwrap();

                let msg = ChildrenReply {
                    hasFirst: true,
                    hasLast: true,
                    nodes: children.into_iter().map(|child| {
                        child.encode(registry, true, self.script_chan.clone(), self.pipeline)
                    }).collect(),
                    from: self.name(),
                };
                stream.write_json_packet(&msg);
                true
            }

            _ => false,
        })
    }
}

#[derive(RustcEncodable)]
struct GetPageStyleReply {
    from: String,
    pageStyle: PageStyleMsg,
}

#[derive(RustcEncodable)]
struct PageStyleMsg {
    actor: String,
}

struct PageStyleActor {
    name: String,
    script_chan: Sender<DevtoolScriptControlMsg>,
    pipeline: PipelineId,
}

#[derive(RustcEncodable)]
struct GetAppliedReply {
    entries: Vec<AppliedEntry>,
    rules: Vec<AppliedRule>,
    sheets: Vec<AppliedSheet>,
    from: String,
}

#[derive(RustcEncodable)]
struct GetComputedReply {
    computed: Vec<u32>, //XXX all css props
    from: String,
}

#[derive(RustcEncodable)]
struct AppliedEntry {
    rule: String,
    pseudoElement: Json,
    isSystem: bool,
    matchedSelectors: Vec<String>,
}

#[derive(RustcEncodable)]
struct AppliedRule {
    actor: String,
    __type__: u32,
    href: String,
    cssText: String,
    line: u32,
    column: u32,
    parentStyleSheet: String,
}

#[derive(RustcEncodable)]
struct AppliedSheet {
    actor: String,
    href: String,
    nodeHref: String,
    disabled: bool,
    title: String,
    system: bool,
    styleSheetIndex: isize,
    ruleCount: usize,
}

#[derive(RustcEncodable)]
struct GetLayoutReply {
    width: i32,
    height: i32,
    autoMargins: Json,
    from: String,
}

#[derive(RustcEncodable)]
#[allow(dead_code)]
struct AutoMargins {
    top: String,
    bottom: String,
    left: String,
    right: String,
}

impl Actor for PageStyleActor {
    fn name(&self) -> String {
        self.name.clone()
    }

    fn handle_message(&self,
                      registry: &ActorRegistry,
                      msg_type: &str,
                      msg: &json::Object,
                      stream: &mut TcpStream) -> Result<bool, ()> {
        Ok(match msg_type {
            "getApplied" => {
                //TODO: query script for relevant applied styles to node (msg.node)
                let msg = GetAppliedReply {
                    entries: vec!(),
                    rules: vec!(),
                    sheets: vec!(),
                    from: self.name(),
                };
                stream.write_json_packet(&msg);
                true
            }

            "getComputed" => {
                //TODO: query script for relevant computed styles on node (msg.node)
                let msg = GetComputedReply {
                    computed: vec!(),
                    from: self.name(),
                };
                stream.write_json_packet(&msg);
                true
            }

            //TODO: query script for box layout properties of node (msg.node)
            "getLayout" => {
                let target = msg.get(&"node".to_string()).unwrap().as_string().unwrap();
                let (tx, rx) = channel();
                self.script_chan.send(GetLayout(self.pipeline,
                                      registry.actor_to_script(target.to_string()),
                                      tx))
                                .unwrap();
                let (width, height) = rx.recv().unwrap();

                let auto_margins = msg.get(&"autoMargins".to_string()).unwrap().as_boolean().unwrap();

                //TODO: the remaining layout properties (margin, border, padding, position)
                //      as specified in getLayout in
                //      http://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/server/actors/styles.js
                let msg = GetLayoutReply {
                    width: width.round() as i32,
                    height: height.round() as i32,
                    autoMargins: if auto_margins {
                        //TODO: real values like processMargins in
                        //  http://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/server/actors/styles.js
                        let mut m = BTreeMap::new();
                        m.insert("top".to_string(), "auto".to_string().to_json());
                        m.insert("bottom".to_string(), "auto".to_string().to_json());
                        m.insert("left".to_string(), "auto".to_string().to_json());
                        m.insert("right".to_string(), "auto".to_string().to_json());
                        Json::Object(m)
                    } else {
                        Json::Null
                    },
                    from: self.name(),
                };
                stream.write_json_packet(&msg);
                true
            }

            _ => false,
        })
    }
}

impl Actor for InspectorActor {
    fn name(&self) -> String {
        self.name.clone()
    }

    fn handle_message(&self,
                      registry: &ActorRegistry,
                      msg_type: &str,
                      _msg: &json::Object,
                      stream: &mut TcpStream) -> Result<bool, ()> {
        Ok(match msg_type {
            "getWalker" => {
                if self.walker.borrow().is_none() {
                    let walker = WalkerActor {
                        name: registry.new_name("walker"),
                        script_chan: self.script_chan.clone(),
                        pipeline: self.pipeline,
                    };
                    let mut walker_name = self.walker.borrow_mut();
                    *walker_name = Some(walker.name());
                    registry.register_later(box walker);
                }

                let (tx, rx) = channel();
                self.script_chan.send(GetRootNode(self.pipeline, tx)).unwrap();
                let root_info = rx.recv().unwrap();

                let node = root_info.encode(registry, false, self.script_chan.clone(), self.pipeline);

                let msg = GetWalkerReply {
                    from: self.name(),
                    walker: WalkerMsg {
                        actor: self.walker.borrow().clone().unwrap(),
                        root: node,
                    }
                };
                stream.write_json_packet(&msg);
                true
            }

            "getPageStyle" => {
                if self.pageStyle.borrow().is_none() {
                    let style = PageStyleActor {
                        name: registry.new_name("pageStyle"),
                        script_chan: self.script_chan.clone(),
                        pipeline: self.pipeline,
                    };
                    let mut pageStyle = self.pageStyle.borrow_mut();
                    *pageStyle = Some(style.name());
                    registry.register_later(box style);
                }

                let msg = GetPageStyleReply {
                    from: self.name(),
                    pageStyle: PageStyleMsg {
                        actor: self.pageStyle.borrow().clone().unwrap(),
                    },
                };
                stream.write_json_packet(&msg);
                true
            }

            //TODO: this is an old message; try adding highlightable to the root traits instead
            //      and support getHighlighter instead
            //"highlight" => {}
            "getHighlighter" => {
                if self.highlighter.borrow().is_none() {
                    let highlighter_actor = HighlighterActor {
                        name: registry.new_name("highlighter"),
                    };
                    let mut highlighter = self.highlighter.borrow_mut();
                    *highlighter = Some(highlighter_actor.name());
                    registry.register_later(box highlighter_actor);
                }

                let msg = GetHighlighterReply {
                    from: self.name(),
                    highligter: HighlighterMsg {
                        actor: self.highlighter.borrow().clone().unwrap(),
                    },
                };
                stream.write_json_packet(&msg);
                true
            }

            _ => false,
        })
    }
}
<commit_msg>Auto merge of #6637 - notriddle:master, r=jdm<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Liberally derived from the [Firefox JS implementation]
//! (http://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/server/actors/inspector.js).

use devtools_traits::{DevtoolScriptControlMsg, NodeInfo};
use devtools_traits::DevtoolScriptControlMsg::{GetRootNode, GetDocumentElement, GetChildren};
use devtools_traits::DevtoolScriptControlMsg::{GetLayout, ModifyAttribute};

use actor::{Actor, ActorRegistry};
use protocol::JsonPacketStream;

use std::collections::BTreeMap;
use msg::constellation_msg::PipelineId;
use rustc_serialize::json::{self, Json, ToJson};
use std::cell::RefCell;
use std::net::TcpStream;
use std::sync::mpsc::{channel, Sender};

pub struct InspectorActor {
    pub name: String,
    pub walker: RefCell<Option<String>>,
    pub pageStyle: RefCell<Option<String>>,
    pub highlighter: RefCell<Option<String>>,
    pub script_chan: Sender<DevtoolScriptControlMsg>,
    pub pipeline: PipelineId,
}

#[derive(RustcEncodable)]
struct GetHighlighterReply {
    highligter: HighlighterMsg, // sic.
    from: String,
}

#[derive(RustcEncodable)]
struct HighlighterMsg {
    actor: String,
}

struct HighlighterActor {
    name: String,
}

pub struct NodeActor {
    pub name: String,
    script_chan: Sender<DevtoolScriptControlMsg>,
    pipeline: PipelineId,
}

#[derive(RustcEncodable)]
struct ShowBoxModelReply {
    from: String,
}

#[derive(RustcEncodable)]
struct HideBoxModelReply {
    from: String,
}

impl Actor for HighlighterActor {
    fn name(&self) -> String {
        self.name.clone()
    }

    fn handle_message(&self,
                      _registry: &ActorRegistry,
                      msg_type: &str,
                      _msg: &json::Object,
                      stream: &mut TcpStream) -> Result<bool, ()> {
        Ok(match msg_type {
            "showBoxModel" => {
                let msg = ShowBoxModelReply {
                    from: self.name(),
                };
                stream.write_json_packet(&msg);
                true
            }

            "hideBoxModel" => {
                let msg = HideBoxModelReply {
                    from: self.name(),
                };
                stream.write_json_packet(&msg);
                true
            }

            _ => false,
        })
    }
}

#[derive(RustcEncodable)]
struct ModifyAttributeReply{
    from: String,
}

impl Actor for NodeActor {
    fn name(&self) -> String {
        self.name.clone()
    }

    fn handle_message(&self,
                      registry: &ActorRegistry,
                      msg_type: &str,
                      msg: &json::Object,
                      stream: &mut TcpStream) -> Result<bool, ()> {
        Ok(match msg_type {
            "modifyAttributes" => {
                let target = msg.get(&"to".to_string()).unwrap().as_string().unwrap();
                let mods = msg.get(&"modifications".to_string()).unwrap().as_array().unwrap();
                let modifications = mods.iter().map(|json_mod| {
                    json::decode(&json_mod.to_string()).unwrap()
                }).collect();

                self.script_chan.send(ModifyAttribute(self.pipeline,
                                                      registry.actor_to_script(target.to_string()),
                                                      modifications))
                                .unwrap();
                let reply = ModifyAttributeReply{
                    from: self.name(),
                };
                stream.write_json_packet(&reply);
                true
            }

            _ => false,
        })
    }
}

#[derive(RustcEncodable)]
struct GetWalkerReply {
    from: String,
    walker: WalkerMsg,
}

#[derive(RustcEncodable)]
struct WalkerMsg {
    actor: String,
    root: NodeActorMsg,
}

#[derive(RustcEncodable)]
struct AttrMsg {
    namespace: String,
    name: String,
    value: String,
}

#[derive(RustcEncodable)]
struct NodeActorMsg {
    actor: String,
    baseURI: String,
    parent: String,
    nodeType: u16,
    namespaceURI: String,
    nodeName: String,
    numChildren: usize,

    name: String,
    publicId: String,
    systemId: String,

    attrs: Vec<AttrMsg>,

    pseudoClassLocks: Vec<String>,

    isDisplayed: bool,

    hasEventListeners: bool,

    isDocumentElement: bool,

    shortValue: String,
    incompleteValue: bool,
}

trait NodeInfoToProtocol {
    fn encode(self,
              actors: &ActorRegistry,
              display: bool,
              script_chan: Sender<DevtoolScriptControlMsg>,
              pipeline: PipelineId) -> NodeActorMsg;
}

impl NodeInfoToProtocol for NodeInfo {
    fn encode(self,
              actors: &ActorRegistry,
              display: bool,
              script_chan: Sender<DevtoolScriptControlMsg>,
              pipeline: PipelineId) -> NodeActorMsg {
        let actor_name = if !actors.script_actor_registered(self.uniqueId.clone()) {
            let name = actors.new_name("node");
            let node_actor = NodeActor {
                name: name.clone(),
                script_chan: script_chan,
                pipeline: pipeline.clone(),
            };
            actors.register_script_actor(self.uniqueId, name.clone());
            actors.register_later(box node_actor);
            name
        } else {
            actors.script_to_actor(self.uniqueId)
        };

        NodeActorMsg {
            actor: actor_name,
            baseURI: self.baseURI,
            parent: actors.script_to_actor(self.parent.clone()),
            nodeType: self.nodeType,
            namespaceURI: self.namespaceURI,
            nodeName: self.nodeName,
            numChildren: self.numChildren,

            name: self.name,
            publicId: self.publicId,
            systemId: self.systemId,

            attrs: self.attrs.into_iter().map(|attr| {
                AttrMsg {
                    namespace: attr.namespace,
                    name: attr.name,
                    value: attr.value,
                }
            }).collect(),

            pseudoClassLocks: vec!(), //TODO get this data from script

            isDisplayed: display,

            hasEventListeners: false, //TODO get this data from script

            isDocumentElement: self.isDocumentElement,

            shortValue: self.shortValue,
            incompleteValue: self.incompleteValue,
        }
    }
}

struct WalkerActor {
    name: String,
    script_chan: Sender<DevtoolScriptControlMsg>,
    pipeline: PipelineId,
}

#[derive(RustcEncodable)]
struct QuerySelectorReply {
    from: String,
}

#[derive(RustcEncodable)]
struct DocumentElementReply {
    from: String,
    node: NodeActorMsg,
}

#[derive(RustcEncodable)]
struct ClearPseudoclassesReply {
    from: String,
}

#[derive(RustcEncodable)]
struct ChildrenReply {
    hasFirst: bool,
    hasLast: bool,
    nodes: Vec<NodeActorMsg>,
    from: String,
}

impl Actor for WalkerActor {
    fn name(&self) -> String {
        self.name.clone()
    }

    fn handle_message(&self,
                      registry: &ActorRegistry,
                      msg_type: &str,
                      msg: &json::Object,
                      stream: &mut TcpStream) -> Result<bool, ()> {
        Ok(match msg_type {
            "querySelector" => {
                let msg = QuerySelectorReply {
                    from: self.name(),
                };
                stream.write_json_packet(&msg);
                true
            }

            "documentElement" => {
                let (tx, rx) = channel();
                self.script_chan.send(GetDocumentElement(self.pipeline, tx)).unwrap();
                let doc_elem_info = rx.recv().unwrap();
                let node = doc_elem_info.encode(registry, true, self.script_chan.clone(), self.pipeline);

                let msg = DocumentElementReply {
                    from: self.name(),
                    node: node,
                };
                stream.write_json_packet(&msg);
                true
            }

            "clearPseudoClassLocks" => {
                let msg = ClearPseudoclassesReply {
                    from: self.name(),
                };
                stream.write_json_packet(&msg);
                true
            }

            "children" => {
                let target = msg.get(&"node".to_string()).unwrap().as_string().unwrap();
                let (tx, rx) = channel();
                self.script_chan.send(GetChildren(self.pipeline,
                                                  registry.actor_to_script(target.to_string()),
                                                  tx))
                                .unwrap();
                let children = rx.recv().unwrap();

                let msg = ChildrenReply {
                    hasFirst: true,
                    hasLast: true,
                    nodes: children.into_iter().map(|child| {
                        child.encode(registry, true, self.script_chan.clone(), self.pipeline)
                    }).collect(),
                    from: self.name(),
                };
                stream.write_json_packet(&msg);
                true
            }

            _ => false,
        })
    }
}

#[derive(RustcEncodable)]
struct GetPageStyleReply {
    from: String,
    pageStyle: PageStyleMsg,
}

#[derive(RustcEncodable)]
struct PageStyleMsg {
    actor: String,
}

struct PageStyleActor {
    name: String,
    script_chan: Sender<DevtoolScriptControlMsg>,
    pipeline: PipelineId,
}

#[derive(RustcEncodable)]
struct GetAppliedReply {
    entries: Vec<AppliedEntry>,
    rules: Vec<AppliedRule>,
    sheets: Vec<AppliedSheet>,
    from: String,
}

#[derive(RustcEncodable)]
struct GetComputedReply {
    computed: Vec<u32>, //XXX all css props
    from: String,
}

#[derive(RustcEncodable)]
struct AppliedEntry {
    rule: String,
    pseudoElement: Json,
    isSystem: bool,
    matchedSelectors: Vec<String>,
}

#[derive(RustcEncodable)]
struct AppliedRule {
    actor: String,
    __type__: u32,
    href: String,
    cssText: String,
    line: u32,
    column: u32,
    parentStyleSheet: String,
}

#[derive(RustcEncodable)]
struct AppliedSheet {
    actor: String,
    href: String,
    nodeHref: String,
    disabled: bool,
    title: String,
    system: bool,
    styleSheetIndex: isize,
    ruleCount: usize,
}

#[derive(RustcEncodable)]
struct GetLayoutReply {
    width: i32,
    height: i32,
    autoMargins: Json,
    from: String,
}

#[derive(RustcEncodable)]
#[allow(dead_code)]
struct AutoMargins {
    top: String,
    bottom: String,
    left: String,
    right: String,
}

impl Actor for PageStyleActor {
    fn name(&self) -> String {
        self.name.clone()
    }

    fn handle_message(&self,
                      registry: &ActorRegistry,
                      msg_type: &str,
                      msg: &json::Object,
                      stream: &mut TcpStream) -> Result<bool, ()> {
        Ok(match msg_type {
            "getApplied" => {
                //TODO: query script for relevant applied styles to node (msg.node)
                let msg = GetAppliedReply {
                    entries: vec!(),
                    rules: vec!(),
                    sheets: vec!(),
                    from: self.name(),
                };
                stream.write_json_packet(&msg);
                true
            }

            "getComputed" => {
                //TODO: query script for relevant computed styles on node (msg.node)
                let msg = GetComputedReply {
                    computed: vec!(),
                    from: self.name(),
                };
                stream.write_json_packet(&msg);
                true
            }

            //TODO: query script for box layout properties of node (msg.node)
            "getLayout" => {
                let target = msg.get(&"node".to_string()).unwrap().as_string().unwrap();
                let (tx, rx) = channel();
                self.script_chan.send(GetLayout(self.pipeline,
                                      registry.actor_to_script(target.to_string()),
                                      tx))
                                .unwrap();
                let (width, height) = rx.recv().unwrap();

                let auto_margins = msg.get(&"autoMargins".to_string())
                    .and_then(&Json::as_boolean).unwrap_or(false);

                //TODO: the remaining layout properties (margin, border, padding, position)
                //      as specified in getLayout in
                //      http://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/server/actors/styles.js
                let msg = GetLayoutReply {
                    width: width.round() as i32,
                    height: height.round() as i32,
                    autoMargins: if auto_margins {
                        //TODO: real values like processMargins in
                        //  http://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/server/actors/styles.js
                        let mut m = BTreeMap::new();
                        m.insert("top".to_string(), "auto".to_string().to_json());
                        m.insert("bottom".to_string(), "auto".to_string().to_json());
                        m.insert("left".to_string(), "auto".to_string().to_json());
                        m.insert("right".to_string(), "auto".to_string().to_json());
                        Json::Object(m)
                    } else {
                        Json::Null
                    },
                    from: self.name(),
                };
                stream.write_json_packet(&msg);
                true
            }

            _ => false,
        })
    }
}

impl Actor for InspectorActor {
    fn name(&self) -> String {
        self.name.clone()
    }

    fn handle_message(&self,
                      registry: &ActorRegistry,
                      msg_type: &str,
                      _msg: &json::Object,
                      stream: &mut TcpStream) -> Result<bool, ()> {
        Ok(match msg_type {
            "getWalker" => {
                if self.walker.borrow().is_none() {
                    let walker = WalkerActor {
                        name: registry.new_name("walker"),
                        script_chan: self.script_chan.clone(),
                        pipeline: self.pipeline,
                    };
                    let mut walker_name = self.walker.borrow_mut();
                    *walker_name = Some(walker.name());
                    registry.register_later(box walker);
                }

                let (tx, rx) = channel();
                self.script_chan.send(GetRootNode(self.pipeline, tx)).unwrap();
                let root_info = rx.recv().unwrap();

                let node = root_info.encode(registry, false, self.script_chan.clone(), self.pipeline);

                let msg = GetWalkerReply {
                    from: self.name(),
                    walker: WalkerMsg {
                        actor: self.walker.borrow().clone().unwrap(),
                        root: node,
                    }
                };
                stream.write_json_packet(&msg);
                true
            }

            "getPageStyle" => {
                if self.pageStyle.borrow().is_none() {
                    let style = PageStyleActor {
                        name: registry.new_name("pageStyle"),
                        script_chan: self.script_chan.clone(),
                        pipeline: self.pipeline,
                    };
                    let mut pageStyle = self.pageStyle.borrow_mut();
                    *pageStyle = Some(style.name());
                    registry.register_later(box style);
                }

                let msg = GetPageStyleReply {
                    from: self.name(),
                    pageStyle: PageStyleMsg {
                        actor: self.pageStyle.borrow().clone().unwrap(),
                    },
                };
                stream.write_json_packet(&msg);
                true
            }

            //TODO: this is an old message; try adding highlightable to the root traits instead
            //      and support getHighlighter instead
            //"highlight" => {}
            "getHighlighter" => {
                if self.highlighter.borrow().is_none() {
                    let highlighter_actor = HighlighterActor {
                        name: registry.new_name("highlighter"),
                    };
                    let mut highlighter = self.highlighter.borrow_mut();
                    *highlighter = Some(highlighter_actor.name());
                    registry.register_later(box highlighter_actor);
                }

                let msg = GetHighlighterReply {
                    from: self.name(),
                    highligter: HighlighterMsg {
                        actor: self.highlighter.borrow().clone().unwrap(),
                    },
                };
                stream.write_json_packet(&msg);
                true
            }

            _ => false,
        })
    }
}
<|endoftext|>