<commit_before>use std::cell::RefCell;
use std::slice::bytes::copy_memory;

use sdl2::rect::Rect;
use sdl2::keycode::KeyCode;
use sdl2::render::{RenderDrawer, Texture};

use gb_emu::mmu::Memory;
use gb_emu::emulator::Emulator;
use gb_emu::graphics;
use gb_emu::joypad;

use common::{PlayerData, SpriteData, Direction};

use client;
use interface::{self, extract, hacks, InterfaceData, InterfaceState};
use chat::ChatBox;
use font::Font;

enum GameState {
    Emulator,
    ChatBox,
    Menu,
}

pub struct Game<'a> {
    pub emulator: Box<Emulator>,
    pub emu_texture: Texture<'a>,
    pub default_sprite: Vec<u8>,
    pub font: Font<'a>,

    pub game_state: GameState,
    pub interface_data: RefCell<InterfaceData>,
    pub chat_box: ChatBox,
    pub player_data: PlayerData,
    pub fast_mode: bool,
}

impl<'a> Game<'a> {
    pub fn new(emulator: Box<Emulator>, emu_texture: Texture<'a>, default_sprite: Vec<u8>,
        font: Font<'a>) -> Game<'a>
    {
        Game {
            emulator: emulator,
            emu_texture: emu_texture,
            default_sprite: default_sprite,
            font: font,

            game_state: GameState::Emulator,
            interface_data: RefCell::new(InterfaceData::new()),
            chat_box: ChatBox::new(),
            player_data: PlayerData::new(),
            fast_mode: false,
        }
    }

    pub fn update(&mut self) {
        if self.interface_data.borrow().state == InterfaceState::Normal {
            let interface_data = &mut self.interface_data;
            let mut emulator = &mut self.emulator;
            let emu_texture = &mut self.emu_texture;
            let default_sprite = &self.default_sprite;
            let player_data = &mut self.player_data;

            emulator.frame(
                |cpu, mem| {
                    let interface_data = &mut interface_data.borrow_mut();
                    hacks::sprite_check(cpu, mem, interface_data);
                    hacks::display_text(cpu, mem, interface_data);
                    hacks::sprite_update_tracker(cpu, mem, interface_data);
                },

                |_, mem| {
                    let interface_data = &interface_data.borrow();
                    if interface_data.sprites_enabled() {
                        draw_other_players(interface_data, player_data, mem, default_sprite);
                    }

                    // Copy the screen to the emulator texture
                    let _ = emu_texture.with_lock(None, |mut pixels, _| {
                        copy_memory(&mut pixels, &mem.gpu.framebuffer);
                    });

                    *player_data = extract::player_data(mem);
                }
            );
        }

    }

    pub fn render(&self, drawer: &mut RenderDrawer) {
        drawer.copy(&self.emu_texture, None, Some(Rect::new(0, 0, client::EMU_WIDTH,
            client::EMU_HEIGHT)));

        self.chat_box.draw(drawer, &self.font, Rect::new(client::EMU_WIDTH, 0,
            client::CHAT_WIDTH, client::EMU_HEIGHT));
    }

    pub fn key_down(&mut self, keycode: KeyCode) {
        match self.game_state {
            GameState::Emulator => {
                self.write_to_joypad(keycode, joypad::State::Pressed);
                if keycode == KeyCode::Space { self.fast_mode = true; }
            },

            GameState::ChatBox => self.write_to_chatbox(keycode),

            GameState::Menu => unimplemented!(),
        }
    }

    pub fn key_up(&mut self, keycode: KeyCode) {
        match self.game_state {
            GameState::Emulator => {
                self.write_to_joypad(keycode, joypad::State::Released);
                if keycode == KeyCode::Space { self.fast_mode = false; }
                else if keycode == KeyCode::T { self.game_state = GameState::ChatBox; }
            },

            GameState::ChatBox => {
                if keycode == KeyCode::Return { self.game_state = GameState::Emulator; }
            },

            GameState::Menu => unimplemented!(),
        }
    }

    fn write_to_joypad(&mut self, keycode: KeyCode, state: joypad::State) {
        let joypad = &mut self.emulator.mem.joypad;
        // TODO: Add custom keybindings
        match keycode {
            KeyCode::Up => joypad.up = state,
            KeyCode::Down => joypad.down = state,
            KeyCode::Left => joypad.left = state,
            KeyCode::Right => joypad.right = state,

            KeyCode::Z => joypad.a = state,
            KeyCode::X => joypad.b = state,
            KeyCode::Return => joypad.start = state,
            KeyCode::RShift => joypad.select = state,

            _ => {},
        }
    }

    fn write_to_chatbox(&mut self, keycode: KeyCode) {
        let letter = match keycode {
            KeyCode::Return => { self.chat_box.message_ready = true; return },
            KeyCode::Backspace => { self.chat_box.message_buffer.pop(); return },
            KeyCode::Space => ' ',
            KeyCode::A => 'a',
            KeyCode::B => 'b',
            KeyCode::C => 'c',
            KeyCode::D => 'd',
            KeyCode::E => 'e',
            KeyCode::F => 'f',
            KeyCode::G => 'g',
            KeyCode::H => 'h',
            KeyCode::I => 'i',
            KeyCode::J => 'j',
            KeyCode::K => 'k',
            KeyCode::L => 'l',
            KeyCode::M => 'm',
            KeyCode::N => 'n',
            KeyCode::O => 'o',
            KeyCode::P => 'p',
            KeyCode::Q => 'q',
            KeyCode::R => 'r',
            KeyCode::S => 's',
            KeyCode::T => 't',
            KeyCode::U => 'u',
            KeyCode::V => 'v',
            KeyCode::W => 'w',
            KeyCode::X => 'x',
            KeyCode::Y => 'y',
            KeyCode::Z => 'z',
            _ => return,
        };

        self.chat_box.message_buffer.push(letter);
    }
}

fn draw_other_players(interface_data: &InterfaceData, self_data: &PlayerData, mem: &mut Memory,
    sprite: &[u8])
{
    for player in interface_data.players.values() {
        if player.is_visible_to(self_data) {
            let (x, y) = get_player_draw_position(self_data, player);
            let (index, flags) = get_sprite_index_and_flags(player);
            let sprite_data = SpriteData {
                x: x as isize,
                y: y as isize,
                index: index as usize,
                flags: flags,
            };
            interface::render_sprite(mem, sprite, &sprite_data);
        }
    }
}

fn get_player_draw_position(self_player: &PlayerData, other_player: &PlayerData) -> (i32, i32) {
    let base_x = (graphics::WIDTH as i32) / 2 - 16;
    let base_y = (graphics::HEIGHT as i32) / 2 - 12;

    let (self_x, self_y) = get_player_position(self_player);
    let (other_x, other_y) = get_player_position(other_player);

    (other_x - self_x + base_x, other_y - self_y + base_y)
}

fn get_player_position(player: &PlayerData) -> (i32, i32) {
    let x = player.movement_data.map_x as i32 * 16;
    let y = player.movement_data.map_y as i32 * 16;

    // Determine the offset of the player between tiles:
    // When a player begins walking, the walk counter is set to 8. For each step the walk counter
    // decreases by one, and the player is moved by two pixels, until the walk counter is 0. When
    // we reach this point, the players map coordinate updated.
    let ticks = player.movement_data.walk_counter;
    let offset = if ticks == 0 { 0 } else { (8 - ticks) * 2 } as i32;

    match player.movement_data.direction {
        Direction::Down  => (x, y + offset),
        Direction::Up    => (x, y - offset),
        Direction::Left  => (x - offset, y),
        Direction::Right => (x + offset, y),
    }
}

fn get_sprite_index_and_flags(player: &PlayerData) -> (isize, u8) {
    let (mut index, mut flags) = match player.movement_data.direction {
        Direction::Down  => (0, 0x00),
        Direction::Up    => (1, 0x00),
        Direction::Left  => (2, 0x00),
        Direction::Right => (2, 0x20),
    };

    flags |= 0x80;

    index += match player.movement_data.walk_counter / 4 {
        0 => 0,
        1 => 3,

        _ => 0, // Usually unreachable
    };

    (index, flags)
}
<commit_msg>Clean up and comment game code.<commit_after>use std::cell::RefCell;
use std::slice::bytes::copy_memory;

use sdl2::rect::Rect;
use sdl2::keycode::KeyCode;
use sdl2::render::{RenderDrawer, Texture};

use gb_emu::cpu::Cpu;
use gb_emu::mmu::Memory;
use gb_emu::emulator::Emulator;
use gb_emu::graphics;
use gb_emu::joypad;

use common::{PlayerData, SpriteData, Direction};

use client;
use interface::{self, extract, hacks, InterfaceData, InterfaceState};
use chat::ChatBox;
use font::Font;

enum GameState {
    Emulator,
    ChatBox,
    Menu,
}

pub struct Game<'a> {
    pub emulator: Box<Emulator>,
    pub emu_texture: Texture<'a>,
    pub default_sprite: Vec<u8>,
    pub font: Font<'a>,

    pub game_state: GameState,
    pub interface_data: RefCell<InterfaceData>,
    pub chat_box: ChatBox,
    pub player_data: PlayerData,
    pub fast_mode: bool,
}

impl<'a> Game<'a> {
    pub fn new(emulator: Box<Emulator>, emu_texture: Texture<'a>, default_sprite: Vec<u8>,
        font: Font<'a>) -> Game<'a>
    {
        Game {
            emulator: emulator,
            emu_texture: emu_texture,
            default_sprite: default_sprite,
            font: font,

            game_state: GameState::Emulator,
            interface_data: RefCell::new(InterfaceData::new()),
            chat_box: ChatBox::new(),
            player_data: PlayerData::new(),
            fast_mode: false,
        }
    }

    pub fn update(&mut self) {
        if self.interface_data.borrow().state == InterfaceState::Normal {
            // Individually borrow elements of self that we need so that we pass Rust's borrow
            // checker. (Hopefully we won't need to do this in the future)
            let interface_data = &mut self.interface_data;
            let default_sprite = &self.default_sprite;
            let player_data = &mut self.player_data;
            let emu_texture = &mut self.emu_texture;
            let emulator = &mut self.emulator;

            // After each tick we run all the hacks on the game. Most of the hacks do not actually
            // do anything for most of the cycles but wait for the program to reach a certain point.
            let on_tick = |cpu: &mut Cpu, mem: &mut Memory| {
                let interface_data = &mut interface_data.borrow_mut();
                hacks::sprite_check(cpu, mem, interface_data);
                hacks::display_text(cpu, mem, interface_data);
                hacks::sprite_update_tracker(cpu, mem, interface_data);
            };

            // On each vblank we draw other players to the screen and copy the internal framebuffer
            // to a texture. It is important do this during the vblank period to ensure that we
            // don't get partially redrawn lines affecting the result.
            let on_vblank = |_: &mut Cpu, mem: &mut Memory| {
                *player_data = extract::player_data(mem);
                let interface_data = &interface_data.borrow();
                if interface_data.sprites_enabled() {
                    draw_other_players(interface_data, player_data, mem, default_sprite);
                }

                let _ = emu_texture.with_lock(None, |mut pixels, _| {
                    copy_memory(&mut pixels, &mem.gpu.framebuffer);
                });
            };

            emulator.frame(on_tick, on_vblank);
        }

    }

    pub fn render(&self, drawer: &mut RenderDrawer) {
        drawer.copy(&self.emu_texture, None, Some(Rect::new(0, 0, client::EMU_WIDTH,
            client::EMU_HEIGHT)));

        self.chat_box.draw(drawer, &self.font, Rect::new(client::EMU_WIDTH, 0,
            client::CHAT_WIDTH, client::EMU_HEIGHT));
    }

    pub fn key_down(&mut self, keycode: KeyCode) {
        match self.game_state {
            GameState::Emulator => {
                self.write_to_joypad(keycode, joypad::State::Pressed);
                if keycode == KeyCode::Space { self.fast_mode = true; }
            },

            GameState::ChatBox => self.write_to_chatbox(keycode),

            GameState::Menu => unimplemented!(),
        }
    }

    pub fn key_up(&mut self, keycode: KeyCode) {
        match self.game_state {
            GameState::Emulator => {
                self.write_to_joypad(keycode, joypad::State::Released);
                if keycode == KeyCode::Space { self.fast_mode = false; }
                else if keycode == KeyCode::T { self.game_state = GameState::ChatBox; }
            },

            GameState::ChatBox => {
                if keycode == KeyCode::Return { self.game_state = GameState::Emulator; }
            },

            GameState::Menu => unimplemented!(),
        }
    }

    fn write_to_joypad(&mut self, keycode: KeyCode, state: joypad::State) {
        let joypad = &mut self.emulator.mem.joypad;
        // TODO: Add custom keybindings
        match keycode {
            KeyCode::Up => joypad.up = state,
            KeyCode::Down => joypad.down = state,
            KeyCode::Left => joypad.left = state,
            KeyCode::Right => joypad.right = state,

            KeyCode::Z => joypad.a = state,
            KeyCode::X => joypad.b = state,
            KeyCode::Return => joypad.start = state,
            KeyCode::RShift => joypad.select = state,

            _ => {},
        }
    }

    fn write_to_chatbox(&mut self, keycode: KeyCode) {
        let letter = match keycode {
            KeyCode::Return => { self.chat_box.message_ready = true; return },
            KeyCode::Backspace => { self.chat_box.message_buffer.pop(); return },
            KeyCode::Space => ' ',
            KeyCode::A => 'a',
            KeyCode::B => 'b',
            KeyCode::C => 'c',
            KeyCode::D => 'd',
            KeyCode::E => 'e',
            KeyCode::F => 'f',
            KeyCode::G => 'g',
            KeyCode::H => 'h',
            KeyCode::I => 'i',
            KeyCode::J => 'j',
            KeyCode::K => 'k',
            KeyCode::L => 'l',
            KeyCode::M => 'm',
            KeyCode::N => 'n',
            KeyCode::O => 'o',
            KeyCode::P => 'p',
            KeyCode::Q => 'q',
            KeyCode::R => 'r',
            KeyCode::S => 's',
            KeyCode::T => 't',
            KeyCode::U => 'u',
            KeyCode::V => 'v',
            KeyCode::W => 'w',
            KeyCode::X => 'x',
            KeyCode::Y => 'y',
            KeyCode::Z => 'z',
            _ => return,
        };

        self.chat_box.message_buffer.push(letter);
    }
}

fn draw_other_players(interface_data: &InterfaceData, self_data: &PlayerData, mem: &mut Memory,
    sprite: &[u8])
{
    for player in interface_data.players.values() {
        if player.is_visible_to(self_data) {
            let (x, y) = get_player_draw_position(self_data, player);
            let (index, flags) = get_sprite_index_and_flags(player);
            let sprite_data = SpriteData {
                x: x as isize,
                y: y as isize,
                index: index as usize,
                flags: flags,
            };
            interface::render_sprite(mem, sprite, &sprite_data);
        }
    }
}

/// Get the screen coordinates of where to draw a target player adjusted relative to the local
/// player's screen
fn get_player_draw_position(self_player: &PlayerData, other_player: &PlayerData) -> (i32, i32) {
    let base_x = (graphics::WIDTH as i32) / 2 - 16;
    let base_y = (graphics::HEIGHT as i32) / 2 - 12;

    let (self_x, self_y) = get_player_position(self_player);
    let (other_x, other_y) = get_player_position(other_player);

    (other_x - self_x + base_x, other_y - self_y + base_y)
}

fn get_player_position(player: &PlayerData) -> (i32, i32) {
    let x = player.movement_data.map_x as i32 * 16;
    let y = player.movement_data.map_y as i32 * 16;

    // Determine the offset of the player between tiles:
    // When a player begins walking, the walk counter is set to 8. For each step the walk counter
    // decreases by one, and the player is moved by two pixels, until the walk counter is 0. When
    // we reach this point, the players map coordinate updated.
    let ticks = player.movement_data.walk_counter;
    let offset = if ticks == 0 { 0 } else { (8 - ticks) * 2 } as i32;

    match player.movement_data.direction {
        Direction::Down  => (x, y + offset),
        Direction::Up    => (x, y - offset),
        Direction::Left  => (x - offset, y),
        Direction::Right => (x + offset, y),
    }
}

fn get_sprite_index_and_flags(player: &PlayerData) -> (isize, u8) {
    // Determine the base sprite index and flags that need to be set based on the direction the
    // player is currently facing.
    let (mut index, mut flags) = match player.movement_data.direction {
        Direction::Down  => (0, 0x00),
        Direction::Up    => (1, 0x00),
        Direction::Left  => (2, 0x00),
        Direction::Right => (2, 0x20),
    };

    // Set the flag that indicates background data may be drawn on top. I'm not sure if this is
    // strictly nessesary, however it seems to be set by most sprites.
    flags |= 0x80;

    // Change the frame which is displayed based on
    index += match (player.movement_data.walk_counter / 4) & 1 {
        0 => 0,
        1 => 3,
        _ => unreachable!(),
    };

    (index, flags)
}
<|endoftext|>


<commit_before>//! Main SVG document structure.

use gdk_pixbuf::{PixbufLoader, PixbufLoaderExt};
use markup5ever::{LocalName, Namespace, QualName};
use std::cell::RefCell;
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::rc::Rc;

use crate::allowed_url::{AllowedUrl, AllowedUrlError, Fragment};
use crate::create_node::create_node;
use crate::css::{self, Origin, Stylesheet};
use crate::error::LoadingError;
use crate::handle::LoadOptions;
use crate::io::{self, BinaryData};
use crate::node::{NodeData, NodeType, RsvgNode};
use crate::property_bag::PropertyBag;
use crate::structure::{IntrinsicDimensions, Svg};
use crate::surface_utils::shared_surface::SharedImageSurface;
use crate::text::NodeChars;
use crate::xml::xml_load_from_possibly_compressed_stream;

/// A loaded SVG file and its derived data.
pub struct Document {
    /// Tree of nodes; the root is guaranteed to be an `<svg>` element.
    tree: RsvgNode,

    /// Mapping from `id` attributes to nodes.
    ids: HashMap<String, RsvgNode>,

    // The following two require interior mutability because we load the extern
    // resources all over the place.  Eventually we'll be able to do this
    // once, at loading time, and keep this immutable.
    /// SVG documents referenced from this document.
    externs: RefCell<Resources>,

    /// Image resources referenced from this document.
    images: RefCell<Images>,

    /// Used to load referenced resources.
    load_options: LoadOptions,

    /// Stylesheets defined in the document
    stylesheets: Vec<Stylesheet>,
}

impl Document {
    /// Constructs a `Document` by loading it from a stream.
    pub fn load_from_stream(
        load_options: &LoadOptions,
        stream: &gio::InputStream,
        cancellable: Option<&gio::Cancellable>,
    ) -> Result<Document, LoadingError> {
        xml_load_from_possibly_compressed_stream(
            DocumentBuilder::new(load_options),
            load_options.unlimited_size,
            stream,
            cancellable,
        )
    }

    /// Gets the root node.  This is guaranteed to be an `<svg>` element.
    pub fn root(&self) -> RsvgNode {
        self.tree.clone()
    }

    /// Looks up an element node by its URL.
    ///
    /// This is also used to find elements in referenced resources, as in
    /// `xlink:href="subresource.svg#element_name".
    pub fn lookup(&self, fragment: &Fragment) -> Result<RsvgNode, LoadingError> {
        if fragment.uri().is_some() {
            self.externs
                .borrow_mut()
                .lookup(&self.load_options, fragment)
        } else {
            self.lookup_node_by_id(fragment.fragment())
                .ok_or(LoadingError::BadUrl)
        }
    }

    /// Looks up a node only in this document fragment by its `id` attribute.
    pub fn lookup_node_by_id(&self, id: &str) -> Option<RsvgNode> {
        self.ids.get(id).map(|n| (*n).clone())
    }

    /// Loads an image by URL, or returns a pre-loaded one.
    pub fn lookup_image(&self, href: &str) -> Result<SharedImageSurface, LoadingError> {
        let aurl = AllowedUrl::from_href(href, self.load_options.base_url.as_ref())
            .map_err(|_| LoadingError::BadUrl)?;

        self.images.borrow_mut().lookup(&self.load_options, &aurl)
    }

    /// Gets the dimension parameters of the toplevel `<svg>`.
    pub fn get_intrinsic_dimensions(&self) -> IntrinsicDimensions {
        let root = self.root();
        let node_data = root.borrow();

        assert!(node_data.get_type() == NodeType::Svg);
        node_data.get_impl::<Svg>().get_intrinsic_dimensions()
    }

    pub fn cascade(&mut self, extra: &[Stylesheet]) {
        css::cascade(&mut self.tree, &self.stylesheets, extra);
    }
}

struct Resources {
    resources: HashMap<AllowedUrl, Result<Rc<Document>, LoadingError>>,
}

impl Resources {
    pub fn new() -> Resources {
        Resources {
            resources: Default::default(),
        }
    }

    pub fn lookup(
        &mut self,
        load_options: &LoadOptions,
        fragment: &Fragment,
    ) -> Result<RsvgNode, LoadingError> {
        if let Some(ref href) = fragment.uri() {
            self.get_extern_document(load_options, href)
                .and_then(|doc| {
                    doc.lookup_node_by_id(fragment.fragment())
                        .ok_or(LoadingError::BadUrl)
                })
        } else {
            unreachable!();
        }
    }

    fn get_extern_document(
        &mut self,
        load_options: &LoadOptions,
        href: &str,
    ) -> Result<Rc<Document>, LoadingError> {
        let aurl = AllowedUrl::from_href(href, load_options.base_url.as_ref())
            .map_err(|_| LoadingError::BadUrl)?;

        match self.resources.entry(aurl) {
            Entry::Occupied(e) => e.get().clone(),
            Entry::Vacant(e) => {
                let aurl = e.key();
                // FIXME: pass a cancellable to these
                let doc = io::acquire_stream(aurl, None)
                    .and_then(|stream| {
                        Document::load_from_stream(
                            &load_options.copy_with_base_url(aurl),
                            &stream,
                            None,
                        )
                    })
                    .map(Rc::new);
                let res = e.insert(doc);
                res.clone()
            }
        }
    }
}

struct Images {
    images: HashMap<AllowedUrl, Result<SharedImageSurface, LoadingError>>,
}

impl Images {
    fn new() -> Images {
        Images {
            images: Default::default(),
        }
    }

    fn lookup(
        &mut self,
        load_options: &LoadOptions,
        aurl: &AllowedUrl,
    ) -> Result<SharedImageSurface, LoadingError> {
        match self.images.entry(aurl.clone()) {
            Entry::Occupied(e) => e.get().clone(),
            Entry::Vacant(e) => {
                let surface = load_image(load_options, e.key());
                let res = e.insert(surface);
                res.clone()
            }
        }
    }
}

fn load_image(
    load_options: &LoadOptions,
    aurl: &AllowedUrl,
) -> Result<SharedImageSurface, LoadingError> {
    let BinaryData {
        data: bytes,
        content_type,
    } = io::acquire_data(&aurl, None)?;

    if bytes.is_empty() {
        return Err(LoadingError::EmptyData);
    }

    let loader = if let Some(ref content_type) = content_type {
        PixbufLoader::new_with_mime_type(content_type)?
    } else {
        PixbufLoader::new()
    };

    loader.write(&bytes)?;
    loader.close()?;

    let pixbuf = loader.get_pixbuf().ok_or(LoadingError::Unknown)?;

    let bytes = if load_options.keep_image_data {
        Some(bytes)
    } else {
        None
    };

    let surface =
        SharedImageSurface::from_pixbuf(&pixbuf, bytes, content_type.as_ref().map(String::as_str))?;

    Ok(surface)
}

pub struct DocumentBuilder {
    load_options: LoadOptions,
    tree: Option<RsvgNode>,
    ids: HashMap<String, RsvgNode>,
    stylesheets: Vec<Stylesheet>,
}

impl DocumentBuilder {
    pub fn new(load_options: &LoadOptions) -> DocumentBuilder {
        DocumentBuilder {
            load_options: load_options.clone(),
            tree: None,
            ids: HashMap::new(),
            stylesheets: Vec::new(),
        }
    }

    pub fn append_stylesheet_from_xml_processing_instruction(
        &mut self,
        alternate: Option<String>,
        type_: Option<String>,
        href: &str,
    ) -> Result<(), LoadingError> {
        if type_.as_ref().map(String::as_str) != Some("text/css")
            || (alternate.is_some() && alternate.as_ref().map(String::as_str) != Some("no"))
        {
            return Err(LoadingError::BadStylesheet);
        }

        // FIXME: handle CSS errors
        if let Ok(stylesheet) =
            Stylesheet::from_href(href, self.load_options.base_url.as_ref(), Origin::Author)
        {
            self.stylesheets.push(stylesheet);
        }

        Ok(())
    }

    pub fn append_element(
        &mut self,
        name: &QualName,
        pbag: &PropertyBag,
        parent: Option<RsvgNode>,
    ) -> RsvgNode {
        let mut node = create_node(name, pbag);

        if let Some(id) = node.borrow().get_id() {
            // This is so we don't overwrite an existing id
            self.ids
                .entry(id.to_string())
                .or_insert_with(|| node.clone());
        }

        node.borrow_mut()
            .set_atts(parent.as_ref().clone(), pbag, self.load_options.locale());

        if let Some(mut parent) = parent {
            parent.append(node.clone());
        } else if self.tree.is_none() {
            self.tree = Some(node.clone());
        } else {
            panic!("The tree root has already been set");
        }

        node
    }

    pub fn append_stylesheet_from_text(&mut self, text: &str) {
        // FIXME: handle CSS errors
        if let Ok(stylesheet) =
            Stylesheet::from_data(text, self.load_options.base_url.as_ref(), Origin::Author)
        {
            self.stylesheets.push(stylesheet);
        }
    }

    pub fn append_characters(&mut self, text: &str, parent: &mut RsvgNode) {
        if !text.is_empty() {
            self.append_chars_to_parent(text, parent);
        }
    }

    fn append_chars_to_parent(&mut self, text: &str, parent: &mut RsvgNode) {
        // When the last child is a Chars node we can coalesce
        // the text and avoid screwing up the Pango layouts
        let chars_node = if let Some(child) = parent
            .last_child()
            .filter(|c| c.borrow().get_type() == NodeType::Chars)
        {
            child
        } else {
            let child = RsvgNode::new(NodeData::new(
                NodeType::Chars,
                &QualName::new(
                    None,
                    Namespace::from("https://wiki.gnome.org/Projects/LibRsvg"),
                    LocalName::from("rsvg-chars"),
                ),
                None,
                None,
                Box::new(NodeChars::new()),
            ));

            parent.append(child.clone());

            child
        };

        chars_node.borrow().get_impl::<NodeChars>().append(text);
    }

    pub fn resolve_href(&self, href: &str) -> Result<AllowedUrl, AllowedUrlError> {
        AllowedUrl::from_href(href, self.load_options.base_url.as_ref())
    }

    pub fn build(self) -> Result<Document, LoadingError> {
        let DocumentBuilder {
            load_options,
            tree,
            ids,
            stylesheets,
            ..
        } = self;

        match tree {
            None => Err(LoadingError::SvgHasNoElements),
            Some(root) => {
                if root.borrow().get_type() == NodeType::Svg {
                    let mut document = Document {
                        tree: root.clone(),
                        ids,
                        externs: RefCell::new(Resources::new()),
                        images: RefCell::new(Images::new()),
                        load_options: load_options.clone(),
                        stylesheets,
                    };

                    document.cascade(&[]);

                    Ok(document)
                } else {
                    Err(LoadingError::RootElementIsNotSvg)
                }
            }
        }
    }
}
<commit_msg>Docs for Document.cascade()<commit_after>//! Main SVG document structure.

use gdk_pixbuf::{PixbufLoader, PixbufLoaderExt};
use markup5ever::{LocalName, Namespace, QualName};
use std::cell::RefCell;
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::rc::Rc;

use crate::allowed_url::{AllowedUrl, AllowedUrlError, Fragment};
use crate::create_node::create_node;
use crate::css::{self, Origin, Stylesheet};
use crate::error::LoadingError;
use crate::handle::LoadOptions;
use crate::io::{self, BinaryData};
use crate::node::{NodeData, NodeType, RsvgNode};
use crate::property_bag::PropertyBag;
use crate::structure::{IntrinsicDimensions, Svg};
use crate::surface_utils::shared_surface::SharedImageSurface;
use crate::text::NodeChars;
use crate::xml::xml_load_from_possibly_compressed_stream;

/// A loaded SVG file and its derived data.
pub struct Document {
    /// Tree of nodes; the root is guaranteed to be an `<svg>` element.
    tree: RsvgNode,

    /// Mapping from `id` attributes to nodes.
    ids: HashMap<String, RsvgNode>,

    // The following two require interior mutability because we load the extern
    // resources all over the place.  Eventually we'll be able to do this
    // once, at loading time, and keep this immutable.
    /// SVG documents referenced from this document.
    externs: RefCell<Resources>,

    /// Image resources referenced from this document.
    images: RefCell<Images>,

    /// Used to load referenced resources.
    load_options: LoadOptions,

    /// Stylesheets defined in the document
    stylesheets: Vec<Stylesheet>,
}

impl Document {
    /// Constructs a `Document` by loading it from a stream.
    pub fn load_from_stream(
        load_options: &LoadOptions,
        stream: &gio::InputStream,
        cancellable: Option<&gio::Cancellable>,
    ) -> Result<Document, LoadingError> {
        xml_load_from_possibly_compressed_stream(
            DocumentBuilder::new(load_options),
            load_options.unlimited_size,
            stream,
            cancellable,
        )
    }

    /// Gets the root node.  This is guaranteed to be an `<svg>` element.
    pub fn root(&self) -> RsvgNode {
        self.tree.clone()
    }

    /// Looks up an element node by its URL.
    ///
    /// This is also used to find elements in referenced resources, as in
    /// `xlink:href="subresource.svg#element_name".
    pub fn lookup(&self, fragment: &Fragment) -> Result<RsvgNode, LoadingError> {
        if fragment.uri().is_some() {
            self.externs
                .borrow_mut()
                .lookup(&self.load_options, fragment)
        } else {
            self.lookup_node_by_id(fragment.fragment())
                .ok_or(LoadingError::BadUrl)
        }
    }

    /// Looks up a node only in this document fragment by its `id` attribute.
    pub fn lookup_node_by_id(&self, id: &str) -> Option<RsvgNode> {
        self.ids.get(id).map(|n| (*n).clone())
    }

    /// Loads an image by URL, or returns a pre-loaded one.
    pub fn lookup_image(&self, href: &str) -> Result<SharedImageSurface, LoadingError> {
        let aurl = AllowedUrl::from_href(href, self.load_options.base_url.as_ref())
            .map_err(|_| LoadingError::BadUrl)?;

        self.images.borrow_mut().lookup(&self.load_options, &aurl)
    }

    /// Gets the dimension parameters of the toplevel `<svg>`.
    pub fn get_intrinsic_dimensions(&self) -> IntrinsicDimensions {
        let root = self.root();
        let node_data = root.borrow();

        assert!(node_data.get_type() == NodeType::Svg);
        node_data.get_impl::<Svg>().get_intrinsic_dimensions()
    }

    /// Runs the CSS cascade on the document tree
    ///
    /// This uses the document's internal stylesheets, plus an extra set of stylesheets
    /// supplied by the caller.
    pub fn cascade(&mut self, extra: &[Stylesheet]) {
        css::cascade(&mut self.tree, &self.stylesheets, extra);
    }
}

struct Resources {
    resources: HashMap<AllowedUrl, Result<Rc<Document>, LoadingError>>,
}

impl Resources {
    pub fn new() -> Resources {
        Resources {
            resources: Default::default(),
        }
    }

    pub fn lookup(
        &mut self,
        load_options: &LoadOptions,
        fragment: &Fragment,
    ) -> Result<RsvgNode, LoadingError> {
        if let Some(ref href) = fragment.uri() {
            self.get_extern_document(load_options, href)
                .and_then(|doc| {
                    doc.lookup_node_by_id(fragment.fragment())
                        .ok_or(LoadingError::BadUrl)
                })
        } else {
            unreachable!();
        }
    }

    fn get_extern_document(
        &mut self,
        load_options: &LoadOptions,
        href: &str,
    ) -> Result<Rc<Document>, LoadingError> {
        let aurl = AllowedUrl::from_href(href, load_options.base_url.as_ref())
            .map_err(|_| LoadingError::BadUrl)?;

        match self.resources.entry(aurl) {
            Entry::Occupied(e) => e.get().clone(),
            Entry::Vacant(e) => {
                let aurl = e.key();
                // FIXME: pass a cancellable to these
                let doc = io::acquire_stream(aurl, None)
                    .and_then(|stream| {
                        Document::load_from_stream(
                            &load_options.copy_with_base_url(aurl),
                            &stream,
                            None,
                        )
                    })
                    .map(Rc::new);
                let res = e.insert(doc);
                res.clone()
            }
        }
    }
}

struct Images {
    images: HashMap<AllowedUrl, Result<SharedImageSurface, LoadingError>>,
}

impl Images {
    fn new() -> Images {
        Images {
            images: Default::default(),
        }
    }

    fn lookup(
        &mut self,
        load_options: &LoadOptions,
        aurl: &AllowedUrl,
    ) -> Result<SharedImageSurface, LoadingError> {
        match self.images.entry(aurl.clone()) {
            Entry::Occupied(e) => e.get().clone(),
            Entry::Vacant(e) => {
                let surface = load_image(load_options, e.key());
                let res = e.insert(surface);
                res.clone()
            }
        }
    }
}

fn load_image(
    load_options: &LoadOptions,
    aurl: &AllowedUrl,
) -> Result<SharedImageSurface, LoadingError> {
    let BinaryData {
        data: bytes,
        content_type,
    } = io::acquire_data(&aurl, None)?;

    if bytes.is_empty() {
        return Err(LoadingError::EmptyData);
    }

    let loader = if let Some(ref content_type) = content_type {
        PixbufLoader::new_with_mime_type(content_type)?
    } else {
        PixbufLoader::new()
    };

    loader.write(&bytes)?;
    loader.close()?;

    let pixbuf = loader.get_pixbuf().ok_or(LoadingError::Unknown)?;

    let bytes = if load_options.keep_image_data {
        Some(bytes)
    } else {
        None
    };

    let surface =
        SharedImageSurface::from_pixbuf(&pixbuf, bytes, content_type.as_ref().map(String::as_str))?;

    Ok(surface)
}

pub struct DocumentBuilder {
    load_options: LoadOptions,
    tree: Option<RsvgNode>,
    ids: HashMap<String, RsvgNode>,
    stylesheets: Vec<Stylesheet>,
}

impl DocumentBuilder {
    pub fn new(load_options: &LoadOptions) -> DocumentBuilder {
        DocumentBuilder {
            load_options: load_options.clone(),
            tree: None,
            ids: HashMap::new(),
            stylesheets: Vec::new(),
        }
    }

    pub fn append_stylesheet_from_xml_processing_instruction(
        &mut self,
        alternate: Option<String>,
        type_: Option<String>,
        href: &str,
    ) -> Result<(), LoadingError> {
        if type_.as_ref().map(String::as_str) != Some("text/css")
            || (alternate.is_some() && alternate.as_ref().map(String::as_str) != Some("no"))
        {
            return Err(LoadingError::BadStylesheet);
        }

        // FIXME: handle CSS errors
        if let Ok(stylesheet) =
            Stylesheet::from_href(href, self.load_options.base_url.as_ref(), Origin::Author)
        {
            self.stylesheets.push(stylesheet);
        }

        Ok(())
    }

    pub fn append_element(
        &mut self,
        name: &QualName,
        pbag: &PropertyBag,
        parent: Option<RsvgNode>,
    ) -> RsvgNode {
        let mut node = create_node(name, pbag);

        if let Some(id) = node.borrow().get_id() {
            // This is so we don't overwrite an existing id
            self.ids
                .entry(id.to_string())
                .or_insert_with(|| node.clone());
        }

        node.borrow_mut()
            .set_atts(parent.as_ref().clone(), pbag, self.load_options.locale());

        if let Some(mut parent) = parent {
            parent.append(node.clone());
        } else if self.tree.is_none() {
            self.tree = Some(node.clone());
        } else {
            panic!("The tree root has already been set");
        }

        node
    }

    pub fn append_stylesheet_from_text(&mut self, text: &str) {
        // FIXME: handle CSS errors
        if let Ok(stylesheet) =
            Stylesheet::from_data(text, self.load_options.base_url.as_ref(), Origin::Author)
        {
            self.stylesheets.push(stylesheet);
        }
    }

    pub fn append_characters(&mut self, text: &str, parent: &mut RsvgNode) {
        if !text.is_empty() {
            self.append_chars_to_parent(text, parent);
        }
    }

    fn append_chars_to_parent(&mut self, text: &str, parent: &mut RsvgNode) {
        // When the last child is a Chars node we can coalesce
        // the text and avoid screwing up the Pango layouts
        let chars_node = if let Some(child) = parent
            .last_child()
            .filter(|c| c.borrow().get_type() == NodeType::Chars)
        {
            child
        } else {
            let child = RsvgNode::new(NodeData::new(
                NodeType::Chars,
                &QualName::new(
                    None,
                    Namespace::from("https://wiki.gnome.org/Projects/LibRsvg"),
                    LocalName::from("rsvg-chars"),
                ),
                None,
                None,
                Box::new(NodeChars::new()),
            ));

            parent.append(child.clone());

            child
        };

        chars_node.borrow().get_impl::<NodeChars>().append(text);
    }

    pub fn resolve_href(&self, href: &str) -> Result<AllowedUrl, AllowedUrlError> {
        AllowedUrl::from_href(href, self.load_options.base_url.as_ref())
    }

    pub fn build(self) -> Result<Document, LoadingError> {
        let DocumentBuilder {
            load_options,
            tree,
            ids,
            stylesheets,
            ..
        } = self;

        match tree {
            None => Err(LoadingError::SvgHasNoElements),
            Some(root) => {
                if root.borrow().get_type() == NodeType::Svg {
                    let mut document = Document {
                        tree: root.clone(),
                        ids,
                        externs: RefCell::new(Resources::new()),
                        images: RefCell::new(Images::new()),
                        load_options: load_options.clone(),
                        stylesheets,
                    };

                    document.cascade(&[]);

                    Ok(document)
                } else {
                    Err(LoadingError::RootElementIsNotSvg)
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>use crate::runtime::context;
use crate::time::driver::Inner;
use std::fmt;
use std::sync::{Arc, Weak};

/// Handle to time driver instance.
#[derive(Clone)]
pub(crate) struct Handle {
    inner: Weak<Inner>,
}

impl Handle {
    /// Creates a new timer `Handle` from a shared `Inner` timer state.
    pub(crate) fn new(inner: Weak<Inner>) -> Self {
        Handle { inner }
    }

    /// Tries to get a handle to the current timer.
    ///
    /// # Panics
    ///
    /// This function panics if there is no current timer set.
    pub(crate) fn current() -> Self {
        context::time_handle()
            .expect("there is no timer running, must be called from the context of Tokio runtime")
    }

    /// Tries to return a strong ref to the inner
    pub(crate) fn inner(&self) -> Option<Arc<Inner>> {
        self.inner.upgrade()
    }
}

impl fmt::Debug for Handle {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "Handle")
    }
}
<commit_msg>tokio: document missing timer panics (#2801)<commit_after>use crate::runtime::context;
use crate::time::driver::Inner;
use std::fmt;
use std::sync::{Arc, Weak};

/// Handle to time driver instance.
#[derive(Clone)]
pub(crate) struct Handle {
    inner: Weak<Inner>,
}

impl Handle {
    /// Creates a new timer `Handle` from a shared `Inner` timer state.
    pub(crate) fn new(inner: Weak<Inner>) -> Self {
        Handle { inner }
    }

    /// Tries to get a handle to the current timer.
    ///
    /// # Panics
    ///
    /// This function panics if there is no current timer set.
    ///
    /// It can be triggered when `Builder::enable_time()` or
    /// `Builder::enable_all()` are not included in the builder.
    ///
    /// It can also panic whenever a timer is created outside of a Tokio
    /// runtime. That is why `rt.block_on(delay_for(...))` will panic,
    /// since the function is executed outside of the runtime.
    /// Whereas `rt.block_on(async {delay_for(...).await})` doesn't
    /// panic. And this is because wrapping the function on an async makes it
    /// lazy, and so gets executed inside the runtime successfuly without
    /// panicking.
    pub(crate) fn current() -> Self {
        context::time_handle()
            .expect("there is no timer running, must be called from the context of Tokio runtime")
    }

    /// Tries to return a strong ref to the inner
    pub(crate) fn inner(&self) -> Option<Arc<Inner>> {
        self.inner.upgrade()
    }
}

impl fmt::Debug for Handle {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "Handle")
    }
}
<|endoftext|>


<commit_before>use crate::error::msg::SemError;
use crate::vm::*;

use dora_parser::ast::visit::*;
use dora_parser::ast::Expr::*;
use dora_parser::ast::Stmt::*;
use dora_parser::ast::*;
use dora_parser::interner::Name;
use dora_parser::lexer::position::Position;

use crate::semck::globaldef::report_term_shadow;
use crate::sym::TermSym::{
    SymClassConstructor, SymClassConstructorAndModule, SymConst, SymFct, SymGlobal, SymModule,
    SymNamespace, SymStructConstructor, SymStructConstructorAndModule, SymVar,
};
use crate::sym::TypeSym::{SymClass, SymEnum, SymStruct, SymTypeParam};
use crate::ty::SourceType;

pub fn check<'ast>(vm: &VM<'ast>) {
    for fct in vm.fcts.iter() {
        let fct = fct.read();

        if !fct.is_src() {
            continue;
        }

        let src = fct.src();
        let mut src = src.write();
        let ast = fct.ast;

        let mut nameck = NameCheck {
            vm,
            fct: &fct,
            src: &mut src,
            ast,
        };

        nameck.check();
    }
}

struct NameCheck<'a, 'ast: 'a> {
    vm: &'a VM<'ast>,
    fct: &'a Fct<'ast>,
    src: &'a mut FctSrc,
    ast: &'ast Function,
}

impl<'a, 'ast> NameCheck<'a, 'ast> {
    fn check(&mut self) {
        self.vm.sym.lock().push_level();

        if self.fct.has_self() {
            // add hidden this parameter for ctors and methods
            self.add_hidden_parameter_self();
        }

        let cls_type_params_count = if let FctParent::Class(cls_id) = self.fct.parent {
            let cls = self.vm.classes.idx(cls_id);
            let cls = cls.read();

            for (tpid, tp) in cls.type_params.iter().enumerate() {
                self.vm
                    .sym
                    .lock()
                    .insert_type(tp.name, SymTypeParam(tpid.into()));
            }

            cls.type_params.len()
        } else {
            0
        };

        if let Some(ref type_params) = self.fct.ast.type_params {
            for (tpid, tp) in type_params.iter().enumerate() {
                self.vm
                    .sym
                    .lock()
                    .insert_type(tp.name, SymTypeParam((cls_type_params_count + tpid).into()));
            }
        }

        for p in &self.ast.params {
            self.visit_param(p);
        }

        {
            let block = self.ast.block();

            for stmt in &block.stmts {
                self.visit_stmt(stmt);
            }

            if let Some(ref value) = block.expr {
                self.visit_expr(value);
            }
        }

        self.vm.sym.lock().pop_level();
    }

    pub fn add_hidden_parameter_self(&mut self) {
        let ty = match self.fct.parent {
            FctParent::Class(cls_id) => {
                let cls = self.vm.classes.idx(cls_id);
                let cls = cls.read();

                cls.ty
            }

            FctParent::Impl(impl_id) => {
                let ximpl = self.vm.impls[impl_id].read();
                let cls = self.vm.classes.idx(ximpl.cls_id(self.vm));
                let cls = cls.read();

                cls.ty
            }

            FctParent::Extension(extension_id) => {
                let extension = self.vm.extensions[extension_id].read();
                extension.ty
            }

            _ => unreachable!(),
        };

        let ast_id = self.fct.ast.id;
        let name = self.vm.interner.intern("self");

        let var = Var {
            id: VarId(0),
            name,
            ty,
            reassignable: false,
            node_id: ast_id,
        };

        self.src.vars.push(var);
    }

    pub fn add_var(&mut self, mut var: Var, pos: Position) -> VarId {
        let name = var.name;
        let var_id = VarId(self.src.vars.len());

        var.id = var_id;

        match self.vm.sym.lock().insert_term(name, SymVar(var_id)) {
            Some(SymVar(_)) | None => {}
            Some(sym) => report_term_shadow(self.vm, name, self.fct.file, pos, sym),
        }
        self.src.vars.push(var);

        var_id
    }

    fn check_stmt_let(&mut self, let_decl: &'ast StmtLetType) {
        if let Some(ref expr) = let_decl.expr {
            self.visit_expr(expr);
        }

        self.check_stmt_let_pattern(&let_decl.pattern, let_decl.reassignable);
    }

    fn check_stmt_let_pattern(&mut self, pattern: &LetPattern, reassignable: bool) {
        match pattern {
            LetPattern::Ident(ref ident) => {
                let var_ctxt = Var {
                    id: VarId(0),
                    name: ident.name,
                    reassignable: reassignable || ident.mutable,
                    ty: SourceType::Unit,
                    node_id: ident.id,
                };

                let var_id = self.add_var(var_ctxt, ident.pos);
                self.src.map_vars.insert(ident.id, var_id)
            }

            LetPattern::Underscore(_) => {
                // no variable to declare
            }

            LetPattern::Tuple(ref tuple) => {
                for sub in &tuple.parts {
                    self.check_stmt_let_pattern(sub, reassignable);
                }
            }
        }
    }

    fn check_stmt_for(&mut self, fl: &'ast StmtForType) {
        self.visit_expr(&fl.expr);

        self.vm.sym.lock().push_level();

        self.check_stmt_let_pattern(&fl.pattern, false);

        self.visit_stmt(&fl.block);
        self.vm.sym.lock().pop_level();
    }

    fn check_expr_ident(&mut self, ident: &'ast ExprIdentType) {
        let term_sym = self.vm.sym.lock().get_term(ident.name);
        let type_sym = self.vm.sym.lock().get_type(ident.name);

        match (term_sym, type_sym) {
            (Some(SymVar(id)), None) => {
                self.src.map_idents.insert(ident.id, IdentType::Var(id));
            }

            (Some(SymGlobal(id)), None) => {
                self.src.map_idents.insert(ident.id, IdentType::Global(id));
            }

            (Some(SymConst(id)), None) => {
                self.src.map_idents.insert(ident.id, IdentType::Const(id));
            }

            (Some(SymFct(id)), None) => {
                self.src.map_idents.insert(ident.id, IdentType::Fct(id));
            }

            (Some(SymModule(id)), None) => {
                self.src.map_idents.insert(ident.id, IdentType::Module(id));
            }

            (None, Some(SymStruct(id))) => {
                self.src.map_idents.insert(ident.id, IdentType::Struct(id));
            }

            (None, Some(SymClass(id))) => {
                self.src.map_idents.insert(ident.id, IdentType::Class(id));
            }

            (None, Some(SymTypeParam(id))) => {
                let ty = SourceType::TypeParam(id);
                self.src
                    .map_idents
                    .insert(ident.id, IdentType::TypeParam(ty))
            }

            (None, Some(SymEnum(id))) => self.src.map_idents.insert(ident.id, IdentType::Enum(id)),

            (Some(SymModule(module_id)), Some(SymClass(class_id)))
            | (Some(SymClassConstructorAndModule(_, module_id)), Some(SymClass(class_id))) => self
                .src
                .map_idents
                .insert(ident.id, IdentType::ClassAndModule(class_id, module_id)),

            (Some(SymClassConstructor(id)), _) => {
                self.src.map_idents.insert(ident.id, IdentType::Class(id))
            }

            (Some(SymModule(module_id)), Some(SymStruct(struct_id)))
            | (Some(SymStructConstructorAndModule(_, module_id)), Some(SymStruct(struct_id))) => {
                self.src
                    .map_idents
                    .insert(ident.id, IdentType::StructAndModule(struct_id, module_id))
            }

            (Some(SymStructConstructor(id)), _) => {
                self.src.map_idents.insert(ident.id, IdentType::Struct(id))
            }

            (Some(SymNamespace(id)), _) => self
                .src
                .map_idents
                .insert(ident.id, IdentType::Namespace(id)),

            (None, None) => {
                let name = self.vm.interner.str(ident.name).to_string();
                report(
                    self.vm,
                    self.fct.file,
                    ident.pos,
                    SemError::UnknownIdentifier(name),
                );
            }

            (term_sym, type_sym) => unreachable!(format!("{:?} {:?}", term_sym, type_sym)),
        }
    }

    fn check_expr_path(&mut self, path: &'ast ExprPathType) {
        self.visit_expr(&path.lhs);
        // do not check right hand site of path
    }

    fn check_expr_dot(&mut self, dot: &'ast ExprDotType) {
        self.visit_expr(&dot.lhs);
        // do not check right hand site of dot
    }

    fn check_expr_block(&mut self, block: &'ast ExprBlockType) {
        self.vm.sym.lock().push_level();

        for stmt in &block.stmts {
            self.visit_stmt(stmt);
        }

        if let Some(ref expr) = block.expr {
            self.visit_expr(expr);
        }

        self.vm.sym.lock().pop_level();
    }
}

impl<'a, 'ast> Visitor<'ast> for NameCheck<'a, 'ast> {
    fn visit_param(&mut self, p: &'ast Param) {
        let var_ctxt = Var {
            id: VarId(0),
            name: p.name,
            reassignable: false,
            ty: SourceType::Unit,
            node_id: p.id,
        };

        // params are only allowed to replace functions, vars cannot be replaced
        let term_sym = self.vm.sym.lock().get_term(p.name);
        match term_sym {
            Some(SymFct(_)) | None => {
                let var_id = self.add_var(var_ctxt, p.pos);
                self.src.map_vars.insert(p.id, var_id)
            }
            Some(conflict_sym) => {
                report_term_shadow(self.vm, p.name, self.fct.file, p.pos, conflict_sym)
            }
        }
    }

    fn visit_stmt(&mut self, s: &'ast Stmt) {
        match *s {
            StmtLet(ref stmt) => self.check_stmt_let(stmt),
            StmtFor(ref stmt) => self.check_stmt_for(stmt),

            // no need to handle rest of statements
            _ => visit::walk_stmt(self, s),
        }
    }

    fn visit_expr(&mut self, e: &'ast Expr) {
        match e {
            &ExprIdent(ref ident) => self.check_expr_ident(ident),
            &ExprPath(ref path) => self.check_expr_path(path),
            &ExprDot(ref dot) => self.check_expr_dot(dot),
            &ExprBlock(ref block) => self.check_expr_block(block),

            // no need to handle rest of expressions
            _ => visit::walk_expr(self, e),
        }
    }
}

fn report(vm: &VM, file: FileId, pos: Position, msg: SemError) {
    vm.diag.lock().report(file, pos, msg);
}

fn str(vm: &VM, name: Name) -> String {
    vm.interner.str(name).to_string()
}

#[cfg(test)]
mod tests {
    use crate::error::msg::SemError;
    use crate::error::msg::SemError::ShadowClassConstructor;
    use crate::semck::tests::*;

    #[test]
    fn multiple_functions() {
        ok("fun f() {}\nfun g() {}");
    }

    #[test]
    fn redefine_function() {
        err(
            "fun f() {}\nfun f() {}",
            pos(2, 1),
            SemError::ShadowFunction("f".into()),
        );
    }

    #[test]
    fn shadow_type_with_function() {
        err(
            "fun Int32() {}",
            pos(1, 1),
            SemError::ShadowClassConstructor("Int32".into()),
        );
    }

    #[test]
    fn shadow_type_with_param() {
        err(
            "fun test(Bool: String) {}",
            pos(1, 10),
            ShadowClassConstructor("Bool".into()),
        );
    }

    #[test]
    fn shadow_type_with_var() {
        ok("fun test() { let String = 3; }");
    }

    #[test]
    fn shadow_function() {
        ok("fun f() { let f = 1; }");
        err(
            "fun f() { let f = 1; f(); }",
            pos(1, 23),
            SemError::UnknownMethod("Int32".into(), "get".into(), Vec::new()),
        );
    }

    #[test]
    fn shadow_var() {
        ok("fun f() { let f = 1; let f = 2; }");
    }

    #[test]
    fn shadow_param() {
        err(
            "fun f(a: Int32, b: Int32, a: String) {}",
            pos(1, 27),
            SemError::ShadowParam("a".into()),
        );
    }

    #[test]
    fn multiple_params() {
        ok("fun f(a: Int32, b: Int32, c:String) {}");
    }

    #[test]
    fn undefined_variable() {
        err(
            "fun f() { let b = a; }",
            pos(1, 19),
            SemError::UnknownIdentifier("a".into()),
        );
        err(
            "fun f() { a; }",
            pos(1, 11),
            SemError::UnknownIdentifier("a".into()),
        );
    }

    #[test]
    fn undefined_function() {
        err(
            "fun f() { foo(); }",
            pos(1, 11),
            SemError::UnknownIdentifier("foo".into()),
        );
    }

    #[test]
    fn recursive_function_call() {
        ok("fun f() { f(); }");
    }

    #[test]
    fn function_call() {
        ok("fun a() {}\nfun b() { a(); }");

        // non-forward definition of functions
        ok("fun a() { b(); }\nfun b() {}");
    }

    #[test]
    fn variable_outside_of_scope() {
        err(
            "fun f(): Int32 { { let a = 1; } return a; }",
            pos(1, 40),
            SemError::UnknownIdentifier("a".into()),
        );

        ok("fun f(): Int32 { let a = 1; { let a = 2; } return a; }");
    }

    #[test]
    fn const_value() {
        ok("const one: Int32 = 1;
            fun f(): Int32 { return one; }");
    }

    #[test]
    fn for_var() {
        ok("fun f() { for i in range(0, 4) { i; } }");
    }
}
<commit_msg>use SymTables in nameck<commit_after>use crate::error::msg::SemError;
use crate::sym::SymTables;
use crate::vm::*;

use dora_parser::ast::visit::*;
use dora_parser::ast::Expr::*;
use dora_parser::ast::Stmt::*;
use dora_parser::ast::*;
use dora_parser::interner::Name;
use dora_parser::lexer::position::Position;

use crate::semck::globaldef::report_term_shadow;
use crate::sym::TermSym::{
    SymClassConstructor, SymClassConstructorAndModule, SymConst, SymFct, SymGlobal, SymModule,
    SymNamespace, SymStructConstructor, SymStructConstructorAndModule, SymVar,
};
use crate::sym::TypeSym::{SymClass, SymEnum, SymStruct, SymTypeParam};
use crate::ty::SourceType;

pub fn check<'ast>(vm: &VM<'ast>) {
    for fct in vm.fcts.iter() {
        let fct = fct.read();

        if !fct.is_src() {
            continue;
        }

        let src = fct.src();
        let mut src = src.write();
        let ast = fct.ast;

        let global_namespace = vm.global_namespace.read();

        let mut nameck = NameCheck {
            vm,
            fct: &fct,
            src: &mut src,
            ast,
            sym: SymTables::new(&*global_namespace),
        };

        nameck.check();
    }
}

struct NameCheck<'a, 'ast: 'a> {
    vm: &'a VM<'ast>,
    fct: &'a Fct<'ast>,
    src: &'a mut FctSrc,
    ast: &'ast Function,
    sym: SymTables<'a>,
}

impl<'a, 'ast> NameCheck<'a, 'ast> {
    fn check(&mut self) {
        self.sym.push_level();

        if self.fct.has_self() {
            // add hidden this parameter for ctors and methods
            self.add_hidden_parameter_self();
        }

        let cls_type_params_count = if let FctParent::Class(cls_id) = self.fct.parent {
            let cls = self.vm.classes.idx(cls_id);
            let cls = cls.read();

            for (tpid, tp) in cls.type_params.iter().enumerate() {
                self.sym.insert_type(tp.name, SymTypeParam(tpid.into()));
            }

            cls.type_params.len()
        } else {
            0
        };

        if let Some(ref type_params) = self.fct.ast.type_params {
            for (tpid, tp) in type_params.iter().enumerate() {
                self.sym
                    .insert_type(tp.name, SymTypeParam((cls_type_params_count + tpid).into()));
            }
        }

        for p in &self.ast.params {
            self.visit_param(p);
        }

        {
            let block = self.ast.block();

            for stmt in &block.stmts {
                self.visit_stmt(stmt);
            }

            if let Some(ref value) = block.expr {
                self.visit_expr(value);
            }
        }

        self.sym.pop_level();
    }

    pub fn add_hidden_parameter_self(&mut self) {
        let ty = match self.fct.parent {
            FctParent::Class(cls_id) => {
                let cls = self.vm.classes.idx(cls_id);
                let cls = cls.read();

                cls.ty
            }

            FctParent::Impl(impl_id) => {
                let ximpl = self.vm.impls[impl_id].read();
                let cls = self.vm.classes.idx(ximpl.cls_id(self.vm));
                let cls = cls.read();

                cls.ty
            }

            FctParent::Extension(extension_id) => {
                let extension = self.vm.extensions[extension_id].read();
                extension.ty
            }

            _ => unreachable!(),
        };

        let ast_id = self.fct.ast.id;
        let name = self.vm.interner.intern("self");

        let var = Var {
            id: VarId(0),
            name,
            ty,
            reassignable: false,
            node_id: ast_id,
        };

        self.src.vars.push(var);
    }

    pub fn add_var(&mut self, mut var: Var, pos: Position) -> VarId {
        let name = var.name;
        let var_id = VarId(self.src.vars.len());

        var.id = var_id;

        match self.sym.insert_term(name, SymVar(var_id)) {
            Some(SymVar(_)) | None => {}
            Some(sym) => report_term_shadow(self.vm, name, self.fct.file, pos, sym),
        }
        self.src.vars.push(var);

        var_id
    }

    fn check_stmt_let(&mut self, let_decl: &'ast StmtLetType) {
        if let Some(ref expr) = let_decl.expr {
            self.visit_expr(expr);
        }

        self.check_stmt_let_pattern(&let_decl.pattern, let_decl.reassignable);
    }

    fn check_stmt_let_pattern(&mut self, pattern: &LetPattern, reassignable: bool) {
        match pattern {
            LetPattern::Ident(ref ident) => {
                let var_ctxt = Var {
                    id: VarId(0),
                    name: ident.name,
                    reassignable: reassignable || ident.mutable,
                    ty: SourceType::Unit,
                    node_id: ident.id,
                };

                let var_id = self.add_var(var_ctxt, ident.pos);
                self.src.map_vars.insert(ident.id, var_id)
            }

            LetPattern::Underscore(_) => {
                // no variable to declare
            }

            LetPattern::Tuple(ref tuple) => {
                for sub in &tuple.parts {
                    self.check_stmt_let_pattern(sub, reassignable);
                }
            }
        }
    }

    fn check_stmt_for(&mut self, fl: &'ast StmtForType) {
        self.visit_expr(&fl.expr);

        self.sym.push_level();

        self.check_stmt_let_pattern(&fl.pattern, false);

        self.visit_stmt(&fl.block);
        self.sym.pop_level();
    }

    fn check_expr_ident(&mut self, ident: &'ast ExprIdentType) {
        let term_sym = self.sym.get_term(ident.name);
        let type_sym = self.sym.get_type(ident.name);

        match (term_sym, type_sym) {
            (Some(SymVar(id)), None) => {
                self.src.map_idents.insert(ident.id, IdentType::Var(id));
            }

            (Some(SymGlobal(id)), None) => {
                self.src.map_idents.insert(ident.id, IdentType::Global(id));
            }

            (Some(SymConst(id)), None) => {
                self.src.map_idents.insert(ident.id, IdentType::Const(id));
            }

            (Some(SymFct(id)), None) => {
                self.src.map_idents.insert(ident.id, IdentType::Fct(id));
            }

            (Some(SymModule(id)), None) => {
                self.src.map_idents.insert(ident.id, IdentType::Module(id));
            }

            (None, Some(SymStruct(id))) => {
                self.src.map_idents.insert(ident.id, IdentType::Struct(id));
            }

            (None, Some(SymClass(id))) => {
                self.src.map_idents.insert(ident.id, IdentType::Class(id));
            }

            (None, Some(SymTypeParam(id))) => {
                let ty = SourceType::TypeParam(id);
                self.src
                    .map_idents
                    .insert(ident.id, IdentType::TypeParam(ty))
            }

            (None, Some(SymEnum(id))) => self.src.map_idents.insert(ident.id, IdentType::Enum(id)),

            (Some(SymModule(module_id)), Some(SymClass(class_id)))
            | (Some(SymClassConstructorAndModule(_, module_id)), Some(SymClass(class_id))) => self
                .src
                .map_idents
                .insert(ident.id, IdentType::ClassAndModule(class_id, module_id)),

            (Some(SymClassConstructor(id)), _) => {
                self.src.map_idents.insert(ident.id, IdentType::Class(id))
            }

            (Some(SymModule(module_id)), Some(SymStruct(struct_id)))
            | (Some(SymStructConstructorAndModule(_, module_id)), Some(SymStruct(struct_id))) => {
                self.src
                    .map_idents
                    .insert(ident.id, IdentType::StructAndModule(struct_id, module_id))
            }

            (Some(SymStructConstructor(id)), _) => {
                self.src.map_idents.insert(ident.id, IdentType::Struct(id))
            }

            (Some(SymNamespace(id)), _) => self
                .src
                .map_idents
                .insert(ident.id, IdentType::Namespace(id)),

            (None, None) => {
                let name = self.vm.interner.str(ident.name).to_string();
                report(
                    self.vm,
                    self.fct.file,
                    ident.pos,
                    SemError::UnknownIdentifier(name),
                );
            }

            (term_sym, type_sym) => unreachable!(format!("{:?} {:?}", term_sym, type_sym)),
        }
    }

    fn check_expr_path(&mut self, path: &'ast ExprPathType) {
        self.visit_expr(&path.lhs);
        // do not check right hand site of path
    }

    fn check_expr_dot(&mut self, dot: &'ast ExprDotType) {
        self.visit_expr(&dot.lhs);
        // do not check right hand site of dot
    }

    fn check_expr_block(&mut self, block: &'ast ExprBlockType) {
        self.sym.push_level();

        for stmt in &block.stmts {
            self.visit_stmt(stmt);
        }

        if let Some(ref expr) = block.expr {
            self.visit_expr(expr);
        }

        self.sym.pop_level();
    }
}

impl<'a, 'ast> Visitor<'ast> for NameCheck<'a, 'ast> {
    fn visit_param(&mut self, p: &'ast Param) {
        let var_ctxt = Var {
            id: VarId(0),
            name: p.name,
            reassignable: false,
            ty: SourceType::Unit,
            node_id: p.id,
        };

        // params are only allowed to replace functions, vars cannot be replaced
        let term_sym = self.sym.get_term(p.name);
        match term_sym {
            Some(SymFct(_)) | None => {
                let var_id = self.add_var(var_ctxt, p.pos);
                self.src.map_vars.insert(p.id, var_id)
            }
            Some(conflict_sym) => {
                report_term_shadow(self.vm, p.name, self.fct.file, p.pos, conflict_sym)
            }
        }
    }

    fn visit_stmt(&mut self, s: &'ast Stmt) {
        match *s {
            StmtLet(ref stmt) => self.check_stmt_let(stmt),
            StmtFor(ref stmt) => self.check_stmt_for(stmt),

            // no need to handle rest of statements
            _ => visit::walk_stmt(self, s),
        }
    }

    fn visit_expr(&mut self, e: &'ast Expr) {
        match e {
            &ExprIdent(ref ident) => self.check_expr_ident(ident),
            &ExprPath(ref path) => self.check_expr_path(path),
            &ExprDot(ref dot) => self.check_expr_dot(dot),
            &ExprBlock(ref block) => self.check_expr_block(block),

            // no need to handle rest of expressions
            _ => visit::walk_expr(self, e),
        }
    }
}

fn report(vm: &VM, file: FileId, pos: Position, msg: SemError) {
    vm.diag.lock().report(file, pos, msg);
}

fn str(vm: &VM, name: Name) -> String {
    vm.interner.str(name).to_string()
}

#[cfg(test)]
mod tests {
    use crate::error::msg::SemError;
    use crate::error::msg::SemError::ShadowClassConstructor;
    use crate::semck::tests::*;

    #[test]
    fn multiple_functions() {
        ok("fun f() {}\nfun g() {}");
    }

    #[test]
    fn redefine_function() {
        err(
            "fun f() {}\nfun f() {}",
            pos(2, 1),
            SemError::ShadowFunction("f".into()),
        );
    }

    #[test]
    fn shadow_type_with_function() {
        err(
            "fun Int32() {}",
            pos(1, 1),
            SemError::ShadowClassConstructor("Int32".into()),
        );
    }

    #[test]
    fn shadow_type_with_param() {
        err(
            "fun test(Bool: String) {}",
            pos(1, 10),
            ShadowClassConstructor("Bool".into()),
        );
    }

    #[test]
    fn shadow_type_with_var() {
        ok("fun test() { let String = 3; }");
    }

    #[test]
    fn shadow_function() {
        ok("fun f() { let f = 1; }");
        err(
            "fun f() { let f = 1; f(); }",
            pos(1, 23),
            SemError::UnknownMethod("Int32".into(), "get".into(), Vec::new()),
        );
    }

    #[test]
    fn shadow_var() {
        ok("fun f() { let f = 1; let f = 2; }");
    }

    #[test]
    fn shadow_param() {
        err(
            "fun f(a: Int32, b: Int32, a: String) {}",
            pos(1, 27),
            SemError::ShadowParam("a".into()),
        );
    }

    #[test]
    fn multiple_params() {
        ok("fun f(a: Int32, b: Int32, c:String) {}");
    }

    #[test]
    fn undefined_variable() {
        err(
            "fun f() { let b = a; }",
            pos(1, 19),
            SemError::UnknownIdentifier("a".into()),
        );
        err(
            "fun f() { a; }",
            pos(1, 11),
            SemError::UnknownIdentifier("a".into()),
        );
    }

    #[test]
    fn undefined_function() {
        err(
            "fun f() { foo(); }",
            pos(1, 11),
            SemError::UnknownIdentifier("foo".into()),
        );
    }

    #[test]
    fn recursive_function_call() {
        ok("fun f() { f(); }");
    }

    #[test]
    fn function_call() {
        ok("fun a() {}\nfun b() { a(); }");

        // non-forward definition of functions
        ok("fun a() { b(); }\nfun b() {}");
    }

    #[test]
    fn variable_outside_of_scope() {
        err(
            "fun f(): Int32 { { let a = 1; } return a; }",
            pos(1, 40),
            SemError::UnknownIdentifier("a".into()),
        );

        ok("fun f(): Int32 { let a = 1; { let a = 2; } return a; }");
    }

    #[test]
    fn const_value() {
        ok("const one: Int32 = 1;
            fun f(): Int32 { return one; }");
    }

    #[test]
    fn for_var() {
        ok("fun f() { for i in range(0, 4) { i; } }");
    }
}
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

//! Handlers and types for agent's actions.
//!
//! The basic functionality that a GRR agent exposes is called an _action_.
//! Actions are invoked by the server (when running a _flow_), should gather
//! requested information and report back to the server.
//!
//! In RRG each action consists of three components: a request type, a response
//! type and an action handler. Request and response types wrap lower-level
//! Protocol Buffer messages sent by and to the GRR server. Handlers accept one
//! instance of the corresponding request type and send some (zero or more)
//! instances of the corresponding response type.

#[cfg(feature = "action-filesystems")]
#[cfg(target_os = "linux")]
pub mod filesystems;

#[cfg(feature = "action-interfaces")]
#[cfg(target_family = "unix")]
pub mod interfaces;

#[cfg(feature = "action-metadata")]
pub mod metadata;

#[cfg(feature = "action-listdir")]
pub mod listdir;

#[cfg(feature = "action-timeline")]
pub mod timeline;

#[cfg(feature = "action-network")]
pub mod network;

#[cfg(feature = "action-stat")]
pub mod stat;

#[cfg(feature = "action-insttime")]
pub mod insttime;

#[cfg(feature = "action-memsize")]
pub mod memsize;

#[cfg(feature = "action-finder")]
pub mod finder;

use crate::session::{self, Session};

/// Abstraction for action-specific requests.
///
/// Protocol Buffer messages received from the GRR server are not necessarily
/// easy to work with and are hardly idiomatic to Rust. For this reason, actions
/// should define more structured data types to represent their input and should
/// be able to parse raw messages into them.
pub trait Request: Sized {

    /// A type of the corresponding raw proto message.
    type Proto: protobuf::Message + Default;

    /// A method for converting raw proto messages into structured requests.
    fn from_proto(proto: Self::Proto) -> Result<Self, session::ParseError>;
}

/// Abstraction for action-specific responses.
///
/// Like with the [`Request`] type, Protocol Buffer messages sent to the GRR
/// server are very idiomatic to Rust. For this reason, actions should define
/// more structured data types to represent responses and provide a way to
/// convert them into the wire format.
///
/// Note that because of the design flaws in the protocol, actions also need to
/// specify a name of the wrapper RDF class from the Python implementation.
/// Hopefully, one day this issue would be fixed and class names will not leak
/// into the specification.
///
/// [`Request`]: trait.Request.html
pub trait Response: Sized {

    /// A name of the corresponding RDF class.
    const RDF_NAME: Option<&'static str>;

    /// A type of the corresponding raw proto message.
    type Proto: protobuf::Message + Default;

    /// A method for converting structured responses into raw proto messages.
    fn into_proto(self) -> Self::Proto;
}

impl Request for () {

    type Proto = protobuf::well_known_types::Empty;

    fn from_proto(_: protobuf::well_known_types::Empty) -> Result<(), session::ParseError> {
        Ok(())
    }
}

impl Response for () {

    const RDF_NAME: Option<&'static str> = None;

    type Proto = protobuf::well_known_types::Empty;

    fn into_proto(self) -> protobuf::well_known_types::Empty {
        protobuf::well_known_types::Empty::new()
    }
}

/// Dispatches the given `request` to an appropriate action handler.
///
/// This method is a mapping between action names (as specified in the protocol)
/// and action handlers (implemented on the agent).
///
/// # Errors
///
/// This function will return an error if the given action is unknown (or not
/// yet implemented).
///
/// It will also error out if the action execution itself fails for whatever
/// reason.
pub fn dispatch<'s, S>(session: &mut S, request: crate::message::Request) -> session::Result<()>
where
    S: Session,
{
    match request.action_name() {
        #[cfg(feature = "action-metadata")]
        "GetClientInfo" => handle(session, request, self::metadata::handle),

        #[cfg(feature = "action-listdir")]
        "ListDirectory" => handle(session, request, self::listdir::handle),

        #[cfg(feature = "action-timeline")]
        "Timeline" => handle(session, request, self::timeline::handle),

        #[cfg(feature = "action-network")]
        "ListNetworkConnections" => handle(session, request, self::network::handle),

        #[cfg(feature = "action-stat")]
        "GetFileStat" => handle(session, request, self::stat::handle),

        #[cfg(feature = "action-insttime")]
        "GetInstallDate" => handle(session, request, self::insttime::handle),

        #[cfg(feature = "action-interfaces")]
        #[cfg(target_family = "unix")]
        "EnumerateInterfaces" => handle(session, request, self::interfaces::handle),

        #[cfg(feature = "action-filesystems")]
        #[cfg(target_os = "linux")]
        "EnumerateFilesystems" => handle(session, request, self::filesystems::handle),

        #[cfg(feature = "action-memsize")]
        "GetMemorySize" => handle(session, request, self::memsize::handle),

        action => return Err(session::Error::Dispatch(String::from(action))),
    }
}

/// Handles a `request` using the specified `handler`.
///
/// This method will attempt to interpret request arguments for the specific
/// action and execute the handler with them.
///
/// # Errors
///
/// This function will return an error if the request arguments cannot be parsed
/// for the specific action or if the action execution fails.
fn handle<S, A, H>(session: &mut S, request: crate::message::Request, handler: H) -> session::Result<()>
where
    S: crate::session::Session,
    A: Args,
    H: FnOnce(&mut S, A) -> session::Result<()>,
{
    let args = request.parse_args()?;
    handler(session, args)
}

// TODO(panhania@): Remove all usages of the `Request` trait and replace it with
// `Args`.
pub trait Args: Sized {
    /// Low-level Protocol Buffers type representing the action arguments.
    type Proto: protobuf::Message + Default;

    /// Converts a low-level type to a structured request arguments.
    fn from_proto(proto: Self::Proto) -> Result<Self, ParseArgsError>;
}

impl Args for () {

    type Proto = protobuf::well_known_types::Empty;

    fn from_proto(_: protobuf::well_known_types::Empty) -> Result<(), ParseArgsError> {
        Ok(())
    }
}

pub trait Item: Sized {
    /// Low-level Protocol Buffers type representing the action results.
    type Proto: protobuf::Message + Default;

    /// A name of the corresponding RDF class in GRR.
    const RDF_NAME: &'static str;

    /// Converts an action result ot its low-level representation.
    fn into_proto(self) -> Self::Proto;
}

impl Item for () {

    // This implementation is intended to be used only in tests and we do not
    // really care about the `RDF_NAME` field there. And since GRR does not have
    // any RDF wrapper for empty type (except maybe `EmptyFlowArgs`, but this is
    // semantically different), we just leave it blank.
    const RDF_NAME: &'static str = "";

    type Proto = protobuf::well_known_types::Empty;

    fn into_proto(self) -> protobuf::well_known_types::Empty {
        protobuf::well_known_types::Empty::new()
    }
}

/// The error type for cases when action argument parsing fails.
#[derive(Debug)]
pub struct ParseArgsError {
    /// A corresponding [`ParseArgsErrorKind`] of this error.
    kind: ParseArgsErrorKind,
    /// A detailed payload associated with the error.
    error: Box<dyn std::error::Error + Send + Sync>,
}

impl ParseArgsError {

    /// Creates a new error instance caused by some invalid field error.
    fn invalid_field<E>(error: E) -> ParseArgsError
    where
        E: std::error::Error + Send + Sync + 'static,
    {
        ParseArgsError {
            kind: ParseArgsErrorKind::InvalidField,
            error: Box::new(error),
        }
    }
}

/// Kinds of errors that can happen when parsing action arguments.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum ParseArgsErrorKind {
    /// The serialized message with arguments was impossible to deserialize.
    InvalidProto,
    // TODO(panhania@): Augment with field name.
    /// One of the fields of the arguments struct is invalid.
    InvalidField,
}

impl ParseArgsErrorKind {

    fn as_str(&self) -> &'static str {
        use ParseArgsErrorKind::*;

        match *self {
            InvalidProto => "invalid serialized protobuf message",
            InvalidField => "invalid argument field",
        }
    }
}

impl std::fmt::Display for ParseArgsError {

    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(fmt, "{}: {}", self.kind.as_str(), self.error)
    }
}

impl std::error::Error for ParseArgsError {

    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        self.error.source()
    }
}

impl From<protobuf::ProtobufError> for ParseArgsError {

    fn from(error: protobuf::ProtobufError) -> Self {
        ParseArgsError {
            kind: ParseArgsErrorKind::InvalidProto,
            error: Box::new(error),
        }
    }
}
<commit_msg>Define a new type for dispatch errors.<commit_after>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

//! Handlers and types for agent's actions.
//!
//! The basic functionality that a GRR agent exposes is called an _action_.
//! Actions are invoked by the server (when running a _flow_), should gather
//! requested information and report back to the server.
//!
//! In RRG each action consists of three components: a request type, a response
//! type and an action handler. Request and response types wrap lower-level
//! Protocol Buffer messages sent by and to the GRR server. Handlers accept one
//! instance of the corresponding request type and send some (zero or more)
//! instances of the corresponding response type.

#[cfg(feature = "action-filesystems")]
#[cfg(target_os = "linux")]
pub mod filesystems;

#[cfg(feature = "action-interfaces")]
#[cfg(target_family = "unix")]
pub mod interfaces;

#[cfg(feature = "action-metadata")]
pub mod metadata;

#[cfg(feature = "action-listdir")]
pub mod listdir;

#[cfg(feature = "action-timeline")]
pub mod timeline;

#[cfg(feature = "action-network")]
pub mod network;

#[cfg(feature = "action-stat")]
pub mod stat;

#[cfg(feature = "action-insttime")]
pub mod insttime;

#[cfg(feature = "action-memsize")]
pub mod memsize;

#[cfg(feature = "action-finder")]
pub mod finder;

use crate::session::{self, Session};

/// Abstraction for action-specific requests.
///
/// Protocol Buffer messages received from the GRR server are not necessarily
/// easy to work with and are hardly idiomatic to Rust. For this reason, actions
/// should define more structured data types to represent their input and should
/// be able to parse raw messages into them.
pub trait Request: Sized {

    /// A type of the corresponding raw proto message.
    type Proto: protobuf::Message + Default;

    /// A method for converting raw proto messages into structured requests.
    fn from_proto(proto: Self::Proto) -> Result<Self, session::ParseError>;
}

/// Abstraction for action-specific responses.
///
/// Like with the [`Request`] type, Protocol Buffer messages sent to the GRR
/// server are very idiomatic to Rust. For this reason, actions should define
/// more structured data types to represent responses and provide a way to
/// convert them into the wire format.
///
/// Note that because of the design flaws in the protocol, actions also need to
/// specify a name of the wrapper RDF class from the Python implementation.
/// Hopefully, one day this issue would be fixed and class names will not leak
/// into the specification.
///
/// [`Request`]: trait.Request.html
pub trait Response: Sized {

    /// A name of the corresponding RDF class.
    const RDF_NAME: Option<&'static str>;

    /// A type of the corresponding raw proto message.
    type Proto: protobuf::Message + Default;

    /// A method for converting structured responses into raw proto messages.
    fn into_proto(self) -> Self::Proto;
}

impl Request for () {

    type Proto = protobuf::well_known_types::Empty;

    fn from_proto(_: protobuf::well_known_types::Empty) -> Result<(), session::ParseError> {
        Ok(())
    }
}

impl Response for () {

    const RDF_NAME: Option<&'static str> = None;

    type Proto = protobuf::well_known_types::Empty;

    fn into_proto(self) -> protobuf::well_known_types::Empty {
        protobuf::well_known_types::Empty::new()
    }
}

/// Dispatches the given `request` to an appropriate action handler.
///
/// This method is a mapping between action names (as specified in the protocol)
/// and action handlers (implemented on the agent).
///
/// # Errors
///
/// This function will return an error if the given action is unknown (or not
/// yet implemented).
///
/// It will also error out if the action execution itself fails for whatever
/// reason.
pub fn dispatch<'s, S>(session: &mut S, request: crate::message::Request) -> session::Result<()>
where
    S: Session,
{
    match request.action_name() {
        #[cfg(feature = "action-metadata")]
        "GetClientInfo" => handle(session, request, self::metadata::handle),

        #[cfg(feature = "action-listdir")]
        "ListDirectory" => handle(session, request, self::listdir::handle),

        #[cfg(feature = "action-timeline")]
        "Timeline" => handle(session, request, self::timeline::handle),

        #[cfg(feature = "action-network")]
        "ListNetworkConnections" => handle(session, request, self::network::handle),

        #[cfg(feature = "action-stat")]
        "GetFileStat" => handle(session, request, self::stat::handle),

        #[cfg(feature = "action-insttime")]
        "GetInstallDate" => handle(session, request, self::insttime::handle),

        #[cfg(feature = "action-interfaces")]
        #[cfg(target_family = "unix")]
        "EnumerateInterfaces" => handle(session, request, self::interfaces::handle),

        #[cfg(feature = "action-filesystems")]
        #[cfg(target_os = "linux")]
        "EnumerateFilesystems" => handle(session, request, self::filesystems::handle),

        #[cfg(feature = "action-memsize")]
        "GetMemorySize" => handle(session, request, self::memsize::handle),

        action => return Err(session::Error::Dispatch(String::from(action))),
    }
}

/// Handles a `request` using the specified `handler`.
///
/// This method will attempt to interpret request arguments for the specific
/// action and execute the handler with them.
///
/// # Errors
///
/// This function will return an error if the request arguments cannot be parsed
/// for the specific action or if the action execution fails.
fn handle<S, A, H>(session: &mut S, request: crate::message::Request, handler: H) -> session::Result<()>
where
    S: crate::session::Session,
    A: Args,
    H: FnOnce(&mut S, A) -> session::Result<()>,
{
    let args = request.parse_args()?;
    handler(session, args)
}

// TODO(panhania@): Remove all usages of the `Request` trait and replace it with
// `Args`.
pub trait Args: Sized {
    /// Low-level Protocol Buffers type representing the action arguments.
    type Proto: protobuf::Message + Default;

    /// Converts a low-level type to a structured request arguments.
    fn from_proto(proto: Self::Proto) -> Result<Self, ParseArgsError>;
}

impl Args for () {

    type Proto = protobuf::well_known_types::Empty;

    fn from_proto(_: protobuf::well_known_types::Empty) -> Result<(), ParseArgsError> {
        Ok(())
    }
}

pub trait Item: Sized {
    /// Low-level Protocol Buffers type representing the action results.
    type Proto: protobuf::Message + Default;

    /// A name of the corresponding RDF class in GRR.
    const RDF_NAME: &'static str;

    /// Converts an action result ot its low-level representation.
    fn into_proto(self) -> Self::Proto;
}

impl Item for () {

    // This implementation is intended to be used only in tests and we do not
    // really care about the `RDF_NAME` field there. And since GRR does not have
    // any RDF wrapper for empty type (except maybe `EmptyFlowArgs`, but this is
    // semantically different), we just leave it blank.
    const RDF_NAME: &'static str = "";

    type Proto = protobuf::well_known_types::Empty;

    fn into_proto(self) -> protobuf::well_known_types::Empty {
        protobuf::well_known_types::Empty::new()
    }
}

/// The error type for cases when action argument parsing fails.
#[derive(Debug)]
pub struct ParseArgsError {
    /// A corresponding [`ParseArgsErrorKind`] of this error.
    kind: ParseArgsErrorKind,
    /// A detailed payload associated with the error.
    error: Box<dyn std::error::Error + Send + Sync>,
}

impl ParseArgsError {

    /// Creates a new error instance caused by some invalid field error.
    fn invalid_field<E>(error: E) -> ParseArgsError
    where
        E: std::error::Error + Send + Sync + 'static,
    {
        ParseArgsError {
            kind: ParseArgsErrorKind::InvalidField,
            error: Box::new(error),
        }
    }
}

/// Kinds of errors that can happen when parsing action arguments.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum ParseArgsErrorKind {
    /// The serialized message with arguments was impossible to deserialize.
    InvalidProto,
    // TODO(panhania@): Augment with field name.
    /// One of the fields of the arguments struct is invalid.
    InvalidField,
}

impl ParseArgsErrorKind {

    fn as_str(&self) -> &'static str {
        use ParseArgsErrorKind::*;

        match *self {
            InvalidProto => "invalid serialized protobuf message",
            InvalidField => "invalid argument field",
        }
    }
}

impl std::fmt::Display for ParseArgsError {

    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(fmt, "{}: {}", self.kind.as_str(), self.error)
    }
}

impl std::error::Error for ParseArgsError {

    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        self.error.source()
    }
}

impl From<protobuf::ProtobufError> for ParseArgsError {

    fn from(error: protobuf::ProtobufError) -> Self {
        ParseArgsError {
            kind: ParseArgsErrorKind::InvalidProto,
            error: Box::new(error),
        }
    }
}

/// The error type for cases when action dispatch (or execution) fails.
#[derive(Debug)]
pub struct DispatchError {
    /// A corresponding [`DispatchErrorKind`] of this error.
    kind: DispatchErrorKind,
    /// A detailed error object.
    error: Option<Box<dyn std::error::Error + Send + Sync>>,
}

/// Kinds of errors that can happen when dispatching an action.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum DispatchErrorKind {
    /// The action handler is unknown or does not exist yet.
    UnknownAction(&'static str),
    /// The action arguments were invalid.
    InvalidArgs,
}

impl std::fmt::Display for DispatchError {

    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self.error {
            Some(ref error) => write!(fmt, "{}: {}", self.kind, error),
            None => write!(fmt, "{}", self.kind),
        }
    }
}

impl std::error::Error for DispatchError {

    fn cause(&self) -> Option<&dyn std::error::Error> {
        self.error.as_deref().map(|error| error as &_)
    }
}

impl std::fmt::Display for DispatchErrorKind {

    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        use DispatchErrorKind::*;

        match *self {
            UnknownAction(name) => write!(fmt, "unknown action '{}'", name),
            InvalidArgs => write!(fmt, "invalid action arguments"),
        }
    }
}

impl From<ParseArgsError> for DispatchError {

    fn from(error: ParseArgsError) -> DispatchError {
        DispatchError {
            kind: DispatchErrorKind::InvalidArgs,
            error: Some(Box::new(error)),
        }
    }
}
<|endoftext|>