<commit_before>// Copyright 2019 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

//! Crate for displaying simple surfaces and GPU buffers over wayland.

extern crate data_model;
extern crate sys_util;

#[path = "dwl.rs"]
mod dwl;

use dwl::*;

use crate::{DisplayT, GpuDisplayError, GpuDisplayFramebuffer};

use std::cell::Cell;
use std::collections::HashMap;
use std::ffi::{CStr, CString};
use std::os::unix::io::{AsRawFd, RawFd};
use std::path::Path;
use std::ptr::{null, null_mut};

use data_model::VolatileMemory;
use sys_util::{round_up_to_page_size, MemoryMapping, SharedMemory};

const BUFFER_COUNT: usize = 2;
const BYTES_PER_PIXEL: u32 = 4;

struct DwlContext(*mut dwl_context);
impl Drop for DwlContext {
    fn drop(&mut self) {
        if !self.0.is_null() {
            // Safe given that we checked the pointer for non-null and it should always be of the
            // correct type.
            unsafe {
                dwl_context_destroy(&mut self.0);
            }
        }
    }
}

struct DwlDmabuf(*mut dwl_dmabuf);
impl Drop for DwlDmabuf {
    fn drop(&mut self) {
        if !self.0.is_null() {
            // Safe given that we checked the pointer for non-null and it should always be of the
            // correct type.
            unsafe {
                dwl_dmabuf_destroy(&mut self.0);
            }
        }
    }
}

struct DwlSurface(*mut dwl_surface);
impl Drop for DwlSurface {
    fn drop(&mut self) {
        if !self.0.is_null() {
            // Safe given that we checked the pointer for non-null and it should always be of the
            // correct type.
            unsafe {
                dwl_surface_destroy(&mut self.0);
            }
        }
    }
}

struct Surface {
    surface: DwlSurface,
    row_size: u32,
    buffer_size: usize,
    buffer_index: Cell<usize>,
    buffer_mem: MemoryMapping,
}

impl Surface {
    fn surface(&self) -> *mut dwl_surface {
        self.surface.0
    }
}

/// A connection to the compositor and associated collection of state.
///
/// The user of `GpuDisplay` can use `AsRawFd` to poll on the compositor connection's file
/// descriptor. When the connection is readable, `dispatch_events` can be called to process it.
pub struct DisplayWl {
    ctx: DwlContext,
    dmabufs: HashMap<u32, DwlDmabuf>,
    dmabuf_next_id: u32,
    surfaces: HashMap<u32, Surface>,
    surface_next_id: u32,
}

impl DisplayWl {
    /// Opens a fresh connection to the compositor.
    pub fn new(wayland_path: Option<&Path>) -> Result<DisplayWl, GpuDisplayError> {
        // The dwl_context_new call should always be safe to call, and we check its result.
        let ctx = DwlContext(unsafe { dwl_context_new() });
        if ctx.0.is_null() {
            return Err(GpuDisplayError::Allocate);
        }

        // The dwl_context_setup call is always safe to call given that the supplied context is
        // valid. and we check its result.
        let cstr_path = match wayland_path.map(|p| p.as_os_str().to_str()) {
            Some(Some(s)) => match CString::new(s) {
                Ok(cstr) => Some(cstr),
                Err(_) => return Err(GpuDisplayError::InvalidPath),
            },
            Some(None) => return Err(GpuDisplayError::InvalidPath),
            None => None,
        };
        let setup_success =
            unsafe { dwl_context_setup(ctx.0, cstr_path.map(|s| s.as_ptr()).unwrap_or(null())) };
        if !setup_success {
            return Err(GpuDisplayError::Connect);
        }

        Ok(DisplayWl {
            ctx,
            dmabufs: Default::default(),
            dmabuf_next_id: 0,
            surfaces: Default::default(),
            surface_next_id: 0,
        })
    }

    fn ctx(&self) -> *mut dwl_context {
        self.ctx.0
    }

    fn get_surface(&self, surface_id: u32) -> Option<&Surface> {
        self.surfaces.get(&surface_id)
    }
}

impl DisplayT for DisplayWl {
    fn import_dmabuf(
        &mut self,
        fd: RawFd,
        offset: u32,
        stride: u32,
        modifiers: u64,
        width: u32,
        height: u32,
        fourcc: u32,
    ) -> Result<u32, GpuDisplayError> {
        // Safe given that the context pointer is valid. Any other invalid parameters would be
        // rejected by dwl_context_dmabuf_new safely. We check that the resulting dmabuf is valid
        // before filing it away.
        let dmabuf = DwlDmabuf(unsafe {
            dwl_context_dmabuf_new(
                self.ctx(),
                fd,
                offset,
                stride,
                modifiers,
                width,
                height,
                fourcc,
            )
        });
        if dmabuf.0.is_null() {
            return Err(GpuDisplayError::FailedImport);
        }

        let next_id = self.dmabuf_next_id;
        self.dmabufs.insert(next_id, dmabuf);
        self.dmabuf_next_id += 1;
        Ok(next_id)
    }

    fn release_import(&mut self, import_id: u32) {
        self.dmabufs.remove(&import_id);
    }

    fn dispatch_events(&mut self) {
        // Safe given that the context pointer is valid.
        unsafe {
            dwl_context_dispatch(self.ctx());
        }
    }

    fn create_surface(
        &mut self,
        parent_surface_id: Option<u32>,
        width: u32,
        height: u32,
    ) -> Result<u32, GpuDisplayError> {
        let parent_ptr = match parent_surface_id {
            Some(id) => match self.get_surface(id).map(|p| p.surface()) {
                Some(ptr) => ptr,
                None => return Err(GpuDisplayError::InvalidSurfaceId),
            },
            None => null_mut(),
        };
        let row_size = width * BYTES_PER_PIXEL;
        let fb_size = row_size * height;
        let buffer_size = round_up_to_page_size(fb_size as usize * BUFFER_COUNT);
        let mut buffer_shm = SharedMemory::new(Some(
            CStr::from_bytes_with_nul(b"GpuDisplaySurface\0").unwrap(),
        ))
        .map_err(GpuDisplayError::CreateShm)?;
        buffer_shm
            .set_size(buffer_size as u64)
            .map_err(GpuDisplayError::SetSize)?;
        let buffer_mem = MemoryMapping::from_fd(&buffer_shm, buffer_size).unwrap();

        // Safe because only a valid context, parent pointer (if not  None), and buffer FD are used.
        // The returned surface is checked for validity before being filed away.
        let surface = DwlSurface(unsafe {
            dwl_context_surface_new(
                self.ctx(),
                parent_ptr,
                buffer_shm.as_raw_fd(),
                buffer_size,
                fb_size as usize,
                width,
                height,
                row_size,
            )
        });

        if surface.0.is_null() {
            return Err(GpuDisplayError::CreateSurface);
        }

        let next_id = self.surface_next_id;
        self.surfaces.insert(
            next_id,
            Surface {
                surface,
                row_size,
                buffer_size: fb_size as usize,
                buffer_index: Cell::new(0),
                buffer_mem,
            },
        );

        self.surface_next_id += 1;
        Ok(next_id)
    }

    fn release_surface(&mut self, surface_id: u32) {
        self.surfaces.remove(&surface_id);
    }

    fn framebuffer(&mut self, surface_id: u32) -> Option<GpuDisplayFramebuffer> {
        let surface = self.get_surface(surface_id)?;
        let buffer_index = (surface.buffer_index.get() + 1) % BUFFER_COUNT;
        let framebuffer = surface
            .buffer_mem
            .get_slice(
                (buffer_index * surface.buffer_size) as u64,
                surface.buffer_size as u64,
            )
            .ok()?;
        Some(GpuDisplayFramebuffer::new(
            framebuffer,
            surface.row_size,
            BYTES_PER_PIXEL,
        ))
    }

    fn commit(&mut self, surface_id: u32) {
        match self.get_surface(surface_id) {
            Some(surface) => {
                // Safe because only a valid surface is used.
                unsafe {
                    dwl_surface_commit(surface.surface());
                }
            }
            None => debug_assert!(false, "invalid surface_id {}", surface_id),
        }
    }

    fn next_buffer_in_use(&self, surface_id: u32) -> bool {
        match self.get_surface(surface_id) {
            Some(surface) => {
                let next_buffer_index = (surface.buffer_index.get() + 1) % BUFFER_COUNT;
                // Safe because only a valid surface and buffer index is used.
                unsafe { dwl_surface_buffer_in_use(surface.surface(), next_buffer_index) }
            }
            None => {
                debug_assert!(false, "invalid surface_id {}", surface_id);
                false
            }
        }
    }

    fn flip(&mut self, surface_id: u32) {
        match self.get_surface(surface_id) {
            Some(surface) => {
                surface
                    .buffer_index
                    .set((surface.buffer_index.get() + 1) % BUFFER_COUNT);
                // Safe because only a valid surface and buffer index is used.
                unsafe {
                    dwl_surface_flip(surface.surface(), surface.buffer_index.get());
                }
            }
            None => debug_assert!(false, "invalid surface_id {}", surface_id),
        }
    }

    fn flip_to(&mut self, surface_id: u32, import_id: u32) {
        match self.get_surface(surface_id) {
            Some(surface) => {
                match self.dmabufs.get(&import_id) {
                    // Safe because only a valid surface and dmabuf is used.
                    Some(dmabuf) => unsafe { dwl_surface_flip_to(surface.surface(), dmabuf.0) },
                    None => debug_assert!(false, "invalid import_id {}", import_id),
                }
            }
            None => debug_assert!(false, "invalid surface_id {}", surface_id),
        }
    }

    fn close_requested(&self, surface_id: u32) -> bool {
        match self.get_surface(surface_id) {
            Some(surface) =>
            // Safe because only a valid surface is used.
            unsafe { dwl_surface_close_requested(surface.surface()) }
            None => false,
        }
    }

    fn set_position(&mut self, surface_id: u32, x: u32, y: u32) {
        match self.get_surface(surface_id) {
            Some(surface) => {
                // Safe because only a valid surface is used.
                unsafe {
                    dwl_surface_set_position(surface.surface(), x, y);
                }
            }
            None => debug_assert!(false, "invalid surface_id {}", surface_id),
        }
    }
}

impl AsRawFd for DisplayWl {
    fn as_raw_fd(&self) -> RawFd {
        // Safe given that the context pointer is valid.
        unsafe { dwl_context_fd(self.ctx.0) }
    }
}
<commit_msg>gpu_display: fix use after free for the wayland socket path<commit_after>// Copyright 2019 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

//! Crate for displaying simple surfaces and GPU buffers over wayland.

extern crate data_model;
extern crate sys_util;

#[path = "dwl.rs"]
mod dwl;

use dwl::*;

use crate::{DisplayT, GpuDisplayError, GpuDisplayFramebuffer};

use std::cell::Cell;
use std::collections::HashMap;
use std::ffi::{CStr, CString};
use std::os::unix::io::{AsRawFd, RawFd};
use std::path::Path;
use std::ptr::{null, null_mut};

use data_model::VolatileMemory;
use sys_util::{round_up_to_page_size, MemoryMapping, SharedMemory};

const BUFFER_COUNT: usize = 2;
const BYTES_PER_PIXEL: u32 = 4;

struct DwlContext(*mut dwl_context);
impl Drop for DwlContext {
    fn drop(&mut self) {
        if !self.0.is_null() {
            // Safe given that we checked the pointer for non-null and it should always be of the
            // correct type.
            unsafe {
                dwl_context_destroy(&mut self.0);
            }
        }
    }
}

struct DwlDmabuf(*mut dwl_dmabuf);
impl Drop for DwlDmabuf {
    fn drop(&mut self) {
        if !self.0.is_null() {
            // Safe given that we checked the pointer for non-null and it should always be of the
            // correct type.
            unsafe {
                dwl_dmabuf_destroy(&mut self.0);
            }
        }
    }
}

struct DwlSurface(*mut dwl_surface);
impl Drop for DwlSurface {
    fn drop(&mut self) {
        if !self.0.is_null() {
            // Safe given that we checked the pointer for non-null and it should always be of the
            // correct type.
            unsafe {
                dwl_surface_destroy(&mut self.0);
            }
        }
    }
}

struct Surface {
    surface: DwlSurface,
    row_size: u32,
    buffer_size: usize,
    buffer_index: Cell<usize>,
    buffer_mem: MemoryMapping,
}

impl Surface {
    fn surface(&self) -> *mut dwl_surface {
        self.surface.0
    }
}

/// A connection to the compositor and associated collection of state.
///
/// The user of `GpuDisplay` can use `AsRawFd` to poll on the compositor connection's file
/// descriptor. When the connection is readable, `dispatch_events` can be called to process it.
pub struct DisplayWl {
    ctx: DwlContext,
    dmabufs: HashMap<u32, DwlDmabuf>,
    dmabuf_next_id: u32,
    surfaces: HashMap<u32, Surface>,
    surface_next_id: u32,
}

impl DisplayWl {
    /// Opens a fresh connection to the compositor.
    pub fn new(wayland_path: Option<&Path>) -> Result<DisplayWl, GpuDisplayError> {
        // The dwl_context_new call should always be safe to call, and we check its result.
        let ctx = DwlContext(unsafe { dwl_context_new() });
        if ctx.0.is_null() {
            return Err(GpuDisplayError::Allocate);
        }

        // The dwl_context_setup call is always safe to call given that the supplied context is
        // valid. and we check its result.
        let cstr_path = match wayland_path.map(|p| p.as_os_str().to_str()) {
            Some(Some(s)) => match CString::new(s) {
                Ok(cstr) => Some(cstr),
                Err(_) => return Err(GpuDisplayError::InvalidPath),
            },
            Some(None) => return Err(GpuDisplayError::InvalidPath),
            None => None,
        };
        // This grabs a pointer to cstr_path without moving the CString into the .map closure
        // accidentally, which triggeres a really hard to catch use after free in
        // dwl_context_setup.
        let cstr_path_ptr = cstr_path
            .as_ref()
            .map(|s: &CString| CStr::as_ptr(s))
            .unwrap_or(null());
        let setup_success = unsafe { dwl_context_setup(ctx.0, cstr_path_ptr) };
        if !setup_success {
            return Err(GpuDisplayError::Connect);
        }

        Ok(DisplayWl {
            ctx,
            dmabufs: Default::default(),
            dmabuf_next_id: 0,
            surfaces: Default::default(),
            surface_next_id: 0,
        })
    }

    fn ctx(&self) -> *mut dwl_context {
        self.ctx.0
    }

    fn get_surface(&self, surface_id: u32) -> Option<&Surface> {
        self.surfaces.get(&surface_id)
    }
}

impl DisplayT for DisplayWl {
    fn import_dmabuf(
        &mut self,
        fd: RawFd,
        offset: u32,
        stride: u32,
        modifiers: u64,
        width: u32,
        height: u32,
        fourcc: u32,
    ) -> Result<u32, GpuDisplayError> {
        // Safe given that the context pointer is valid. Any other invalid parameters would be
        // rejected by dwl_context_dmabuf_new safely. We check that the resulting dmabuf is valid
        // before filing it away.
        let dmabuf = DwlDmabuf(unsafe {
            dwl_context_dmabuf_new(
                self.ctx(),
                fd,
                offset,
                stride,
                modifiers,
                width,
                height,
                fourcc,
            )
        });
        if dmabuf.0.is_null() {
            return Err(GpuDisplayError::FailedImport);
        }

        let next_id = self.dmabuf_next_id;
        self.dmabufs.insert(next_id, dmabuf);
        self.dmabuf_next_id += 1;
        Ok(next_id)
    }

    fn release_import(&mut self, import_id: u32) {
        self.dmabufs.remove(&import_id);
    }

    fn dispatch_events(&mut self) {
        // Safe given that the context pointer is valid.
        unsafe {
            dwl_context_dispatch(self.ctx());
        }
    }

    fn create_surface(
        &mut self,
        parent_surface_id: Option<u32>,
        width: u32,
        height: u32,
    ) -> Result<u32, GpuDisplayError> {
        let parent_ptr = match parent_surface_id {
            Some(id) => match self.get_surface(id).map(|p| p.surface()) {
                Some(ptr) => ptr,
                None => return Err(GpuDisplayError::InvalidSurfaceId),
            },
            None => null_mut(),
        };
        let row_size = width * BYTES_PER_PIXEL;
        let fb_size = row_size * height;
        let buffer_size = round_up_to_page_size(fb_size as usize * BUFFER_COUNT);
        let mut buffer_shm = SharedMemory::new(Some(
            CStr::from_bytes_with_nul(b"GpuDisplaySurface\0").unwrap(),
        ))
        .map_err(GpuDisplayError::CreateShm)?;
        buffer_shm
            .set_size(buffer_size as u64)
            .map_err(GpuDisplayError::SetSize)?;
        let buffer_mem = MemoryMapping::from_fd(&buffer_shm, buffer_size).unwrap();

        // Safe because only a valid context, parent pointer (if not  None), and buffer FD are used.
        // The returned surface is checked for validity before being filed away.
        let surface = DwlSurface(unsafe {
            dwl_context_surface_new(
                self.ctx(),
                parent_ptr,
                buffer_shm.as_raw_fd(),
                buffer_size,
                fb_size as usize,
                width,
                height,
                row_size,
            )
        });

        if surface.0.is_null() {
            return Err(GpuDisplayError::CreateSurface);
        }

        let next_id = self.surface_next_id;
        self.surfaces.insert(
            next_id,
            Surface {
                surface,
                row_size,
                buffer_size: fb_size as usize,
                buffer_index: Cell::new(0),
                buffer_mem,
            },
        );

        self.surface_next_id += 1;
        Ok(next_id)
    }

    fn release_surface(&mut self, surface_id: u32) {
        self.surfaces.remove(&surface_id);
    }

    fn framebuffer(&mut self, surface_id: u32) -> Option<GpuDisplayFramebuffer> {
        let surface = self.get_surface(surface_id)?;
        let buffer_index = (surface.buffer_index.get() + 1) % BUFFER_COUNT;
        let framebuffer = surface
            .buffer_mem
            .get_slice(
                (buffer_index * surface.buffer_size) as u64,
                surface.buffer_size as u64,
            )
            .ok()?;
        Some(GpuDisplayFramebuffer::new(
            framebuffer,
            surface.row_size,
            BYTES_PER_PIXEL,
        ))
    }

    fn commit(&mut self, surface_id: u32) {
        match self.get_surface(surface_id) {
            Some(surface) => {
                // Safe because only a valid surface is used.
                unsafe {
                    dwl_surface_commit(surface.surface());
                }
            }
            None => debug_assert!(false, "invalid surface_id {}", surface_id),
        }
    }

    fn next_buffer_in_use(&self, surface_id: u32) -> bool {
        match self.get_surface(surface_id) {
            Some(surface) => {
                let next_buffer_index = (surface.buffer_index.get() + 1) % BUFFER_COUNT;
                // Safe because only a valid surface and buffer index is used.
                unsafe { dwl_surface_buffer_in_use(surface.surface(), next_buffer_index) }
            }
            None => {
                debug_assert!(false, "invalid surface_id {}", surface_id);
                false
            }
        }
    }

    fn flip(&mut self, surface_id: u32) {
        match self.get_surface(surface_id) {
            Some(surface) => {
                surface
                    .buffer_index
                    .set((surface.buffer_index.get() + 1) % BUFFER_COUNT);
                // Safe because only a valid surface and buffer index is used.
                unsafe {
                    dwl_surface_flip(surface.surface(), surface.buffer_index.get());
                }
            }
            None => debug_assert!(false, "invalid surface_id {}", surface_id),
        }
    }

    fn flip_to(&mut self, surface_id: u32, import_id: u32) {
        match self.get_surface(surface_id) {
            Some(surface) => {
                match self.dmabufs.get(&import_id) {
                    // Safe because only a valid surface and dmabuf is used.
                    Some(dmabuf) => unsafe { dwl_surface_flip_to(surface.surface(), dmabuf.0) },
                    None => debug_assert!(false, "invalid import_id {}", import_id),
                }
            }
            None => debug_assert!(false, "invalid surface_id {}", surface_id),
        }
    }

    fn close_requested(&self, surface_id: u32) -> bool {
        match self.get_surface(surface_id) {
            Some(surface) =>
            // Safe because only a valid surface is used.
            unsafe { dwl_surface_close_requested(surface.surface()) }
            None => false,
        }
    }

    fn set_position(&mut self, surface_id: u32, x: u32, y: u32) {
        match self.get_surface(surface_id) {
            Some(surface) => {
                // Safe because only a valid surface is used.
                unsafe {
                    dwl_surface_set_position(surface.surface(), x, y);
                }
            }
            None => debug_assert!(false, "invalid surface_id {}", surface_id),
        }
    }
}

impl AsRawFd for DisplayWl {
    fn as_raw_fd(&self) -> RawFd {
        // Safe given that the context pointer is valid.
        unsafe { dwl_context_fd(self.ctx.0) }
    }
}
<|endoftext|>


<commit_before>use anyhow::{Result};
use std::io;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use std::thread;
use tree_sitter::Parser;

#[cfg(unix)]
use std::path::PathBuf;
#[cfg(unix)]
use std::process::{Child, ChildStdin, Command, Stdio};

#[cfg(unix)]
const HTML_HEADER: &[u8] = b"<!DOCTYPE html>\n<style>svg { width: 100%; }</style>\n\n";

pub fn cancel_on_stdin() -> Arc<AtomicUsize> {
    let result = Arc::new(AtomicUsize::new(0));
    if atty::is(atty::Stream::Stdin) {
        thread::spawn({
            let flag = result.clone();
            move || {
                let mut line = String::new();
                io::stdin().read_line(&mut line).unwrap();
                flag.store(1, Ordering::Relaxed);
            }
        });
    }
    result
}
#[cfg(windows)]
pub struct LogSession();

#[cfg(unix)]
pub struct LogSession(PathBuf, Option<Child>, Option<ChildStdin>);

#[cfg(windows)]
pub fn log_graphs(_parser: &mut Parser, _path: &str) -> Result<LogSession> {
    Ok(LogSession())
}

#[cfg(unix)]
pub fn log_graphs(parser: &mut Parser, path: &str) -> Result<LogSession> {
    use std::io::Write;

    let mut dot_file = std::fs::File::create(path)?;
    dot_file.write(HTML_HEADER)?;
    let mut dot_process = Command::new("dot")
        .arg("-Tsvg")
        .stdin(Stdio::piped())
        .stdout(dot_file)
        .spawn()
        .with_context(|| "Failed to run the `dot` command. Check that graphviz is installed.")?;
    let dot_stdin = dot_process
        .stdin
        .take()
        .ok_or_else(|| anyhow!("Failed to open stdin for `dot` process."))?;
    parser.print_dot_graphs(&dot_stdin);
    Ok(LogSession(
        PathBuf::from(path),
        Some(dot_process),
        Some(dot_stdin),
    ))
}

#[cfg(unix)]
impl Drop for LogSession {
    fn drop(&mut self) {
        use std::fs;

        drop(self.2.take().unwrap());
        let output = self.1.take().unwrap().wait_with_output().unwrap();
        if output.status.success() {
            if cfg!(target_os = "macos")
                && fs::metadata(&self.0).unwrap().len() > HTML_HEADER.len() as u64
            {
                Command::new("open").arg(&self.0).output().unwrap();
            }
        } else {
            eprintln!(
                "Dot failed: {} {}",
                String::from_utf8_lossy(&output.stdout),
                String::from_utf8_lossy(&output.stderr)
            );
        }
    }
}
<commit_msg>Fixed cargo fmt<commit_after>use anyhow::Result;
use std::io;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use std::thread;
use tree_sitter::Parser;

#[cfg(unix)]
use std::path::PathBuf;
#[cfg(unix)]
use std::process::{Child, ChildStdin, Command, Stdio};

#[cfg(unix)]
const HTML_HEADER: &[u8] = b"<!DOCTYPE html>\n<style>svg { width: 100%; }</style>\n\n";

pub fn cancel_on_stdin() -> Arc<AtomicUsize> {
    let result = Arc::new(AtomicUsize::new(0));
    if atty::is(atty::Stream::Stdin) {
        thread::spawn({
            let flag = result.clone();
            move || {
                let mut line = String::new();
                io::stdin().read_line(&mut line).unwrap();
                flag.store(1, Ordering::Relaxed);
            }
        });
    }
    result
}
#[cfg(windows)]
pub struct LogSession();

#[cfg(unix)]
pub struct LogSession(PathBuf, Option<Child>, Option<ChildStdin>);

#[cfg(windows)]
pub fn log_graphs(_parser: &mut Parser, _path: &str) -> Result<LogSession> {
    Ok(LogSession())
}

#[cfg(unix)]
pub fn log_graphs(parser: &mut Parser, path: &str) -> Result<LogSession> {
    use std::io::Write;

    let mut dot_file = std::fs::File::create(path)?;
    dot_file.write(HTML_HEADER)?;
    let mut dot_process = Command::new("dot")
        .arg("-Tsvg")
        .stdin(Stdio::piped())
        .stdout(dot_file)
        .spawn()
        .with_context(|| "Failed to run the `dot` command. Check that graphviz is installed.")?;
    let dot_stdin = dot_process
        .stdin
        .take()
        .ok_or_else(|| anyhow!("Failed to open stdin for `dot` process."))?;
    parser.print_dot_graphs(&dot_stdin);
    Ok(LogSession(
        PathBuf::from(path),
        Some(dot_process),
        Some(dot_stdin),
    ))
}

#[cfg(unix)]
impl Drop for LogSession {
    fn drop(&mut self) {
        use std::fs;

        drop(self.2.take().unwrap());
        let output = self.1.take().unwrap().wait_with_output().unwrap();
        if output.status.success() {
            if cfg!(target_os = "macos")
                && fs::metadata(&self.0).unwrap().len() > HTML_HEADER.len() as u64
            {
                Command::new("open").arg(&self.0).output().unwrap();
            }
        } else {
            eprintln!(
                "Dot failed: {} {}",
                String::from_utf8_lossy(&output.stdout),
                String::from_utf8_lossy(&output.stderr)
            );
        }
    }
}
<|endoftext|>


<commit_before>use crate::assist_context::{AssistContext, Assists};
use hir::HirDisplay;
use ide_db::{
    assists::{AssistId, AssistKind},
    defs::NameRefClass,
};
use syntax::{
    ast::{self, edit::IndentLevel},
    AstNode,
};

// Assist: generate_constant
//
// Generate a named constant.
//
// ```
// struct S { i: usize }
// impl S { pub fn new(n: usize) {} }
// fn main() {
//     let v = S::new(CAPA$0CITY);
// }
// ```
// ->
// ```
// struct S { i: usize }
// impl S { pub fn new(n: usize) {} }
// fn main() {
//     const CAPACITY: usize = $0;
//     let v = S::new(CAPACITY);
// }
// ```

pub(crate) fn generate_constant(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
    let constant_token = ctx.find_node_at_offset::<ast::NameRef>()?;
    let expr = constant_token.syntax().ancestors().find_map(ast::Expr::cast)?;
    let statement = expr.syntax().ancestors().find_map(ast::Stmt::cast)?;
    let ty = ctx.sema.type_of_expr(&expr)?;
    let scope = ctx.sema.scope(statement.syntax())?;
    let module = scope.module();
    let type_name = ty.original().display_source_code(ctx.db(), module.into()).ok()?;
    let indent = IndentLevel::from_node(statement.syntax());
    if constant_token.to_string().chars().any(|it| !(it.is_uppercase() || it == '_')) {
        cov_mark::hit!(not_constant_name);
        return None;
    }
    if NameRefClass::classify(&ctx.sema, &constant_token).is_some() {
        cov_mark::hit!(already_defined);
        return None;
    }
    let target = statement.syntax().parent()?.text_range();
    acc.add(
        AssistId("generate_constant", AssistKind::QuickFix),
        "Generate constant",
        target,
        |builder| {
            builder.insert(
                statement.syntax().text_range().start(),
                format!("const {}: {} = $0;\n{}", constant_token, type_name, indent),
            );
        },
    )
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::tests::{check_assist, check_assist_not_applicable};

    #[test]
    fn test_trivial() {
        check_assist(
            generate_constant,
            r#"struct S { i: usize }
impl S {
    pub fn new(n: usize) {}
}
fn main() {
    let v = S::new(CAPA$0CITY);
}"#,
            r#"struct S { i: usize }
impl S {
    pub fn new(n: usize) {}
}
fn main() {
    const CAPACITY: usize = $0;
    let v = S::new(CAPACITY);
}"#,
        );
    }
    #[test]
    fn test_wont_apply_when_defined() {
        cov_mark::check!(already_defined);
        check_assist_not_applicable(
            generate_constant,
            r#"struct S { i: usize }
impl S {
    pub fn new(n: usize) {}
}
fn main() {
    const CAPACITY: usize = 10;
    let v = S::new(CAPAC$0ITY);
}"#,
        );
    }
    #[test]
    fn test_wont_apply_when_maybe_not_constant() {
        cov_mark::check!(not_constant_name);
        check_assist_not_applicable(
            generate_constant,
            r#"struct S { i: usize }
impl S {
    pub fn new(n: usize) {}
}
fn main() {
    let v = S::new(capa$0city);
}"#,
        );
    }
}
<commit_msg>Auto merge of #12286 - harpsword:fix_generate_constant, r=Veykril<commit_after>use crate::assist_context::{AssistContext, Assists};
use hir::{HasVisibility, HirDisplay, Module};
use ide_db::{
    assists::{AssistId, AssistKind},
    base_db::{FileId, Upcast},
    defs::{Definition, NameRefClass},
};
use syntax::{
    ast::{self, edit::IndentLevel, NameRef},
    AstNode, Direction, SyntaxKind, TextSize,
};

// Assist: generate_constant
//
// Generate a named constant.
//
// ```
// struct S { i: usize }
// impl S { pub fn new(n: usize) {} }
// fn main() {
//     let v = S::new(CAPA$0CITY);
// }
// ```
// ->
// ```
// struct S { i: usize }
// impl S { pub fn new(n: usize) {} }
// fn main() {
//     const CAPACITY: usize = $0;
//     let v = S::new(CAPACITY);
// }
// ```

pub(crate) fn generate_constant(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
    let constant_token = ctx.find_node_at_offset::<ast::NameRef>()?;
    if constant_token.to_string().chars().any(|it| !(it.is_uppercase() || it == '_')) {
        cov_mark::hit!(not_constant_name);
        return None;
    }
    if NameRefClass::classify(&ctx.sema, &constant_token).is_some() {
        cov_mark::hit!(already_defined);
        return None;
    }
    let expr = constant_token.syntax().ancestors().find_map(ast::Expr::cast)?;
    let statement = expr.syntax().ancestors().find_map(ast::Stmt::cast)?;
    let ty = ctx.sema.type_of_expr(&expr)?;
    let scope = ctx.sema.scope(statement.syntax())?;
    let constant_module = scope.module();
    let type_name = ty.original().display_source_code(ctx.db(), constant_module.into()).ok()?;
    let target = statement.syntax().parent()?.text_range();
    let path = constant_token.syntax().ancestors().find_map(ast::Path::cast)?;

    let name_refs = path.segments().map(|s| s.name_ref());
    let mut outer_exists = false;
    let mut not_exist_name_ref = Vec::new();
    let mut current_module = constant_module;
    for name_ref in name_refs {
        let name_ref_value = name_ref?;
        let name_ref_class = NameRefClass::classify(&ctx.sema, &name_ref_value);
        match name_ref_class {
            Some(NameRefClass::Definition(Definition::Module(m))) => {
                if !m.visibility(ctx.sema.db).is_visible_from(ctx.sema.db, constant_module.into()) {
                    return None;
                }
                outer_exists = true;
                current_module = m;
            }
            Some(_) => {
                return None;
            }
            None => {
                not_exist_name_ref.push(name_ref_value);
            }
        }
    }
    let (offset, indent, file_id, post_string) =
        target_data_for_generate_constant(ctx, current_module, constant_module).unwrap_or_else(
            || {
                let indent = IndentLevel::from_node(statement.syntax());
                (statement.syntax().text_range().start(), indent, None, format!("\n{}", indent))
            },
        );

    let text = get_text_for_generate_constant(not_exist_name_ref, indent, outer_exists, type_name)?;
    acc.add(
        AssistId("generate_constant", AssistKind::QuickFix),
        "Generate constant",
        target,
        |builder| {
            if let Some(file_id) = file_id {
                builder.edit_file(file_id);
            }
            builder.insert(offset, format!("{}{}", text, post_string));
        },
    )
}

fn get_text_for_generate_constant(
    mut not_exist_name_ref: Vec<NameRef>,
    indent: IndentLevel,
    outer_exists: bool,
    type_name: String,
) -> Option<String> {
    let constant_token = not_exist_name_ref.pop()?;
    let vis = if not_exist_name_ref.len() == 0 && !outer_exists { "" } else { "\npub " };
    let mut text = format!("{}const {}: {} = $0;", vis, constant_token, type_name);
    while let Some(name_ref) = not_exist_name_ref.pop() {
        let vis = if not_exist_name_ref.len() == 0 && !outer_exists { "" } else { "\npub " };
        text = text.replace("\n", "\n    ");
        text = format!("{}mod {} {{{}\n}}", vis, name_ref.to_string(), text);
    }
    Some(text.replace("\n", &format!("\n{}", indent)))
}

fn target_data_for_generate_constant(
    ctx: &AssistContext,
    current_module: Module,
    constant_module: Module,
) -> Option<(TextSize, IndentLevel, Option<FileId>, String)> {
    if current_module == constant_module {
        // insert in current file
        return None;
    }
    let in_file_source = current_module.definition_source(ctx.sema.db);
    let file_id = in_file_source.file_id.original_file(ctx.sema.db.upcast());
    match in_file_source.value {
        hir::ModuleSource::Module(module_node) => {
            let indent = IndentLevel::from_node(module_node.syntax());
            let l_curly_token = module_node.item_list()?.l_curly_token()?;
            let offset = l_curly_token.text_range().end();

            let siblings_has_newline = l_curly_token
                .siblings_with_tokens(Direction::Next)
                .find(|it| it.kind() == SyntaxKind::WHITESPACE && it.to_string().contains("\n"))
                .is_some();
            let post_string =
                if siblings_has_newline { format!("{}", indent) } else { format!("\n{}", indent) };
            Some((offset, indent + 1, Some(file_id), post_string))
        }
        _ => Some((TextSize::from(0), 0.into(), Some(file_id), "\n".into())),
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::tests::{check_assist, check_assist_not_applicable};

    #[test]
    fn test_trivial() {
        check_assist(
            generate_constant,
            r#"struct S { i: usize }
impl S {
    pub fn new(n: usize) {}
}
fn main() {
    let v = S::new(CAPA$0CITY);
}"#,
            r#"struct S { i: usize }
impl S {
    pub fn new(n: usize) {}
}
fn main() {
    const CAPACITY: usize = $0;
    let v = S::new(CAPACITY);
}"#,
        );
    }
    #[test]
    fn test_wont_apply_when_defined() {
        cov_mark::check!(already_defined);
        check_assist_not_applicable(
            generate_constant,
            r#"struct S { i: usize }
impl S {
    pub fn new(n: usize) {}
}
fn main() {
    const CAPACITY: usize = 10;
    let v = S::new(CAPAC$0ITY);
}"#,
        );
    }
    #[test]
    fn test_wont_apply_when_maybe_not_constant() {
        cov_mark::check!(not_constant_name);
        check_assist_not_applicable(
            generate_constant,
            r#"struct S { i: usize }
impl S {
    pub fn new(n: usize) {}
}
fn main() {
    let v = S::new(capa$0city);
}"#,
        );
    }

    #[test]
    fn test_constant_with_path() {
        check_assist(
            generate_constant,
            r#"mod foo {}
fn bar() -> i32 {
    foo::A_CON$0STANT
}"#,
            r#"mod foo {
    pub const A_CONSTANT: i32 = $0;
}
fn bar() -> i32 {
    foo::A_CONSTANT
}"#,
        );
    }

    #[test]
    fn test_constant_with_longer_path() {
        check_assist(
            generate_constant,
            r#"mod foo {
    pub mod goo {}
}
fn bar() -> i32 {
    foo::goo::A_CON$0STANT
}"#,
            r#"mod foo {
    pub mod goo {
        pub const A_CONSTANT: i32 = $0;
    }
}
fn bar() -> i32 {
    foo::goo::A_CONSTANT
}"#,
        );
    }

    #[test]
    fn test_constant_with_not_exist_longer_path() {
        check_assist(
            generate_constant,
            r#"fn bar() -> i32 {
    foo::goo::A_CON$0STANT
}"#,
            r#"mod foo {
    pub mod goo {
        pub const A_CONSTANT: i32 = $0;
    }
}
fn bar() -> i32 {
    foo::goo::A_CONSTANT
}"#,
        );
    }
}
<|endoftext|>


<commit_before>#![allow(unused_variables,unused_must_use)]
extern crate sozu_lib as sozu;
#[macro_use] extern crate sozu_command_lib as sozu_command;
extern crate time;
extern crate tiny_http;
extern crate ureq;

use std::thread;
use sozu_command::proxy;
use sozu_command::channel::Channel;
use sozu_command::proxy::LoadBalancingParams;
use sozu_command::logging::{Logger,LoggerBackend};
use tiny_http::{Server, Response};
use std::{
    io::{stdout, Read, Write},
    net::{TcpListener,TcpStream,ToSocketAddrs},
    time::Duration,
    str
};

#[test]
fn test() {
    Logger::init("EXAMPLE".to_string(), "debug", LoggerBackend::Stdout(stdout()), None);

    info!("starting up");

    let config = proxy::HttpListener {
        front: "127.0.0.1:8080".parse().expect("could not parse address"),
        ..Default::default()
    };

    let (mut command, channel) = Channel::generate(1000, 10000).expect("should create a channel");

    let jg = thread::spawn(move || {
        Logger::init("SOZU".to_string(), "info", LoggerBackend::Stdout(stdout()), None);
        let max_buffers = 20;
        let buffer_size = 16384;
        sozu::http::start(config, channel, max_buffers, buffer_size);
    });

    command.write_message(&proxy::ProxyRequest {
        id:    String::from("ID_Status"),
        order: proxy::ProxyRequestData::Status
    });

    // wait for sozu to start and answer
    info!("Status -> {:?}", command.read_message());

    let agent = ureq::AgentBuilder::new()
        .resolver(|addr: &str| match addr {
            "example.com:8080" => Ok(vec![([127,0,0,1], 8080).into()]),
            addr => addr.to_socket_addrs().map(Iterator::collect),
        })
    .build();

    info!("expecting 404");
    match agent
        .get("http://example.com:8080/")
        .call().unwrap_err() {
            ureq::Error::Status(404, res) => {
                assert_eq!(res.header("connection"), Some("close"));
            },
            e => panic!("invalid response: {:?}", e),
        };

    let http_front = proxy::HttpFront {
        app_id:     String::from("test"),
        address:    "127.0.0.1:8080".parse().unwrap(),
        hostname:   String::from("example.com"),
        path_begin: String::from("/"),
    };

    command.write_message(&proxy::ProxyRequest {
        id:    String::from("ID_ABCD"),
        order: proxy::ProxyRequestData::AddHttpFront(http_front)
    });
    println!("HTTP -> {:?}", command.read_message());

    info!("expecting 503");
    match agent
        .get("http://example.com:8080/")
        .call().unwrap_err() {
            ureq::Error::Status(503, res) => {
                assert_eq!(res.header("connection"), Some("close"));
            },
            e => panic!("invalid response: {:?}", e),
        };

    let http_backend = proxy::Backend {
        app_id:                    String::from("test"),
        backend_id:                String::from("test-0"),
        address:                   "127.0.0.1:2048".parse().unwrap(),
        load_balancing_parameters: Some(LoadBalancingParams::default()),
        sticky_id:                 None,
        backup:                    None,
    };

    command.write_message(&proxy::ProxyRequest {
        id:    String::from("ID_EFGH"),
        order: proxy::ProxyRequestData::AddBackend(http_backend)
    });

    println!("HTTP -> {:?}", command.read_message());

    info!("sending invalid request, expecting 400");
    let mut client = TcpStream::connect(("127.0.0.1", 8080)).expect("could not parse address");
    // 1 seconds of timeout
    client.set_read_timeout(Some(Duration::new(1,0)));

    let w = client.write(&b"HELLO\r\n\r\n"[..]);
    println!("http client write: {:?}", w);

    let expected_answer = "HTTP/1.1 400 Bad Request\r\nCache-Control: no-cache\r\nConnection: close\r\n\r\n";
    let mut buffer = [0;4096];
    let mut index = 0;

    let r = client.read(&mut buffer[..]);
    println!("http client read: {:?}", r);
    match r {
        Err(e)      => assert!(false, "client request should not fail. Error: {:?}",e),
        Ok(sz) => {
            index += sz;
            let answer = str::from_utf8(&buffer[..index]).expect("could not make string from buffer");
            println!("Response: {}", answer);
        }
    }

    let answer = str::from_utf8(&buffer[..index]).expect("could not make string from buffer");
    println!("Response: {}", answer);
    assert_eq!(answer, expected_answer);

    let _ = thread::spawn(move || {
        let listener = TcpListener::bind("127.0.0.1:2048").expect("could not parse address");
        let mut stream = listener.incoming().next().unwrap().unwrap();
        let response = b"TEST\r\n\r\n";
        stream.write(&response[..]).unwrap();
    });

    info!("expecting 502");
    match agent
        .get("http://example.com:8080/")
        .call().unwrap_err() {
            ureq::Error::Status(502, res) => {
                assert_eq!(res.status_text(), "Bad Gateway");
            },
            e => panic!("invalid response: {:?}", e),
        };

    start_server(2048);

    info!("expecting 200");
    let res = agent
        .get("http://example.com:8080/")
        .call().unwrap();
    assert_eq!(res.status(), 200);

    //let _ = jg.join();
    info!("good bye");
}

fn start_server(port: u16) {
    thread::spawn(move|| {
        let server = Server::http(&format!("127.0.0.1:{}", port)).expect("could not create server");
        info!("starting web server in port {}", port);

        for request in server.incoming_requests() {
            println!("backend web server got request -> method: {:?}, url: {:?}, headers: {:?}",
                     request.method(),
                     request.url(),
                     request.headers()
                    );

            let response = Response::from_string("hello world");
            request.respond(response).unwrap();
            println!("backend web server sent response");
            println!("server session stopped");
        }

        println!("server on port {} closed", port);
    });
}
<commit_msg>some synchro in tests<commit_after>#![allow(unused_variables,unused_must_use)]
extern crate sozu_lib as sozu;
#[macro_use] extern crate sozu_command_lib as sozu_command;
extern crate time;
extern crate tiny_http;
extern crate ureq;

use std::thread;
use sozu_command::proxy;
use sozu_command::channel::Channel;
use sozu_command::proxy::LoadBalancingParams;
use sozu_command::logging::{Logger,LoggerBackend};
use tiny_http::{Server, Response};
use std::{
    io::{stdout, Read, Write},
    net::{TcpListener,TcpStream,ToSocketAddrs},
    time::Duration,
    sync::{Arc, Barrier},
    str
};

#[test]
fn test() {
    Logger::init("EXAMPLE".to_string(), "debug", LoggerBackend::Stdout(stdout()), None);

    info!("starting up");

    let config = proxy::HttpListener {
        front: "127.0.0.1:8080".parse().expect("could not parse address"),
        ..Default::default()
    };

    let (mut command, channel) = Channel::generate(1000, 10000).expect("should create a channel");

    let jg = thread::spawn(move || {
        Logger::init("SOZU".to_string(), "info", LoggerBackend::Stdout(stdout()), None);
        let max_buffers = 20;
        let buffer_size = 16384;
        sozu::http::start(config, channel, max_buffers, buffer_size);
    });

    command.write_message(&proxy::ProxyRequest {
        id:    String::from("ID_Status"),
        order: proxy::ProxyRequestData::Status
    });

    // wait for sozu to start and answer
    info!("Status -> {:?}", command.read_message());

    let agent = ureq::AgentBuilder::new()
        .resolver(|addr: &str| match addr {
            "example.com:8080" => Ok(vec![([127,0,0,1], 8080).into()]),
            addr => addr.to_socket_addrs().map(Iterator::collect),
        })
    .build();

    info!("expecting 404");
    match agent
        .get("http://example.com:8080/")
        .call().unwrap_err() {
            ureq::Error::Status(404, res) => {
                assert_eq!(res.header("connection"), Some("close"));
            },
            e => panic!("invalid response: {:?}", e),
        };

    let http_front = proxy::HttpFront {
        app_id:     String::from("test"),
        address:    "127.0.0.1:8080".parse().unwrap(),
        hostname:   String::from("example.com"),
        path_begin: String::from("/"),
    };

    command.write_message(&proxy::ProxyRequest {
        id:    String::from("ID_ABCD"),
        order: proxy::ProxyRequestData::AddHttpFront(http_front)
    });
    println!("HTTP -> {:?}", command.read_message());

    info!("expecting 503");
    match agent
        .get("http://example.com:8080/")
        .call().unwrap_err() {
            ureq::Error::Status(503, res) => {
                assert_eq!(res.header("connection"), Some("close"));
            },
            e => panic!("invalid response: {:?}", e),
        };

    let http_backend = proxy::Backend {
        app_id:                    String::from("test"),
        backend_id:                String::from("test-0"),
        address:                   "127.0.0.1:2048".parse().unwrap(),
        load_balancing_parameters: Some(LoadBalancingParams::default()),
        sticky_id:                 None,
        backup:                    None,
    };

    command.write_message(&proxy::ProxyRequest {
        id:    String::from("ID_EFGH"),
        order: proxy::ProxyRequestData::AddBackend(http_backend)
    });

    println!("HTTP -> {:?}", command.read_message());

    info!("sending invalid request, expecting 400");
    let mut client = TcpStream::connect(("127.0.0.1", 8080)).expect("could not parse address");
    // 1 seconds of timeout
    client.set_read_timeout(Some(Duration::new(1,0)));

    let w = client.write(&b"HELLO\r\n\r\n"[..]);
    println!("http client write: {:?}", w);

    let expected_answer = "HTTP/1.1 400 Bad Request\r\nCache-Control: no-cache\r\nConnection: close\r\n\r\n";
    let mut buffer = [0;4096];
    let mut index = 0;

    let r = client.read(&mut buffer[..]);
    println!("http client read: {:?}", r);
    match r {
        Err(e)      => assert!(false, "client request should not fail. Error: {:?}",e),
        Ok(sz) => {
            index += sz;
            let answer = str::from_utf8(&buffer[..index]).expect("could not make string from buffer");
            println!("Response: {}", answer);
        }
    }

    let answer = str::from_utf8(&buffer[..index]).expect("could not make string from buffer");
    println!("Response: {}", answer);
    assert_eq!(answer, expected_answer);

    let barrier = Arc::new(Barrier::new(2));
    let barrier2 = barrier.clone();

    let _ = thread::spawn(move || {
        let listener = TcpListener::bind("127.0.0.1:2048").expect("could not parse address");
        barrier2.wait();
        let mut stream = listener.incoming().next().unwrap().unwrap();
        let response = b"TEST\r\n\r\n";
        stream.write(&response[..]).unwrap();
    });

    barrier.wait();
    info!("expecting 502");
    match agent
        .get("http://example.com:8080/")
        .call().unwrap_err() {
            ureq::Error::Status(502, res) => {
                assert_eq!(res.status_text(), "Bad Gateway");
            },
            e => panic!("invalid response: {:?}", e),
        };

    start_server(2048);

    info!("expecting 200");
    let res = agent
        .get("http://example.com:8080/")
        .call().unwrap();
    assert_eq!(res.status(), 200);

    //let _ = jg.join();
    info!("good bye");
}

fn start_server(port: u16) {
    thread::spawn(move|| {
        let server = Server::http(&format!("127.0.0.1:{}", port)).expect("could not create server");
        info!("starting web server in port {}", port);

        for request in server.incoming_requests() {
            println!("backend web server got request -> method: {:?}, url: {:?}, headers: {:?}",
                     request.method(),
                     request.url(),
                     request.headers()
                    );

            let response = Response::from_string("hello world");
            request.respond(response).unwrap();
            println!("backend web server sent response");
            println!("server session stopped");
        }

        println!("server on port {} closed", port);
    });
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::env;
use std::ffi::OsStr;
use std::fs;
use std::fs::File;
use std::io;
use std::io::BufReader;
use std::io::BufWriter;
use std::io::Read;
use std::io::Write;
use std::io::Seek;
use std::path::Path;

extern crate env_logger;
extern crate tools;
use tools::find_source_file;
use tools::file_format::analysis::{read_analysis, read_source, read_jumps};
use tools::format::format_file_data;
use tools::git_ops;
use tools::config;
use tools::languages;
use tools::describe;
use languages::FormatAs;

use tools::output::{PanelItem, PanelSection};

fn main() {
    env_logger::init();

    let args: Vec<_> = env::args().collect();
    let (base_args, fname_args) = args.split_at(3);

    let cfg = config::load(&base_args[1], true);
    println!("Config file read");

    let tree_name = &base_args[2];
    let tree_config = cfg.trees.get(tree_name).unwrap();

    let jumps_fname = format!("{}/jumps", tree_config.paths.index_path);
    //let jumps : std::collections::HashMap<String, tools::analysis::Jump> = std::collections::HashMap::new();
    let jumps = read_jumps(&jumps_fname);
    println!("Jumps read");

    let (blame_commit, head_oid) = match &tree_config.git {
        &Some(ref git) => {
            let head_oid = git.repo.refname_to_id("HEAD").unwrap();
            let blame_commit = if let Some(ref blame_repo) = git.blame_repo {
                let blame_oid = blame_repo.refname_to_id("HEAD").unwrap();
                Some(blame_repo.find_commit(blame_oid).unwrap())
            } else {
                None
            };
            (blame_commit, Some(head_oid))
        },
        &None => (None, None),
    };

    let head_commit = head_oid
        .and_then(|oid| tree_config.git.as_ref().unwrap().repo.find_commit(oid).ok());

    let mut extension_mapping = HashMap::new();
    extension_mapping.insert("cpp", ("header", vec!["h", "hh", "hpp", "hxx"]));
    extension_mapping.insert("cc", ("header", vec!["h", "hh", "hpp", "hxx"]));
    extension_mapping.insert("cxx", ("header", vec!["h", "hh", "hpp", "hxx"]));
    extension_mapping.insert("h", ("source", vec!["cpp", "cc", "cxx"]));
    extension_mapping.insert("hh", ("source", vec!["cpp", "cc", "cxx"]));
    extension_mapping.insert("hpp", ("source", vec!["cpp", "cc", "cxx"]));
    extension_mapping.insert("hxx", ("source", vec!["cpp", "cc", "cxx"]));

    let mut diff_cache = git_ops::TreeDiffCache::new();
    for path in fname_args {
        println!("File {}", path);

        let output_fname = format!("{}/file/{}", tree_config.paths.index_path, path);
        let source_fname = find_source_file(path, &tree_config.paths.files_path, &tree_config.paths.objdir_path);

        let format = languages::select_formatting(path);

        let output_file = File::create(output_fname).unwrap();
        let mut writer = BufWriter::new(output_file);

        let source_file = match File::open(source_fname.clone()) {
            Ok(f) => f,
            Err(_) => {
                println!("Unable to open file");
                continue;
            },
        };

        let path_wrapper = Path::new(&source_fname);
        let metadata = fs::symlink_metadata(path_wrapper).unwrap();
        if metadata.file_type().is_symlink() {
            let dest = fs::read_link(path_wrapper).unwrap();
            write!(writer, "Symlink to {}", dest.to_str().unwrap()).unwrap();
            continue;
        }

        let mut reader = BufReader::new(&source_file);

        match format {
            FormatAs::Binary => {
                let _ = io::copy(&mut reader, &mut writer);
                continue;
            },
            _ => {},
        };

        let analysis_fname = format!("{}/analysis/{}", tree_config.paths.index_path, path);
        let analysis = read_analysis(&analysis_fname, &mut read_source);

        let mut input = String::new();
        match reader.read_to_string(&mut input) {
            Ok(_) => {},
            Err(_) => {
                let mut bytes = Vec::new();
                reader.seek(std::io::SeekFrom::Start(0)).unwrap();
                match reader.read_to_end(&mut bytes) {
                    Ok(_) => {
                        input.push_str(&bytes.iter().map(|c| *c as char).collect::<String>());
                    },
                    Err(e) => {
                        println!("Unable to read file: {:?}", e);
                        continue;
                    }
                }
            }
        }

        if let Some(file_description) = describe::describe_file(&input, &path_wrapper, &format) {
            let description_fname = format!("{}/description/{}", tree_config.paths.index_path, path);
            let description_file = File::create(description_fname).unwrap();
            let mut desc_writer = BufWriter::new(description_file);
            write!(desc_writer, "{}", file_description).unwrap();
        }

        let extension = path_wrapper.extension().unwrap_or(&OsStr::new("")).to_str().unwrap();
        let show_header = match extension_mapping.get(extension) {
            Some(&(ref description, ref try_extensions)) => {
                let mut result = None;
                for try_ext in try_extensions {
                    let try_buf = path_wrapper.with_extension(try_ext);
                    let try_path = try_buf.as_path();
                    if try_path.exists() {
                        let (path_base, _) = path.split_at(path.len() - extension.len() - 1);
                        result = Some((description.to_owned(), format!("/{}/source/{}.{}", tree_name, path_base, try_ext)));
                        break;
                    }
                }
                result
            },
            None => {
                None
            }
        };

        let mut panel = vec![];

        let mut source_panel_items = vec![];
        if let Some((other_desc, other_path)) = show_header {
            source_panel_items.push(PanelItem {
                title: format!("Go to {} file", other_desc),
                link: other_path,
                update_link_lineno: false,
                accel_key: None,
            });
        };

        if !source_panel_items.is_empty() {
            panel.push(PanelSection {
                name: "Source code".to_owned(),
                items: source_panel_items,
            });
        };

        if let Some(oid) = head_oid {
            if !path.contains("__GENERATED__") {
                panel.push(PanelSection {
                    name: "Revision control".to_owned(),
                    items: vec![PanelItem {
                        title: "Permalink".to_owned(),
                        link: format!("/{}/rev/{}/{}", tree_name, oid, path),
                        update_link_lineno: true,
                        accel_key: Some('Y'),
                    }, PanelItem {
                        title: "Log".to_owned(),
                        link: format!("{}/log/tip/{}", config::get_hg_root(tree_config), path),
                        update_link_lineno: false,
                        accel_key: Some('L'),
                     }, PanelItem {
                        title: "Raw".to_owned(),
                        link: format!("{}/raw-file/tip/{}", config::get_hg_root(tree_config), path),
                        update_link_lineno: false,
                        accel_key: Some('R'),
                    }, PanelItem {
                        title: "Blame".to_owned(),
                        link: "javascript:alert('Hover over the gray bar on the left to see blame information.')".to_owned(),
                        update_link_lineno: false,
                        accel_key: None,
                    }],
                });
            }
        }

        format_file_data(&cfg,
                         tree_name,
                         &panel,
                         &head_commit,
                         &blame_commit,
                         path,
                         input,
                         &jumps,
                         &analysis,
                         &mut writer,
                         Some(&mut diff_cache)).unwrap();
    }
}
<commit_msg>Bug 1448334 - Add a bugzilla link to the source panel<commit_after>use std::collections::HashMap;
use std::env;
use std::ffi::OsStr;
use std::fs;
use std::fs::File;
use std::io;
use std::io::BufReader;
use std::io::BufWriter;
use std::io::Read;
use std::io::Write;
use std::io::Seek;
use std::path::Path;

extern crate env_logger;
extern crate tools;
use tools::find_source_file;
use tools::file_format::analysis::{read_analysis, read_source, read_jumps};
use tools::format::format_file_data;
use tools::git_ops;
use tools::config;
use tools::languages;
use tools::describe;
use languages::FormatAs;

use tools::output::{PanelItem, PanelSection};

extern crate rustc_serialize;
use rustc_serialize::json;

fn read_json_from_file(path: &str) -> Option<json::Object> {
    let components_file = File::open(path).ok()?;
    let mut reader = BufReader::new(&components_file);
    json::Json::from_reader(&mut reader).ok()?.into_object()
}

/// For a given path, looks up the bugzilla product and component and
/// returns it in a tuple if it could be found. The JSON data format
/// comes from https://searchfox.org/mozilla-central/rev/47edbd91c43db6229cf32d1fc4bae9b325b9e2d0/python/mozbuild/mozbuild/frontend/mach_commands.py#209-223,243
/// and is fairly straightforward.
fn get_bugzilla_component<'a>(bugzilla_data: &'a json::Object, path: &str) -> Option<(&'a str, &'a str)> {
    let mut path_obj = bugzilla_data.get("paths")?;
    for path_component in path.split('/') {
        path_obj = path_obj.as_object()?.get(path_component)?;
    }
    let component_id = path_obj.as_i64()?.to_string();
    let mut result_iter = bugzilla_data.get("components")?.as_object()?.get(&component_id)?.as_array()?.iter();
    let product = result_iter.next()?.as_string()?;
    let component = result_iter.next()?.as_string()?;
    Some((product, component))
}

fn main() {
    env_logger::init();

    let args: Vec<_> = env::args().collect();
    let (base_args, fname_args) = args.split_at(3);

    let cfg = config::load(&base_args[1], true);
    println!("Config file read");

    let tree_name = &base_args[2];
    let tree_config = cfg.trees.get(tree_name).unwrap();

    let jumps_fname = format!("{}/jumps", tree_config.paths.index_path);
    //let jumps : std::collections::HashMap<String, tools::analysis::Jump> = std::collections::HashMap::new();
    let jumps = read_jumps(&jumps_fname);
    println!("Jumps read");

    let bugzilla_fname = format!("{}/bugzilla-components.json", tree_config.paths.index_path);
    let bugzilla_data = read_json_from_file(&bugzilla_fname);
    if bugzilla_data.is_some() {
        println!("Bugzilla components read");
    } else {
        println!("No bugzilla-components.json file found");
    }

    let (blame_commit, head_oid) = match &tree_config.git {
        &Some(ref git) => {
            let head_oid = git.repo.refname_to_id("HEAD").unwrap();
            let blame_commit = if let Some(ref blame_repo) = git.blame_repo {
                let blame_oid = blame_repo.refname_to_id("HEAD").unwrap();
                Some(blame_repo.find_commit(blame_oid).unwrap())
            } else {
                None
            };
            (blame_commit, Some(head_oid))
        },
        &None => (None, None),
    };

    let head_commit = head_oid
        .and_then(|oid| tree_config.git.as_ref().unwrap().repo.find_commit(oid).ok());

    let mut extension_mapping = HashMap::new();
    extension_mapping.insert("cpp", ("header", vec!["h", "hh", "hpp", "hxx"]));
    extension_mapping.insert("cc", ("header", vec!["h", "hh", "hpp", "hxx"]));
    extension_mapping.insert("cxx", ("header", vec!["h", "hh", "hpp", "hxx"]));
    extension_mapping.insert("h", ("source", vec!["cpp", "cc", "cxx"]));
    extension_mapping.insert("hh", ("source", vec!["cpp", "cc", "cxx"]));
    extension_mapping.insert("hpp", ("source", vec!["cpp", "cc", "cxx"]));
    extension_mapping.insert("hxx", ("source", vec!["cpp", "cc", "cxx"]));

    let mut diff_cache = git_ops::TreeDiffCache::new();
    for path in fname_args {
        println!("File {}", path);

        let output_fname = format!("{}/file/{}", tree_config.paths.index_path, path);
        let source_fname = find_source_file(path, &tree_config.paths.files_path, &tree_config.paths.objdir_path);

        let format = languages::select_formatting(path);

        let output_file = File::create(output_fname).unwrap();
        let mut writer = BufWriter::new(output_file);

        let source_file = match File::open(source_fname.clone()) {
            Ok(f) => f,
            Err(_) => {
                println!("Unable to open file");
                continue;
            },
        };

        let path_wrapper = Path::new(&source_fname);
        let metadata = fs::symlink_metadata(path_wrapper).unwrap();
        if metadata.file_type().is_symlink() {
            let dest = fs::read_link(path_wrapper).unwrap();
            write!(writer, "Symlink to {}", dest.to_str().unwrap()).unwrap();
            continue;
        }

        let mut reader = BufReader::new(&source_file);

        match format {
            FormatAs::Binary => {
                let _ = io::copy(&mut reader, &mut writer);
                continue;
            },
            _ => {},
        };

        let analysis_fname = format!("{}/analysis/{}", tree_config.paths.index_path, path);
        let analysis = read_analysis(&analysis_fname, &mut read_source);

        let mut input = String::new();
        match reader.read_to_string(&mut input) {
            Ok(_) => {},
            Err(_) => {
                let mut bytes = Vec::new();
                reader.seek(std::io::SeekFrom::Start(0)).unwrap();
                match reader.read_to_end(&mut bytes) {
                    Ok(_) => {
                        input.push_str(&bytes.iter().map(|c| *c as char).collect::<String>());
                    },
                    Err(e) => {
                        println!("Unable to read file: {:?}", e);
                        continue;
                    }
                }
            }
        }

        if let Some(file_description) = describe::describe_file(&input, &path_wrapper, &format) {
            let description_fname = format!("{}/description/{}", tree_config.paths.index_path, path);
            let description_file = File::create(description_fname).unwrap();
            let mut desc_writer = BufWriter::new(description_file);
            write!(desc_writer, "{}", file_description).unwrap();
        }

        let extension = path_wrapper.extension().unwrap_or(&OsStr::new("")).to_str().unwrap();
        let show_header = match extension_mapping.get(extension) {
            Some(&(ref description, ref try_extensions)) => {
                let mut result = None;
                for try_ext in try_extensions {
                    let try_buf = path_wrapper.with_extension(try_ext);
                    let try_path = try_buf.as_path();
                    if try_path.exists() {
                        let (path_base, _) = path.split_at(path.len() - extension.len() - 1);
                        result = Some((description.to_owned(), format!("/{}/source/{}.{}", tree_name, path_base, try_ext)));
                        break;
                    }
                }
                result
            },
            None => {
                None
            }
        };

        let mut panel = vec![];

        let mut source_panel_items = vec![];
        if let Some((other_desc, other_path)) = show_header {
            source_panel_items.push(PanelItem {
                title: format!("Go to {} file", other_desc),
                link: other_path,
                update_link_lineno: false,
                accel_key: None,
            });
        };
        if let Some(ref bugzilla) = bugzilla_data {
            if !path.contains("__GENERATED__") {
                if let Some((product, component)) = get_bugzilla_component(bugzilla, &path) {
                    source_panel_items.push(PanelItem {
                        title: "File a bug for this file".to_owned(),
                        link: format!("https://bugzilla.mozilla.org/enter_bug.cgi?product={}&component={}", product, component),
                        update_link_lineno: false,
                        accel_key: None,
                    });
                }
            }
        }

        if !source_panel_items.is_empty() {
            panel.push(PanelSection {
                name: "Source code".to_owned(),
                items: source_panel_items,
            });
        };

        if let Some(oid) = head_oid {
            if !path.contains("__GENERATED__") {
                panel.push(PanelSection {
                    name: "Revision control".to_owned(),
                    items: vec![PanelItem {
                        title: "Permalink".to_owned(),
                        link: format!("/{}/rev/{}/{}", tree_name, oid, path),
                        update_link_lineno: true,
                        accel_key: Some('Y'),
                    }, PanelItem {
                        title: "Log".to_owned(),
                        link: format!("{}/log/tip/{}", config::get_hg_root(tree_config), path),
                        update_link_lineno: false,
                        accel_key: Some('L'),
                     }, PanelItem {
                        title: "Raw".to_owned(),
                        link: format!("{}/raw-file/tip/{}", config::get_hg_root(tree_config), path),
                        update_link_lineno: false,
                        accel_key: Some('R'),
                    }, PanelItem {
                        title: "Blame".to_owned(),
                        link: "javascript:alert('Hover over the gray bar on the left to see blame information.')".to_owned(),
                        update_link_lineno: false,
                        accel_key: None,
                    }],
                });
            }
        }

        format_file_data(&cfg,
                         tree_name,
                         &panel,
                         &head_commit,
                         &blame_commit,
                         path,
                         input,
                         &jumps,
                         &analysis,
                         &mut writer,
                         Some(&mut diff_cache)).unwrap();
    }
}
<|endoftext|>


<commit_before>use std::collections::linked_list::{self, LinkedList};
use std::mem;
use std::rc::Rc;
use std::result;

use api_server::request::sync::{
    Error as SyncError, NetworkInterfaceBody, OkStatus as SyncOkStatus, RateLimiterDescription,
};
use net_util::{MacAddr, Tap, TapError};

pub struct NetworkInterfaceConfig {
    // The request body received from the API side.
    body: NetworkInterfaceBody,
    // We extract the id from the body and hold it as a reference counted String. This should
    // come in handy later on, when we'll need the id to appear in a number of data structures
    // to implement efficient lookup, update, deletion, etc.
    id: Rc<String>,
    // We open the tap that will be associated with the virtual device as soon as the PUT request
    // arrives from the API. We want to see if there are any errors associated with the operation,
    // and if so, we want to report the failure back to the API caller immediately. This is an
    // option, because the inner value will be moved to the actual virtio net device before boot.
    pub tap: Option<Tap>,
    pub rx_rate_limiter: Option<RateLimiterDescription>,
    pub tx_rate_limiter: Option<RateLimiterDescription>,
}

impl NetworkInterfaceConfig {
    pub fn try_from_body(mut body: NetworkInterfaceBody) -> result::Result<Self, TapError> {
        let id = Rc::new(mem::replace(&mut body.iface_id, String::new()));

        // TODO: rework net_util stuff such that references would suffice here, instead
        // of having to move things around.
        let tap = Tap::open_named(body.host_dev_name.as_str())?;

        let rx_rate_limiter = body.rx_rate_limiter.take();
        let tx_rate_limiter = body.tx_rate_limiter.take();
        Ok(NetworkInterfaceConfig {
            body,
            id,
            tap: Some(tap),
            rx_rate_limiter,
            tx_rate_limiter,
        })
    }

    pub fn id_as_str(&self) -> &str {
        self.id.as_str()
    }

    pub fn take_tap(&mut self) -> Option<Tap> {
        self.tap.take()
    }

    pub fn guest_mac(&self) -> Option<&MacAddr> {
        self.body.guest_mac.as_ref()
    }
}

pub struct NetworkInterfaceConfigs {
    // We use just a list for now, since we only add interfaces as this point.
    if_list: LinkedList<NetworkInterfaceConfig>,
}

impl NetworkInterfaceConfigs {
    pub fn new() -> Self {
        NetworkInterfaceConfigs {
            if_list: LinkedList::new(),
        }
    }

    pub fn put(&mut self, body: NetworkInterfaceBody) -> result::Result<SyncOkStatus, SyncError> {
        let cfg = NetworkInterfaceConfig::try_from_body(body).map_err(SyncError::OpenTap)?;
        for device_config in self.if_list.iter_mut() {
            if device_config.id_as_str() == cfg.id_as_str() {
                device_config.tap = cfg.tap;
                device_config.body = cfg.body.clone();
                return Ok(SyncOkStatus::Updated);
            }

            if cfg.guest_mac().is_some() && device_config.guest_mac() == cfg.guest_mac() {
                return Err(SyncError::GuestMacAddressInUse);
            }
        }
        self.if_list.push_back(cfg);
        Ok(SyncOkStatus::Created)
    }

    pub fn iter_mut(&mut self) -> linked_list::IterMut<NetworkInterfaceConfig> {
        self.if_list.iter_mut()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use api_server::request::sync::DeviceState;
    use net_util::MacAddr;

    #[test]
    fn test_put() {
        let mut netif_configs = NetworkInterfaceConfigs::new();
        assert!(netif_configs.if_list.is_empty());

        if let Ok(mac) = MacAddr::parse_str("01:23:45:67:89:0A") {
            let mut netif_body = NetworkInterfaceBody {
                iface_id: String::from("foo"),
                state: DeviceState::Attached,
                host_dev_name: String::from("bar"),
                guest_mac: Some(mac.clone()),
                rx_rate_limiter: Some(RateLimiterDescription::default()),
                tx_rate_limiter: Some(RateLimiterDescription::default()),
            };
            assert!(netif_configs.put(netif_body.clone()).is_ok());
            assert_eq!(netif_configs.if_list.len(), 1);

            netif_body.host_dev_name = String::from("baz");
            assert!(netif_configs.put(netif_body).is_ok());
            assert_eq!(netif_configs.if_list.len(), 1);

            let other_netif_body = NetworkInterfaceBody {
                iface_id: String::from("bar"),
                state: DeviceState::Attached,
                host_dev_name: String::from("foo"),
                guest_mac: Some(mac.clone()),
                rx_rate_limiter: None,
                tx_rate_limiter: None,
            };
            assert!(netif_configs.put(other_netif_body).is_err());
            assert_eq!(netif_configs.if_list.len(), 1);
        }
    }
}
<commit_msg>Fix network interfaces API updates before boot<commit_after>use std::mem;
use std::rc::Rc;
use std::result;

use api_server::request::sync::{
    Error as SyncError, NetworkInterfaceBody, OkStatus as SyncOkStatus, RateLimiterDescription,
};
use net_util::{MacAddr, Tap, TapError};

pub struct NetworkInterfaceConfig {
    // The request body received from the API side.
    body: NetworkInterfaceBody,
    // We extract the id from the body and hold it as a reference counted String. This should
    // come in handy later on, when we'll need the id to appear in a number of data structures
    // to implement efficient lookup, update, deletion, etc.
    id: Rc<String>,
    // We open the tap that will be associated with the virtual device as soon as the PUT request
    // arrives from the API. We want to see if there are any errors associated with the operation,
    // and if so, we want to report the failure back to the API caller immediately. This is an
    // option, because the inner value will be moved to the actual virtio net device before boot.
    pub tap: Option<Tap>,
    pub rx_rate_limiter: Option<RateLimiterDescription>,
    pub tx_rate_limiter: Option<RateLimiterDescription>,
}

impl NetworkInterfaceConfig {
    pub fn try_from_body(mut body: NetworkInterfaceBody) -> result::Result<Self, TapError> {
        let id = Rc::new(mem::replace(&mut body.iface_id, String::new()));

        // TODO: rework net_util stuff such that references would suffice here, instead
        // of having to move things around.
        let tap = Tap::open_named(body.host_dev_name.as_str())?;

        let rx_rate_limiter = body.rx_rate_limiter.take();
        let tx_rate_limiter = body.tx_rate_limiter.take();
        Ok(NetworkInterfaceConfig {
            body,
            id,
            tap: Some(tap),
            rx_rate_limiter,
            tx_rate_limiter,
        })
    }

    fn update_from_body(&mut self, mut body: NetworkInterfaceBody) {
        self.id = Rc::new(mem::replace(&mut body.iface_id, String::new()));
        self.rx_rate_limiter = body.rx_rate_limiter.take();
        self.tx_rate_limiter = body.tx_rate_limiter.take();
        self.body = body;
    }

    pub fn id_as_str(&self) -> &str {
        self.id.as_str()
    }

    pub fn take_tap(&mut self) -> Option<Tap> {
        self.tap.take()
    }

    pub fn guest_mac(&self) -> Option<&MacAddr> {
        self.body.guest_mac.as_ref()
    }
}

pub struct NetworkInterfaceConfigs {
    if_list: Vec<NetworkInterfaceConfig>,
}

impl NetworkInterfaceConfigs {
    pub fn new() -> Self {
        NetworkInterfaceConfigs {
            if_list: Vec::new(),
        }
    }

    pub fn put(&mut self, body: NetworkInterfaceBody) -> result::Result<SyncOkStatus, SyncError> {
        match self.if_list
            .iter()
            .position(|netif| netif.id.as_str() == body.iface_id.as_str())
        {
            Some(index) => self.update(index, body),
            None => self.create(body),
        }
    }

    pub fn iter_mut(&mut self) -> ::std::slice::IterMut<NetworkInterfaceConfig> {
        self.if_list.iter_mut()
    }

    fn create(&mut self, body: NetworkInterfaceBody) -> result::Result<SyncOkStatus, SyncError> {
        self.validate_unique_mac(&body.guest_mac)?;
        let cfg = NetworkInterfaceConfig::try_from_body(body).map_err(SyncError::OpenTap)?;
        self.if_list.push(cfg);
        Ok(SyncOkStatus::Created)
    }

    fn update(
        &mut self,
        index: usize,
        body: NetworkInterfaceBody,
    ) -> result::Result<SyncOkStatus, SyncError> {
        if self.if_list[index].body.host_dev_name != body.host_dev_name {
            // This is a new tap device which replaces the one at the specified ID.
            self.if_list.remove(index);
            self.create(body)?;
        } else {
            // The same tap device is being updated.
            self.validate_unique_mac(&body.guest_mac)?;
            self.if_list[index].update_from_body(body);
        }
        Ok(SyncOkStatus::Updated)
    }

    fn validate_unique_mac(&self, mac: &Option<MacAddr>) -> result::Result<(), SyncError> {
        for device_config in self.if_list.iter() {
            if mac.is_some() && mac == &device_config.body.guest_mac {
                return Err(SyncError::GuestMacAddressInUse);
            }
        }
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use api_server::request::sync::DeviceState;
    use net_util::MacAddr;

    fn make_netif(id: &str, name: &str, mac: MacAddr) -> NetworkInterfaceBody {
        NetworkInterfaceBody {
            iface_id: String::from(id),
            state: DeviceState::Attached,
            host_dev_name: String::from(name),
            guest_mac: Some(mac),
            rx_rate_limiter: Some(RateLimiterDescription::default()),
            tx_rate_limiter: Some(RateLimiterDescription::default()),
        }
    }

    fn make_netif_cfg(body: NetworkInterfaceBody, id: &str) -> NetworkInterfaceConfig {
        NetworkInterfaceConfig {
            body: body,
            id: Rc::new(String::from(id)),
            tap: None,
            tx_rate_limiter: None,
            rx_rate_limiter: None,
        }
    }

    #[test]
    fn test_put() {
        let mut netif_configs = NetworkInterfaceConfigs::new();
        assert!(netif_configs.if_list.is_empty());

        let mac1 = MacAddr::parse_str("01:23:45:67:89:0A").unwrap();
        let mac2 = MacAddr::parse_str("23:45:67:89:0A:01").unwrap();
        let mac3 = MacAddr::parse_str("45:67:89:0A:01:23").unwrap();

        // Add an interface.
        let mut netif_body = make_netif("foo", "bar", mac1);
        netif_configs
            .if_list
            .push(make_netif_cfg(netif_body.clone(), "foo"));
        assert_eq!(netif_configs.if_list.len(), 1);

        // Update MAC.
        netif_body.guest_mac = Some(mac2.clone());
        assert!(netif_configs.put(netif_body).is_ok());
        assert_eq!(netif_configs.if_list.len(), 1);

        // Try to add another interface with the same MAC.
        let mut other_netif_body = make_netif("bar", "foo", mac2.clone());
        assert!(netif_configs.put(other_netif_body.clone()).is_err());
        assert_eq!(netif_configs.if_list.len(), 1);

        // Add another interface.
        other_netif_body.guest_mac = Some(mac3);
        netif_configs
            .if_list
            .push(make_netif_cfg(other_netif_body.clone(), "foo"));
        assert_eq!(netif_configs.if_list.len(), 2);

        // Try to update with an unavailable name.
        other_netif_body.host_dev_name = String::from("baz");
        assert!(netif_configs.put(other_netif_body.clone()).is_err());
        assert_eq!(netif_configs.if_list.len(), 2);

        // Try to update with an unavailable MAC.
        other_netif_body.guest_mac = Some(mac2);
        assert!(netif_configs.put(other_netif_body).is_err());
        assert_eq!(netif_configs.if_list.len(), 2);
    }
}
<|endoftext|>