<commit_before>use std::{cell::RefCell, rc::Rc, sync::atomic::Ordering, time::Duration};

#[cfg(feature = "debug")]
use smithay::backend::renderer::gles2::Gles2Texture;
#[cfg(feature = "egl")]
use smithay::{
    backend::renderer::{ImportDma, ImportEgl},
    wayland::dmabuf::init_dmabuf_global,
};
use smithay::{
    backend::{
        renderer::{Renderer, Transform},
        winit::{self, WinitEvent},
        SwapBuffersError,
    },
    reexports::{
        calloop::EventLoop,
        wayland_server::{protocol::wl_output, Display},
    },
    wayland::{
        output::{Mode, PhysicalProperties},
        seat::CursorImageStatus,
    },
};

use slog::Logger;

use crate::state::{AnvilState, Backend};
use crate::{drawing::*, render::render_layers_and_windows};

pub const OUTPUT_NAME: &str = "winit";

pub struct WinitData {
    #[cfg(feature = "debug")]
    fps_texture: Gles2Texture,
    #[cfg(feature = "debug")]
    pub fps: fps_ticker::Fps,
}

impl Backend for WinitData {
    fn seat_name(&self) -> String {
        String::from("winit")
    }
}

pub fn run_winit(log: Logger) {
    let mut event_loop = EventLoop::try_new().unwrap();
    let display = Rc::new(RefCell::new(Display::new()));

    let (backend, mut winit) = match winit::init(log.clone()) {
        Ok(ret) => ret,
        Err(err) => {
            slog::crit!(log, "Failed to initialize Winit backend: {}", err);
            return;
        }
    };
    let backend = Rc::new(RefCell::new(backend));

    #[cfg(feature = "egl")]
    if backend
        .borrow_mut()
        .renderer()
        .bind_wl_display(&display.borrow())
        .is_ok()
    {
        info!(log, "EGL hardware-acceleration enabled");
        let dmabuf_formats = backend
            .borrow_mut()
            .renderer()
            .dmabuf_formats()
            .cloned()
            .collect::<Vec<_>>();
        let backend = backend.clone();
        init_dmabuf_global(
            &mut *display.borrow_mut(),
            dmabuf_formats,
            move |buffer, _| backend.borrow_mut().renderer().import_dmabuf(buffer).is_ok(),
            log.clone(),
        );
    };

    let size = backend.borrow().window_size().physical_size;

    /*
     * Initialize the globals
     */

    let data = WinitData {
        #[cfg(feature = "debug")]
        fps_texture: import_bitmap(
            backend.borrow_mut().renderer(),
            &image::io::Reader::with_format(std::io::Cursor::new(FPS_NUMBERS_PNG), image::ImageFormat::Png)
                .decode()
                .unwrap()
                .to_rgba8(),
        )
        .expect("Unable to upload FPS texture"),
        #[cfg(feature = "debug")]
        fps: fps_ticker::Fps::default(),
    };
    let mut state = AnvilState::init(display.clone(), event_loop.handle(), data, log.clone(), true);

    let mode = Mode {
        size,
        refresh: 60_000,
    };

    state.output_map.borrow_mut().add(
        OUTPUT_NAME,
        PhysicalProperties {
            size: (0, 0).into(),
            subpixel: wl_output::Subpixel::Unknown,
            make: "Smithay".into(),
            model: "Winit".into(),
        },
        mode,
    );

    let start_time = std::time::Instant::now();
    let mut cursor_visible = true;

    #[cfg(feature = "xwayland")]
    state.start_xwayland();

    info!(log, "Initialization completed, starting the main loop.");

    while state.running.load(Ordering::SeqCst) {
        if winit
            .dispatch_new_events(|event| match event {
                WinitEvent::Resized { size, .. } => {
                    state.output_map.borrow_mut().update_mode_by_name(
                        Mode {
                            size,
                            refresh: 60_000,
                        },
                        OUTPUT_NAME,
                    );

                    let output_mut = state.output_map.borrow();
                    let output = output_mut.find_by_name(OUTPUT_NAME).unwrap();

                    state.window_map.borrow_mut().layers.arange_layers(output);
                }

                WinitEvent::Input(event) => state.process_input_event_windowed(event, OUTPUT_NAME),

                _ => (),
            })
            .is_err()
        {
            state.running.store(false, Ordering::SeqCst);
            break;
        }

        // drawing logic
        {
            let mut backend = backend.borrow_mut();
            // This is safe to do as with winit we are guaranteed to have exactly one output
            let (output_geometry, output_scale) = state
                .output_map
                .borrow()
                .find_by_name(OUTPUT_NAME)
                .map(|output| (output.geometry(), output.scale()))
                .unwrap();

            let result = backend
                .bind()
                .and_then(|_| {
                    backend
                        .renderer()
                        .render(size, Transform::Flipped180, |renderer, frame| {
                            render_layers_and_windows(
                                renderer,
                                frame,
                                &*state.window_map.borrow(),
                                output_geometry,
                                output_scale,
                                &log,
                            )?;

                            let (x, y) = state.pointer_location.into();

                            // draw the dnd icon if any
                            {
                                let guard = state.dnd_icon.lock().unwrap();
                                if let Some(ref surface) = *guard {
                                    if surface.as_ref().is_alive() {
                                        draw_dnd_icon(
                                            renderer,
                                            frame,
                                            surface,
                                            (x as i32, y as i32).into(),
                                            output_scale,
                                            &log,
                                        )?;
                                    }
                                }
                            }
                            // draw the cursor as relevant
                            {
                                let mut guard = state.cursor_status.lock().unwrap();
                                // reset the cursor if the surface is no longer alive
                                let mut reset = false;
                                if let CursorImageStatus::Image(ref surface) = *guard {
                                    reset = !surface.as_ref().is_alive();
                                }
                                if reset {
                                    *guard = CursorImageStatus::Default;
                                }

                                // draw as relevant
                                if let CursorImageStatus::Image(ref surface) = *guard {
                                    cursor_visible = false;
                                    draw_cursor(
                                        renderer,
                                        frame,
                                        surface,
                                        (x as i32, y as i32).into(),
                                        output_scale,
                                        &log,
                                    )?;
                                } else {
                                    cursor_visible = true;
                                }
                            }

                            #[cfg(feature = "debug")]
                            {
                                let fps = state.backend_data.fps.avg().round() as u32;

                                draw_fps(
                                    renderer,
                                    frame,
                                    &state.backend_data.fps_texture,
                                    output_scale as f64,
                                    fps,
                                )?;
                            }

                            Ok(())
                        })
                        .map_err(Into::<SwapBuffersError>::into)
                        .and_then(|x| x)
                })
                .and_then(|_| backend.submit(None, 1.0));

            backend.window().set_cursor_visible(cursor_visible);

            if let Err(SwapBuffersError::ContextLost(err)) = result {
                error!(log, "Critical Rendering Error: {}", err);
                state.running.store(false, Ordering::SeqCst);
            }
        }

        // Send frame events so that client start drawing their next frame
        state
            .window_map
            .borrow()
            .send_frames(start_time.elapsed().as_millis() as u32);
        display.borrow_mut().flush_clients(&mut state);

        if event_loop
            .dispatch(Some(Duration::from_millis(16)), &mut state)
            .is_err()
        {
            state.running.store(false, Ordering::SeqCst);
        } else {
            display.borrow_mut().flush_clients(&mut state);
            state.window_map.borrow_mut().refresh();
            state.output_map.borrow_mut().refresh();
        }

        #[cfg(feature = "debug")]
        state.backend_data.fps.tick();
    }

    // Cleanup stuff
    state.window_map.borrow_mut().clear();
}
<commit_msg>anvil: Fix winit resizing<commit_after>use std::{cell::RefCell, rc::Rc, sync::atomic::Ordering, time::Duration};

#[cfg(feature = "debug")]
use smithay::backend::renderer::gles2::Gles2Texture;
#[cfg(feature = "egl")]
use smithay::{
    backend::renderer::{ImportDma, ImportEgl},
    wayland::dmabuf::init_dmabuf_global,
};
use smithay::{
    backend::{
        renderer::{Renderer, Transform},
        winit::{self, WinitEvent},
        SwapBuffersError,
    },
    reexports::{
        calloop::EventLoop,
        wayland_server::{protocol::wl_output, Display},
    },
    wayland::{
        output::{Mode, PhysicalProperties},
        seat::CursorImageStatus,
    },
};

use slog::Logger;

use crate::state::{AnvilState, Backend};
use crate::{drawing::*, render::render_layers_and_windows};

pub const OUTPUT_NAME: &str = "winit";

pub struct WinitData {
    #[cfg(feature = "debug")]
    fps_texture: Gles2Texture,
    #[cfg(feature = "debug")]
    pub fps: fps_ticker::Fps,
}

impl Backend for WinitData {
    fn seat_name(&self) -> String {
        String::from("winit")
    }
}

pub fn run_winit(log: Logger) {
    let mut event_loop = EventLoop::try_new().unwrap();
    let display = Rc::new(RefCell::new(Display::new()));

    let (backend, mut winit) = match winit::init(log.clone()) {
        Ok(ret) => ret,
        Err(err) => {
            slog::crit!(log, "Failed to initialize Winit backend: {}", err);
            return;
        }
    };
    let backend = Rc::new(RefCell::new(backend));

    #[cfg(feature = "egl")]
    if backend
        .borrow_mut()
        .renderer()
        .bind_wl_display(&display.borrow())
        .is_ok()
    {
        info!(log, "EGL hardware-acceleration enabled");
        let dmabuf_formats = backend
            .borrow_mut()
            .renderer()
            .dmabuf_formats()
            .cloned()
            .collect::<Vec<_>>();
        let backend = backend.clone();
        init_dmabuf_global(
            &mut *display.borrow_mut(),
            dmabuf_formats,
            move |buffer, _| backend.borrow_mut().renderer().import_dmabuf(buffer).is_ok(),
            log.clone(),
        );
    };

    let size = backend.borrow().window_size().physical_size;

    /*
     * Initialize the globals
     */

    let data = WinitData {
        #[cfg(feature = "debug")]
        fps_texture: import_bitmap(
            backend.borrow_mut().renderer(),
            &image::io::Reader::with_format(std::io::Cursor::new(FPS_NUMBERS_PNG), image::ImageFormat::Png)
                .decode()
                .unwrap()
                .to_rgba8(),
        )
        .expect("Unable to upload FPS texture"),
        #[cfg(feature = "debug")]
        fps: fps_ticker::Fps::default(),
    };
    let mut state = AnvilState::init(display.clone(), event_loop.handle(), data, log.clone(), true);

    let mode = Mode {
        size,
        refresh: 60_000,
    };

    state.output_map.borrow_mut().add(
        OUTPUT_NAME,
        PhysicalProperties {
            size: (0, 0).into(),
            subpixel: wl_output::Subpixel::Unknown,
            make: "Smithay".into(),
            model: "Winit".into(),
        },
        mode,
    );

    let start_time = std::time::Instant::now();
    let mut cursor_visible = true;

    #[cfg(feature = "xwayland")]
    state.start_xwayland();

    info!(log, "Initialization completed, starting the main loop.");

    while state.running.load(Ordering::SeqCst) {
        if winit
            .dispatch_new_events(|event| match event {
                WinitEvent::Resized { size, .. } => {
                    state.output_map.borrow_mut().update_mode_by_name(
                        Mode {
                            size,
                            refresh: 60_000,
                        },
                        OUTPUT_NAME,
                    );

                    let output_mut = state.output_map.borrow();
                    let output = output_mut.find_by_name(OUTPUT_NAME).unwrap();

                    state.window_map.borrow_mut().layers.arange_layers(output);
                }

                WinitEvent::Input(event) => state.process_input_event_windowed(event, OUTPUT_NAME),

                _ => (),
            })
            .is_err()
        {
            state.running.store(false, Ordering::SeqCst);
            break;
        }

        // drawing logic
        {
            let mut backend = backend.borrow_mut();
            // This is safe to do as with winit we are guaranteed to have exactly one output
            let (output_geometry, output_scale) = state
                .output_map
                .borrow()
                .find_by_name(OUTPUT_NAME)
                .map(|output| (output.geometry(), output.scale()))
                .unwrap();

            let result = backend
                .bind()
                .and_then(|_| {
                    backend
                        .renderer()
                        .render(
                            output_geometry
                                .size
                                .to_f64()
                                .to_physical(output_scale as f64)
                                .to_i32_round(),
                            Transform::Flipped180,
                            |renderer, frame| {
                                render_layers_and_windows(
                                    renderer,
                                    frame,
                                    &*state.window_map.borrow(),
                                    output_geometry,
                                    output_scale,
                                    &log,
                                )?;

                                let (x, y) = state.pointer_location.into();

                                // draw the dnd icon if any
                                {
                                    let guard = state.dnd_icon.lock().unwrap();
                                    if let Some(ref surface) = *guard {
                                        if surface.as_ref().is_alive() {
                                            draw_dnd_icon(
                                                renderer,
                                                frame,
                                                surface,
                                                (x as i32, y as i32).into(),
                                                output_scale,
                                                &log,
                                            )?;
                                        }
                                    }
                                }
                                // draw the cursor as relevant
                                {
                                    let mut guard = state.cursor_status.lock().unwrap();
                                    // reset the cursor if the surface is no longer alive
                                    let mut reset = false;
                                    if let CursorImageStatus::Image(ref surface) = *guard {
                                        reset = !surface.as_ref().is_alive();
                                    }
                                    if reset {
                                        *guard = CursorImageStatus::Default;
                                    }

                                    // draw as relevant
                                    if let CursorImageStatus::Image(ref surface) = *guard {
                                        cursor_visible = false;
                                        draw_cursor(
                                            renderer,
                                            frame,
                                            surface,
                                            (x as i32, y as i32).into(),
                                            output_scale,
                                            &log,
                                        )?;
                                    } else {
                                        cursor_visible = true;
                                    }
                                }

                                #[cfg(feature = "debug")]
                                {
                                    let fps = state.backend_data.fps.avg().round() as u32;

                                    draw_fps(
                                        renderer,
                                        frame,
                                        &state.backend_data.fps_texture,
                                        output_scale as f64,
                                        fps,
                                    )?;
                                }

                                Ok(())
                            },
                        )
                        .map_err(Into::<SwapBuffersError>::into)
                        .and_then(|x| x)
                })
                .and_then(|_| backend.submit(None, 1.0));

            backend.window().set_cursor_visible(cursor_visible);

            if let Err(SwapBuffersError::ContextLost(err)) = result {
                error!(log, "Critical Rendering Error: {}", err);
                state.running.store(false, Ordering::SeqCst);
            }
        }

        // Send frame events so that client start drawing their next frame
        state
            .window_map
            .borrow()
            .send_frames(start_time.elapsed().as_millis() as u32);
        display.borrow_mut().flush_clients(&mut state);

        if event_loop
            .dispatch(Some(Duration::from_millis(16)), &mut state)
            .is_err()
        {
            state.running.store(false, Ordering::SeqCst);
        } else {
            display.borrow_mut().flush_clients(&mut state);
            state.window_map.borrow_mut().refresh();
            state.output_map.borrow_mut().refresh();
        }

        #[cfg(feature = "debug")]
        state.backend_data.fps.tick();
    }

    // Cleanup stuff
    state.window_map.borrow_mut().clear();
}
<|endoftext|>


<commit_before>use std::io::BufReader;
use std::io::prelude::*;
use std::fs::File;
use std::env;

fn main() {
    let mut tab: Vec<_> = env::args().collect();

    if tab.len()==1 {
        tab[0] = "./".to_string();
    }
}
<commit_msg>first implementation without args<commit_after>use std::fs;
use std::env;

fn main() {
    let tab: Vec<_> = env::args().collect();

    if tab.len()==1 {
        for entry in fs::read_dir("./").unwrap() {
            match entry {
                Ok(n) => println!("> {:?}", n.path()),
                Err(err) => println!("Error: {}", err),
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::cell::RefCell;
use std::rc::Rc;
use super::*;
use super::errors::*;

#[derive(Debug)]
pub struct ServiceBody<'input> {
    pub name: &'input str,
    pub comment: Vec<&'input str>,
    pub children: Vec<ServiceNested<'input>>,
}

struct Node {
    parent: Option<Rc<RefCell<Node>>>,
    method: Option<RpLoc<String>>,
    path: Option<RpLoc<RpPathSpec>>,
    options: Vec<RpLoc<RpOptionDecl>>,
    comment: Vec<String>,
    returns: Vec<RpServiceReturns>,
    accepts: Vec<RpServiceAccepts>,
}

impl Node {
    fn push_returns(&mut self, input: RpServiceReturns) {
        self.returns.push(input);
    }

    fn push_accepts(&mut self, input: RpServiceAccepts) {
        self.accepts.push(input);
    }
}

fn convert_return(comment: Vec<String>,
                  ty: RpLoc<RpType>,
                  options: Vec<RpLoc<OptionDecl>>)
                  -> Result<RpServiceReturns> {
    let options = Options::new(options.into_model()?);

    let produces: Option<RpLoc<String>> = options.find_one_string("produces")?;

    let produces = if let Some(produces) = produces {
        let (produces, pos) = produces.both();

        let produces = produces.parse()
            .chain_err(|| ErrorKind::Pos("not a valid mime type".to_owned(), pos.into()))?;

        Some(produces)
    } else {
        None
    };

    let status: Option<RpLoc<RpNumber>> = options.find_one_number("status")?;

    let status = if let Some(status) = status {
        let (status, pos) = status.both();

        let status = status.to_u32()
            .ok_or_else(|| ErrorKind::Pos("not a valid status".to_owned(), pos.into()))?;

        Some(status)
    } else {
        None
    };

    Ok(RpServiceReturns {
        comment: comment,
        ty: ty.into_model()?,
        produces: produces,
        status: status,
    })
}

fn convert_accepts(comment: Vec<String>,
                   ty: RpLoc<RpType>,
                   options: Vec<RpLoc<OptionDecl>>)
                   -> Result<RpServiceAccepts> {
    let options = Options::new(options.into_model()?);

    let accepts: Option<RpLoc<String>> = options.find_one_string("accept")?;

    let accepts = if let Some(accepts) = accepts {
        let (accepts, pos) = accepts.both();

        let accepts = accepts.parse()
            .chain_err(|| ErrorKind::Pos("not a valid mime type".to_owned(), pos.into()))?;

        Some(accepts)
    } else {
        None
    };

    Ok(RpServiceAccepts {
        comment: comment,
        ty: ty.into_model()?,
        accepts: accepts,
    })
}

/// Recursively unwind all inherited information about the given node, and convert to a service
/// endpoint.
fn unwind(node: Rc<RefCell<Node>>) -> Result<RpServiceEndpoint> {
    let mut method: Option<RpLoc<String>> = None;
    let mut path = Vec::new();
    let mut options: Vec<RpLoc<RpOptionDecl>> = Vec::new();
    let mut returns = Vec::new();
    let mut accepts = Vec::new();

    let comment = node.try_borrow()?.comment.clone();

    let mut current = Some(node);

    while let Some(step) = current {
        let next = step.try_borrow()?;

        // set method if not set
        method = method.or_else(|| next.method.clone());

        if let Some(ref next_url) = next.path {
            // correct order by extending in reverse
            path.extend(next_url.as_ref().segments.iter().rev().map(Clone::clone));
        }

        options.extend(next.options.iter().map(Clone::clone).rev());
        returns.extend(next.returns.iter().map(Clone::clone));
        accepts.extend(next.accepts.iter().map(Clone::clone));

        current = next.parent.clone();
    }

    let path = RpPathSpec { segments: path.into_iter().rev().collect() };

    let _options = Options::new(options.into_iter().rev().collect());

    Ok(RpServiceEndpoint {
        method: method,
        path: path,
        comment: comment,
        returns: returns,
        accepts: accepts,
    })
}

impl<'input> IntoModel for ServiceBody<'input> {
    type Output = Rc<RpServiceBody>;

    fn into_model(self) -> Result<Rc<RpServiceBody>> {
        let mut endpoints: Vec<RpServiceEndpoint> = Vec::new();

        let mut queue: Vec<(Option<Rc<RefCell<Node>>>, Vec<ServiceNested>)> = Vec::new();
        queue.push((None, self.children));

        while let Some((parent, children)) = queue.pop() {
            for child in children {
                match child {
                    ServiceNested::Endpoint { method, path, comment, options, children } => {
                        let node = Rc::new(RefCell::new(Node {
                            parent: parent.as_ref().map(Clone::clone),
                            method: method.into_model()?,
                            path: path.into_model()?,
                            options: options.into_model()?,
                            comment: comment.into_iter().map(ToOwned::to_owned).collect(),
                            returns: Vec::new(),
                            accepts: Vec::new(),
                        }));

                        queue.push((Some(node.clone()), children));
                    }
                    // end node, manifest an endpoint.
                    ServiceNested::Returns { comment, ty, options } => {
                        let comment = comment.into_iter().map(ToOwned::to_owned).collect();
                        let returns = convert_return(comment, ty, options)?;

                        if let Some(parent) = parent.as_ref() {
                            parent.try_borrow_mut()?.push_returns(returns);
                        }
                    }
                    ServiceNested::Accepts { comment, ty, options } => {
                        let comment = comment.into_iter().map(ToOwned::to_owned).collect();
                        let accepts = convert_accepts(comment, ty, options)?;

                        if let Some(parent) = parent.as_ref() {
                            parent.try_borrow_mut()?.push_accepts(accepts);
                        }
                    }
                }
            }

            if let Some(ref parent) = parent {
                let p = parent.try_borrow()?;

                if p.method.is_some() {
                    endpoints.push(unwind(parent.clone())?);
                }
            }

            continue;
        }

        let endpoints = endpoints.into_iter().rev().collect();

        let service_body = RpServiceBody {
            name: self.name.to_owned(),
            comment: self.comment.into_iter().map(ToOwned::to_owned).collect(),
            endpoints: endpoints,
        };

        Ok(Rc::new(service_body))
    }
}
<commit_msg>parser: pick up root definition<commit_after>use std::cell::RefCell;
use std::rc::Rc;
use super::*;
use super::errors::*;

#[derive(Debug)]
pub struct ServiceBody<'input> {
    pub name: &'input str,
    pub comment: Vec<&'input str>,
    pub children: Vec<ServiceNested<'input>>,
}

struct Node {
    parent: Option<Rc<RefCell<Node>>>,
    method: Option<RpLoc<String>>,
    path: Option<RpLoc<RpPathSpec>>,
    options: Vec<RpLoc<RpOptionDecl>>,
    comment: Vec<String>,
    returns: Vec<RpServiceReturns>,
    accepts: Vec<RpServiceAccepts>,
}

impl Node {
    fn push_returns(&mut self, input: RpServiceReturns) {
        self.returns.push(input);
    }

    fn push_accepts(&mut self, input: RpServiceAccepts) {
        self.accepts.push(input);
    }
}

fn convert_return(comment: Vec<String>,
                  ty: RpLoc<RpType>,
                  options: Vec<RpLoc<OptionDecl>>)
                  -> Result<RpServiceReturns> {
    let options = Options::new(options.into_model()?);

    let produces: Option<RpLoc<String>> = options.find_one_string("produces")?;

    let produces = if let Some(produces) = produces {
        let (produces, pos) = produces.both();

        let produces = produces.parse()
            .chain_err(|| ErrorKind::Pos("not a valid mime type".to_owned(), pos.into()))?;

        Some(produces)
    } else {
        None
    };

    let status: Option<RpLoc<RpNumber>> = options.find_one_number("status")?;

    let status = if let Some(status) = status {
        let (status, pos) = status.both();

        let status = status.to_u32()
            .ok_or_else(|| ErrorKind::Pos("not a valid status".to_owned(), pos.into()))?;

        Some(status)
    } else {
        None
    };

    Ok(RpServiceReturns {
        comment: comment,
        ty: ty.into_model()?,
        produces: produces,
        status: status,
    })
}

fn convert_accepts(comment: Vec<String>,
                   ty: RpLoc<RpType>,
                   options: Vec<RpLoc<OptionDecl>>)
                   -> Result<RpServiceAccepts> {
    let options = Options::new(options.into_model()?);

    let accepts: Option<RpLoc<String>> = options.find_one_string("accept")?;

    let accepts = if let Some(accepts) = accepts {
        let (accepts, pos) = accepts.both();

        let accepts = accepts.parse()
            .chain_err(|| ErrorKind::Pos("not a valid mime type".to_owned(), pos.into()))?;

        Some(accepts)
    } else {
        None
    };

    Ok(RpServiceAccepts {
        comment: comment,
        ty: ty.into_model()?,
        accepts: accepts,
    })
}

/// Recursively unwind all inherited information about the given node, and convert to a service
/// endpoint.
fn unwind(node: Rc<RefCell<Node>>) -> Result<RpServiceEndpoint> {
    let mut method: Option<RpLoc<String>> = None;
    let mut path = Vec::new();
    let mut options: Vec<RpLoc<RpOptionDecl>> = Vec::new();
    let mut returns = Vec::new();
    let mut accepts = Vec::new();

    let comment = node.try_borrow()?.comment.clone();

    let mut current = Some(node);

    while let Some(step) = current {
        let next = step.try_borrow()?;

        // set method if not set
        method = method.or_else(|| next.method.clone());

        if let Some(ref next_url) = next.path {
            // correct order by extending in reverse
            path.extend(next_url.as_ref().segments.iter().rev().map(Clone::clone));
        }

        options.extend(next.options.iter().map(Clone::clone).rev());
        returns.extend(next.returns.iter().map(Clone::clone));
        accepts.extend(next.accepts.iter().map(Clone::clone));

        current = next.parent.clone();
    }

    let path = RpPathSpec { segments: path.into_iter().rev().collect() };

    let _options = Options::new(options.into_iter().rev().collect());

    Ok(RpServiceEndpoint {
        method: method,
        path: path,
        comment: comment,
        returns: returns,
        accepts: accepts,
    })
}

impl<'input> IntoModel for ServiceBody<'input> {
    type Output = Rc<RpServiceBody>;

    fn into_model(self) -> Result<Rc<RpServiceBody>> {
        let mut endpoints: Vec<RpServiceEndpoint> = Vec::new();

        // collecting root declarations
        let root = Rc::new(RefCell::new(Node {
            parent: None,
            method: None,
            path: None,
            options: Vec::new(),
            comment: Vec::new(),
            returns: Vec::new(),
            accepts: Vec::new(),
        }));

        let mut queue = Vec::new();
        queue.push((root, self.children));

        while let Some((parent, children)) = queue.pop() {
            for child in children {
                match child {
                    ServiceNested::Endpoint { method, path, comment, options, children } => {
                        let node = Rc::new(RefCell::new(Node {
                            parent: Some(parent.clone()),
                            method: method.into_model()?,
                            path: path.into_model()?,
                            options: options.into_model()?,
                            comment: comment.into_iter().map(ToOwned::to_owned).collect(),
                            returns: Vec::new(),
                            accepts: Vec::new(),
                        }));

                        queue.push((node, children));
                    }
                    // end node, manifest an endpoint.
                    ServiceNested::Returns { comment, ty, options } => {
                        let comment = comment.into_iter().map(ToOwned::to_owned).collect();
                        let returns = convert_return(comment, ty, options)?;
                        parent.try_borrow_mut()?.push_returns(returns);
                    }
                    ServiceNested::Accepts { comment, ty, options } => {
                        let comment = comment.into_iter().map(ToOwned::to_owned).collect();
                        let accepts = convert_accepts(comment, ty, options)?;
                        parent.try_borrow_mut()?.push_accepts(accepts);
                    }
                }
            }

            let p = parent.as_ref().try_borrow()?;

            if p.method.is_some() {
                endpoints.push(unwind(parent.clone())?);
            }
        }

        let endpoints = endpoints.into_iter().rev().collect();

        let service_body = RpServiceBody {
            name: self.name.to_owned(),
            comment: self.comment.into_iter().map(ToOwned::to_owned).collect(),
            endpoints: endpoints,
        };

        Ok(Rc::new(service_body))
    }
}
<|endoftext|>


<commit_before>//! Provides an interface to output sound to the user's speakers.
//!
//! It consists of two main types: `SoundData` is just raw sound data,
//! and a `Source` is a `SoundData` connected to a particular sound
//! channel.

use std::fmt;
use std::io;
use std::io::Read;
use std::path;

use std::sync::Arc;

use rodio;

use context::Context;
use GameError;
use GameResult;

/// A struct that contains all information for tracking sound info.
///
/// You generally don't have to create this yourself, it will be part
/// of your `Context` object.
pub struct AudioContext {
    endpoint: rodio::Endpoint,
}

impl AudioContext {
    /// Create new AudioContext.
    pub fn new() -> GameResult<AudioContext> {
        let error = GameError::AudioError(String::from(
            "Could not initialize sound system (for \
             some reason)",
        ));
        // This is deprecated in cpal but not in the most recent rodio (0.5.2) yet.
        #[allow(deprecated)]
        let e = rodio::get_default_endpoint().ok_or(error)?;
        Ok(AudioContext { endpoint: e })
    }
}

impl fmt::Debug for AudioContext {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "<AudioContext: {:p}>", self)
    }
}

/// Static sound data stored in memory.
/// It is Arc'ed, so cheap to clone.
#[derive(Clone, Debug)]
pub struct SoundData(Arc<Vec<u8>>);

impl SoundData {
    /// Copies the data in the given slice into a new SoundData object.
    pub fn from_bytes(data: &[u8]) -> Self {
        let mut buffer = Vec::with_capacity(data.len());
        buffer.extend(data);
        SoundData::from(buffer)
    }

    /// Creates a SoundData from any Read object; this involves
    /// copying it into a buffer.
    pub fn from_read<R>(reader: &mut R) -> GameResult<Self>
    where
        R: Read,
    {
        let mut buffer = Vec::new();
        reader.read_to_end(&mut buffer)?;

        Ok(SoundData::from(buffer))
    }
}

impl From<Vec<u8>> for SoundData {
    fn from(v: Vec<u8>) -> Self {
        SoundData(Arc::new(v))
    }
}

impl AsRef<[u8]> for SoundData {
    fn as_ref(&self) -> &[u8] {
        self.0.as_ref().as_ref()
    }
}

/// A source of audio data connected to a particular `Channel`.
/// Will stop playing when dropped.
// TODO: Check and see if this matches Love2d's semantics!
// Eventually it might read from a streaming decoder of some kind,
// but for now it is just an in-memory SoundData structure.
// The source of a rodio decoder must be Send, which something
// that contains a reference to a ZipFile is not, so we are going
// to just slurp all the data into memory for now.
// There's really a lot of work that needs to be done here, since
// rodio has gotten better (if still somewhat arcane) and our filesystem
// code has done the data-slurping-from-zip's for us
// but for now it works.
pub struct Source {
    data: io::Cursor<SoundData>,
    sink: rodio::Sink,
}

impl Source {
    /// Create a new Source from the given file.
    pub fn new<P: AsRef<path::Path>>(context: &mut Context, path: P) -> GameResult<Self> {
        let path = path.as_ref();
        let data = {
            let file = &mut context.filesystem.open(path)?;
            SoundData::from_read(file)?
        };
        Source::from_data(context, data)
    }

    /// Creates a new Source using the given SoundData object.
    pub fn from_data(context: &mut Context, data: SoundData) -> GameResult<Self> {
        let sink = rodio::Sink::new(&context.audio_context.endpoint);
        let cursor = io::Cursor::new(data);
        Ok(Source {
            data: cursor,
            sink: sink,
        })
    }

    /// Plays the Source.
    pub fn play(&self) -> GameResult<()> {
        // Creating a new Decoder each time seems a little messy,
        // since it may do checking and data-type detection that is
        // redundant, but it's not super expensive.
        // See https://github.com/ggez/ggez/issues/98 for discussion
        let cursor = self.data.clone();
        let decoder = rodio::Decoder::new(cursor)?;
        self.sink.append(decoder);
        Ok(())
    }

    /// Pauses playback
    pub fn pause(&self) {
        self.sink.pause()
    }

    /// Resumes playback
    pub fn resume(&self) {
        self.sink.play()
    }

    /// Stops playback
    pub fn stop(&self) {
        self.sink.stop()
    }

    /// Gets the current volume
    pub fn volume(&self) -> f32 {
        self.sink.volume()
    }

    /// Sets the current volume
    pub fn set_volume(&mut self, value: f32) {
        self.sink.set_volume(value)
    }

    /// Get whether or not the source is paused
    pub fn paused(&self) -> bool {
        self.sink.is_paused()
    }

    /// Get whether or not the source is playing
    pub fn playing(&self) -> bool {
        !self.paused() // && !self.stopped()
    }
}

impl fmt::Debug for Source {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "<Audio source: {:p}>", self)
    }
}

// TODO: global start, stop, volume?
<commit_msg>Use Vec::from in SoundData::from_bytes<commit_after>//! Provides an interface to output sound to the user's speakers.
//!
//! It consists of two main types: `SoundData` is just raw sound data,
//! and a `Source` is a `SoundData` connected to a particular sound
//! channel.

use std::fmt;
use std::io;
use std::io::Read;
use std::path;

use std::sync::Arc;

use rodio;

use context::Context;
use GameError;
use GameResult;

/// A struct that contains all information for tracking sound info.
///
/// You generally don't have to create this yourself, it will be part
/// of your `Context` object.
pub struct AudioContext {
    endpoint: rodio::Endpoint,
}

impl AudioContext {
    /// Create new AudioContext.
    pub fn new() -> GameResult<AudioContext> {
        let error = GameError::AudioError(String::from(
            "Could not initialize sound system (for \
             some reason)",
        ));
        // This is deprecated in cpal but not in the most recent rodio (0.5.2) yet.
        #[allow(deprecated)]
        let e = rodio::get_default_endpoint().ok_or(error)?;
        Ok(AudioContext { endpoint: e })
    }
}

impl fmt::Debug for AudioContext {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "<AudioContext: {:p}>", self)
    }
}

/// Static sound data stored in memory.
/// It is Arc'ed, so cheap to clone.
#[derive(Clone, Debug)]
pub struct SoundData(Arc<Vec<u8>>);

impl SoundData {
    /// Copies the data in the given slice into a new SoundData object.
    pub fn from_bytes(data: &[u8]) -> Self {
        SoundData::from(Vec::from(data))
    }

    /// Creates a SoundData from any Read object; this involves
    /// copying it into a buffer.
    pub fn from_read<R>(reader: &mut R) -> GameResult<Self>
    where
        R: Read,
    {
        let mut buffer = Vec::new();
        reader.read_to_end(&mut buffer)?;

        Ok(SoundData::from(buffer))
    }
}

impl From<Vec<u8>> for SoundData {
    fn from(v: Vec<u8>) -> Self {
        SoundData(Arc::new(v))
    }
}

impl AsRef<[u8]> for SoundData {
    fn as_ref(&self) -> &[u8] {
        self.0.as_ref().as_ref()
    }
}

/// A source of audio data connected to a particular `Channel`.
/// Will stop playing when dropped.
// TODO: Check and see if this matches Love2d's semantics!
// Eventually it might read from a streaming decoder of some kind,
// but for now it is just an in-memory SoundData structure.
// The source of a rodio decoder must be Send, which something
// that contains a reference to a ZipFile is not, so we are going
// to just slurp all the data into memory for now.
// There's really a lot of work that needs to be done here, since
// rodio has gotten better (if still somewhat arcane) and our filesystem
// code has done the data-slurping-from-zip's for us
// but for now it works.
pub struct Source {
    data: io::Cursor<SoundData>,
    sink: rodio::Sink,
}

impl Source {
    /// Create a new Source from the given file.
    pub fn new<P: AsRef<path::Path>>(context: &mut Context, path: P) -> GameResult<Self> {
        let path = path.as_ref();
        let data = {
            let file = &mut context.filesystem.open(path)?;
            SoundData::from_read(file)?
        };
        Source::from_data(context, data)
    }

    /// Creates a new Source using the given SoundData object.
    pub fn from_data(context: &mut Context, data: SoundData) -> GameResult<Self> {
        let sink = rodio::Sink::new(&context.audio_context.endpoint);
        let cursor = io::Cursor::new(data);
        Ok(Source {
            data: cursor,
            sink: sink,
        })
    }

    /// Plays the Source.
    pub fn play(&self) -> GameResult<()> {
        // Creating a new Decoder each time seems a little messy,
        // since it may do checking and data-type detection that is
        // redundant, but it's not super expensive.
        // See https://github.com/ggez/ggez/issues/98 for discussion
        let cursor = self.data.clone();
        let decoder = rodio::Decoder::new(cursor)?;
        self.sink.append(decoder);
        Ok(())
    }

    /// Pauses playback
    pub fn pause(&self) {
        self.sink.pause()
    }

    /// Resumes playback
    pub fn resume(&self) {
        self.sink.play()
    }

    /// Stops playback
    pub fn stop(&self) {
        self.sink.stop()
    }

    /// Gets the current volume
    pub fn volume(&self) -> f32 {
        self.sink.volume()
    }

    /// Sets the current volume
    pub fn set_volume(&mut self, value: f32) {
        self.sink.set_volume(value)
    }

    /// Get whether or not the source is paused
    pub fn paused(&self) -> bool {
        self.sink.is_paused()
    }

    /// Get whether or not the source is playing
    pub fn playing(&self) -> bool {
        !self.paused() // && !self.stopped()
    }
}

impl fmt::Debug for Source {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "<Audio source: {:p}>", self)
    }
}

// TODO: global start, stop, volume?
<|endoftext|>


<commit_before>//! Event handling (mouse, keyboard, controller, touch screen, etc.)
//!
//! See [`Event`](enum.Event.html) for more information.
//!
//! # Unstable
//!
//! There are still many unanswered questions about the design of the events API in the turtle
//! crate. This module may change or be completely removed in the future. There will definitely
//! be *some* events API in the future, but it may end up looking different than it does today.

use serde::{Serialize, Deserialize};
use glutin::{
    dpi::{LogicalSize, PhysicalPosition},
    event::{self as glutin_event, WindowEvent, KeyboardInput},
};

use crate::Point;

/// Possible events returned from [`Drawing::poll_event()`](../struct.Drawing.html#method.poll_event).
///
/// Events are used to make programs more interactive. See that method's documentation for more
/// information about how to use events.
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Event {
    /// Sent when a keyboard key is pressed or released
    Key(Key, PressedState),

    /// Sent when a mouse button is pressed or released
    MouseButton(MouseButton, PressedState),

    /// Sent when the mouse is moving. Only sent when the mouse is over the window.
    /// `x` and `y` represent the new coordinates of where the mouse is currently.
    ///
    /// Coordinates are relative to the center of the window.
    MouseMove(Point),

    /// Sent when the mouse is scrolled. Only sent when the mouse is over the window.
    /// `x` and `y` are in scroll ticks.
    MouseScroll { x: f64, y: f64 },

    /// Sent when the window gets resized
    WindowResized { width: u32, height: u32 },

    /// Sent when the window focus changes
    ///
    /// The boolean value is true if the window is in focus.
    WindowFocused(bool),
    /// Sent when the cursor enters or leaves the window
    ///
    /// The boolean value is true if the cursor entered the window, and false if it left.
    WindowCursor(bool),

    /// Sent when the window is closed
    WindowClosed,
}

impl Event {
    /// Returns `None` if the input event is not a supported variant of `Event`
    #[cfg_attr(any(feature = "test", test), allow(dead_code))]
    pub(crate) fn from_window_event(
        event: WindowEvent,
        scale_factor: f64,
        to_logical: impl FnOnce(PhysicalPosition<f64>) -> Point,
    ) -> Option<Self> {
        match event {
            WindowEvent::Resized(size) => {
                let LogicalSize {width, height} = size.to_logical(scale_factor);
                Some(Event::WindowResized {width, height})
            },

            WindowEvent::KeyboardInput {input: KeyboardInput {state, virtual_keycode, ..}, ..} => {
                Some(Event::Key(
                    Key::from_keycode(virtual_keycode?)?,
                    PressedState::from_state(state),
                ))
            },
            WindowEvent::CursorEntered {..} => Some(Event::WindowCursor(true)),
            WindowEvent::CursorLeft {..} => Some(Event::WindowCursor(false)),
            WindowEvent::CursorMoved {position, ..} => {
                Some(Event::MouseMove(to_logical(position)))
            },
            WindowEvent::MouseInput {state, button, ..} => Some(Event::MouseButton(
                MouseButton::from_button(button)?,
                PressedState::from_state(state),
            )),
            WindowEvent::Focused(focused) => Some(Event::WindowFocused(focused)),
            WindowEvent::Destroyed => Some(Event::WindowClosed),

            WindowEvent::Moved(_) |
            WindowEvent::CloseRequested |
            WindowEvent::DroppedFile(_) |
            WindowEvent::HoveredFile(_) |
            WindowEvent::HoveredFileCancelled |
            WindowEvent::ReceivedCharacter(_) |
            WindowEvent::ModifiersChanged(_) |
            WindowEvent::MouseWheel {..} |
            WindowEvent::TouchpadPressure {..} |
            WindowEvent::AxisMotion {..} |
            WindowEvent::Touch(_) |
            WindowEvent::ScaleFactorChanged {..} |
            WindowEvent::ThemeChanged(_) => None, // Not supported
        }
    }
}

//TODO: Documentation
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub enum PressedState {
    Pressed,
    Released,
}

impl PressedState {
    #[cfg_attr(any(feature = "test", test), allow(dead_code))]
    fn from_state(state: glutin_event::ElementState) -> PressedState {
        match state {
            glutin_event::ElementState::Pressed => PressedState::Pressed,
            glutin_event::ElementState::Released => PressedState::Released,
        }
    }
}

//TODO: Documentation
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub enum Key {
    /// The '1' key over the letters.
    Num1,
    /// The '2' key over the letters.
    Num2,
    /// The '3' key over the letters.
    Num3,
    /// The '4' key over the letters.
    Num4,
    /// The '5' key over the letters.
    Num5,
    /// The '6' key over the letters.
    Num6,
    /// The '7' key over the letters.
    Num7,
    /// The '8' key over the letters.
    Num8,
    /// The '9' key over the letters.
    Num9,
    /// The '0' key over the letters.
    Num0,

    A,
    B,
    C,
    D,
    E,
    F,
    G,
    H,
    I,
    J,
    K,
    L,
    M,
    N,
    O,
    P,
    Q,
    R,
    S,
    T,
    U,
    V,
    W,
    X,
    Y,
    Z,

    /// The Escape key, next to F1
    Esc,

    F1,
    F2,
    F3,
    F4,
    F5,
    F6,
    F7,
    F8,
    F9,
    F10,
    F11,
    F12,
    F13,
    F14,
    F15,
    F16,
    F17,
    F18,
    F19,
    F20,
    F21,
    F22,
    F23,
    F24,

    Home,
    Delete,
    End,
    /// The PageDown (PgDn) key
    PageDown,
    /// The PageUp (PgUp) key
    PageUp,
    /// The backspace key, right over Enter/Return
    Backspace,
    /// The Enter/Return key, under Backspace
    Return,
    /// The spacebar key
    Space,

    /// The up arrow key
    UpArrow,
    /// The left arrow key
    LeftArrow,
    /// The right arrow key
    RightArrow,
    /// The down arrow key
    DownArrow,

    Numpad0,
    Numpad1,
    Numpad2,
    Numpad3,
    Numpad4,
    Numpad5,
    Numpad6,
    Numpad7,
    Numpad8,
    Numpad9,
    NumpadComma,
    NumpadEnter,
    NumpadEquals,

    Apostrophe,
    At,
    Backslash,
    Backtick,
    Colon,
    Comma,
    Decimal,
    Divide,
    Equals,
    Minus,
    Multiply,
    Period,
    Plus,
    /// The left bracket `[` key
    LeftBracket,
    /// The left bracket `]` key
    RightBracket,
    Semicolon,
    Slash,
    Tab,
}

impl Key {
    #[cfg_attr(any(feature = "test", test), allow(dead_code))]
    fn from_keycode(key: glutin_event::VirtualKeyCode) -> Option<Self> {
        use glutin_event::VirtualKeyCode::*;
        Some(match key {
            Key1 => Key::Num1,
            Key2 => Key::Num2,
            Key3 => Key::Num3,
            Key4 => Key::Num4,
            Key5 => Key::Num5,
            Key6 => Key::Num6,
            Key7 => Key::Num7,
            Key8 => Key::Num8,
            Key9 => Key::Num9,
            Key0 => Key::Num0,

            A => Key::A,
            B => Key::B,
            C => Key::C,
            D => Key::D,
            E => Key::E,
            F => Key::F,
            G => Key::G,
            H => Key::H,
            I => Key::I,
            J => Key::J,
            K => Key::K,
            L => Key::L,
            M => Key::M,
            N => Key::N,
            O => Key::O,
            P => Key::P,
            Q => Key::Q,
            R => Key::R,
            S => Key::S,
            T => Key::T,
            U => Key::U,
            V => Key::V,
            W => Key::W,
            X => Key::X,
            Y => Key::Y,
            Z => Key::Z,

            Escape => Key::Esc,

            F1 => Key::F1,
            F2 => Key::F2,
            F3 => Key::F3,
            F4 => Key::F4,
            F5 => Key::F5,
            F6 => Key::F6,
            F7 => Key::F7,
            F8 => Key::F8,
            F9 => Key::F9,
            F10 => Key::F10,
            F11 => Key::F11,
            F12 => Key::F12,
            F13 => Key::F13,
            F14 => Key::F14,
            F15 => Key::F15,
            F16 => Key::F16,
            F17 => Key::F17,
            F18 => Key::F18,
            F19 => Key::F19,
            F20 => Key::F20,
            F21 => Key::F21,
            F22 => Key::F22,
            F23 => Key::F23,
            F24 => Key::F24,

            Home => Key::Home,
            Delete => Key::Delete,
            End => Key::End,
            PageDown => Key::PageDown,
            PageUp => Key::PageUp,
            Back => Key::Backspace,
            Return => Key::Return,
            Space => Key::Space,

            Left => Key::LeftArrow,
            Up => Key::UpArrow,
            Right => Key::RightArrow,
            Down => Key::DownArrow,

            Numpad0 => Key::Numpad0,
            Numpad1 => Key::Numpad1,
            Numpad2 => Key::Numpad2,
            Numpad3 => Key::Numpad3,
            Numpad4 => Key::Numpad4,
            Numpad5 => Key::Numpad5,
            Numpad6 => Key::Numpad6,
            Numpad7 => Key::Numpad7,
            Numpad8 => Key::Numpad8,
            Numpad9 => Key::Numpad9,

            Add => Key::Plus,
            Apostrophe => Key::Apostrophe,
            At => Key::At,
            Backslash => Key::Backslash,
            Colon => Key::Colon,
            Comma => Key::Comma,
            Decimal => Key::Decimal,
            Divide => Key::Divide,
            Equals => Key::Equals,
            Grave => Key::Backtick,
            LBracket => Key::LeftBracket,
            Minus => Key::Minus,
            Multiply => Key::Multiply,
            NumpadComma => Key::NumpadComma,
            NumpadEnter => Key::NumpadEnter,
            NumpadEquals => Key::NumpadEquals,
            Period => Key::Period,
            RBracket => Key::RightBracket,
            Semicolon => Key::Semicolon,
            Slash => Key::Slash,
            Subtract => Key::Minus,
            Tab => Key::Tab,

            // Unsupported keys (could be changed in the future)
            Snapshot |
            Scroll |
            Pause |
            Insert |
            Compose |
            Caret |
            Numlock |
            AbntC1 |
            AbntC2 |
            Apps |
            Ax |
            Calculator |
            Capital |
            Convert |
            Kana |
            Kanji |
            LAlt |
            LControl |
            LShift |
            LWin |
            Mail |
            MediaSelect |
            MediaStop |
            Mute |
            MyComputer |
            NavigateForward |
            NavigateBackward |
            NextTrack |
            NoConvert |
            OEM102 |
            PlayPause |
            Power |
            PrevTrack |
            RAlt |
            RControl |
            RShift |
            RWin |
            Sleep |
            Stop |
            Sysrq |
            Underline |
            Unlabeled |
            VolumeDown |
            VolumeUp |
            Wake |
            WebBack |
            WebFavorites |
            WebForward |
            WebHome |
            WebRefresh |
            WebSearch |
            WebStop |
            Yen |
            Copy |
            Paste |
            Cut => return None,
        })
    }
}

//TODO: Documentation
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub enum MouseButton {
    /// The left mouse button
    LeftButton,
    /// The middle mouse button
    MiddleButton,
    /// The right mouse button
    RightButton,
}

impl MouseButton {
    #[cfg_attr(any(feature = "test", test), allow(dead_code))]
    fn from_button(button: glutin_event::MouseButton) -> Option<Self> {
        use glutin_event::MouseButton::*;
        match button {
            Left => Some(MouseButton::LeftButton),
            Middle => Some(MouseButton::MiddleButton),
            Right => Some(MouseButton::RightButton),
            Other(_) => None,
        }
    }
}
<commit_msg>Fix #205: update to latest glutin version<commit_after>//! Event handling (mouse, keyboard, controller, touch screen, etc.)
//!
//! See [`Event`](enum.Event.html) for more information.
//!
//! # Unstable
//!
//! There are still many unanswered questions about the design of the events API in the turtle
//! crate. This module may change or be completely removed in the future. There will definitely
//! be *some* events API in the future, but it may end up looking different than it does today.

use serde::{Serialize, Deserialize};
use glutin::{
    dpi::{LogicalSize, PhysicalPosition},
    event::{self as glutin_event, ElementState, KeyboardInput, VirtualKeyCode, WindowEvent},
};

use crate::Point;

/// Possible events returned from [`Drawing::poll_event()`](../struct.Drawing.html#method.poll_event).
///
/// Events are used to make programs more interactive. See that method's documentation for more
/// information about how to use events.
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Event {
    /// Sent when a keyboard key is pressed or released
    Key(Key, PressedState),

    /// Sent when a mouse button is pressed or released
    MouseButton(MouseButton, PressedState),

    /// Sent when the mouse is moving. Only sent when the mouse is over the window.
    /// `x` and `y` represent the new coordinates of where the mouse is currently.
    ///
    /// Coordinates are relative to the center of the window.
    MouseMove(Point),

    /// Sent when the mouse is scrolled. Only sent when the mouse is over the window.
    /// `x` and `y` are in scroll ticks.
    MouseScroll { x: f64, y: f64 },

    /// Sent when the window gets resized
    WindowResized { width: u32, height: u32 },

    /// Sent when the window focus changes
    ///
    /// The boolean value is true if the window is in focus.
    WindowFocused(bool),
    /// Sent when the cursor enters or leaves the window
    ///
    /// The boolean value is true if the cursor entered the window, and false if it left.
    WindowCursor(bool),

    /// Sent when the window is closed
    WindowClosed,
}

impl Event {
    /// Returns `None` if the input event is not a supported variant of `Event`
    #[cfg_attr(any(feature = "test", test), allow(dead_code))]
    pub(crate) fn from_window_event(
        event: WindowEvent,
        scale_factor: f64,
        to_logical: impl FnOnce(PhysicalPosition<f64>) -> Point,
    ) -> Option<Self> {
        match event {
            WindowEvent::Resized(size) => {
                let LogicalSize {width, height} = size.to_logical(scale_factor);
                Some(Event::WindowResized {width, height})
            },

            WindowEvent::KeyboardInput {input: KeyboardInput {state, virtual_keycode, ..}, ..} => {
                Some(Event::Key(
                    Key::from_keycode(virtual_keycode?)?,
                    PressedState::from_state(state),
                ))
            },
            WindowEvent::CursorEntered {..} => Some(Event::WindowCursor(true)),
            WindowEvent::CursorLeft {..} => Some(Event::WindowCursor(false)),
            WindowEvent::CursorMoved {position, ..} => {
                Some(Event::MouseMove(to_logical(position)))
            },
            WindowEvent::MouseInput {state, button, ..} => Some(Event::MouseButton(
                MouseButton::from_button(button)?,
                PressedState::from_state(state),
            )),
            WindowEvent::Focused(focused) => Some(Event::WindowFocused(focused)),
            WindowEvent::Destroyed => Some(Event::WindowClosed),

            WindowEvent::Moved(_) |
            WindowEvent::CloseRequested |
            WindowEvent::DroppedFile(_) |
            WindowEvent::HoveredFile(_) |
            WindowEvent::HoveredFileCancelled |
            WindowEvent::ReceivedCharacter(_) |
            WindowEvent::ModifiersChanged(_) |
            WindowEvent::MouseWheel {..} |
            WindowEvent::TouchpadPressure {..} |
            WindowEvent::AxisMotion {..} |
            WindowEvent::Touch(_) |
            WindowEvent::ScaleFactorChanged {..} |
            WindowEvent::ThemeChanged(_) => None, // Not supported
        }
    }
}

//TODO: Documentation
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub enum PressedState {
    Pressed,
    Released,
}

impl PressedState {
    #[cfg_attr(any(feature = "test", test), allow(dead_code))]
    fn from_state(state: ElementState) -> PressedState {
        match state {
            ElementState::Pressed => PressedState::Pressed,
            ElementState::Released => PressedState::Released,
        }
    }
}

//TODO: Documentation
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub enum Key {
    /// The '1' key over the letters.
    Num1,
    /// The '2' key over the letters.
    Num2,
    /// The '3' key over the letters.
    Num3,
    /// The '4' key over the letters.
    Num4,
    /// The '5' key over the letters.
    Num5,
    /// The '6' key over the letters.
    Num6,
    /// The '7' key over the letters.
    Num7,
    /// The '8' key over the letters.
    Num8,
    /// The '9' key over the letters.
    Num9,
    /// The '0' key over the letters.
    Num0,

    A,
    B,
    C,
    D,
    E,
    F,
    G,
    H,
    I,
    J,
    K,
    L,
    M,
    N,
    O,
    P,
    Q,
    R,
    S,
    T,
    U,
    V,
    W,
    X,
    Y,
    Z,

    /// The Escape key, next to F1
    Esc,

    F1,
    F2,
    F3,
    F4,
    F5,
    F6,
    F7,
    F8,
    F9,
    F10,
    F11,
    F12,
    F13,
    F14,
    F15,
    F16,
    F17,
    F18,
    F19,
    F20,
    F21,
    F22,
    F23,
    F24,

    Home,
    Delete,
    End,
    /// The PageDown (PgDn) key
    PageDown,
    /// The PageUp (PgUp) key
    PageUp,
    /// The backspace key, right over Enter/Return
    Backspace,
    /// The Enter/Return key, under Backspace
    Return,
    /// The spacebar key
    Space,

    /// The up arrow key
    UpArrow,
    /// The left arrow key
    LeftArrow,
    /// The right arrow key
    RightArrow,
    /// The down arrow key
    DownArrow,

    Numpad0,
    Numpad1,
    Numpad2,
    Numpad3,
    Numpad4,
    Numpad5,
    Numpad6,
    Numpad7,
    Numpad8,
    Numpad9,
    NumpadComma,
    NumpadEnter,
    NumpadEquals,

    Apostrophe,
    At,
    Backslash,
    Backtick,
    Colon,
    Comma,
    Decimal,
    Divide,
    Equals,
    Minus,
    Multiply,
    Period,
    Plus,
    /// The left bracket `[` key
    LeftBracket,
    /// The left bracket `]` key
    RightBracket,
    Semicolon,
    Slash,
    Tab,
}

impl Key {
    #[cfg_attr(any(feature = "test", test), allow(dead_code))]
    fn from_keycode(key: VirtualKeyCode) -> Option<Self> {
        Some(match key {
            VirtualKeyCode::Key1 => Key::Num1,
            VirtualKeyCode::Key2 => Key::Num2,
            VirtualKeyCode::Key3 => Key::Num3,
            VirtualKeyCode::Key4 => Key::Num4,
            VirtualKeyCode::Key5 => Key::Num5,
            VirtualKeyCode::Key6 => Key::Num6,
            VirtualKeyCode::Key7 => Key::Num7,
            VirtualKeyCode::Key8 => Key::Num8,
            VirtualKeyCode::Key9 => Key::Num9,
            VirtualKeyCode::Key0 => Key::Num0,

            VirtualKeyCode::A => Key::A,
            VirtualKeyCode::B => Key::B,
            VirtualKeyCode::C => Key::C,
            VirtualKeyCode::D => Key::D,
            VirtualKeyCode::E => Key::E,
            VirtualKeyCode::F => Key::F,
            VirtualKeyCode::G => Key::G,
            VirtualKeyCode::H => Key::H,
            VirtualKeyCode::I => Key::I,
            VirtualKeyCode::J => Key::J,
            VirtualKeyCode::K => Key::K,
            VirtualKeyCode::L => Key::L,
            VirtualKeyCode::M => Key::M,
            VirtualKeyCode::N => Key::N,
            VirtualKeyCode::O => Key::O,
            VirtualKeyCode::P => Key::P,
            VirtualKeyCode::Q => Key::Q,
            VirtualKeyCode::R => Key::R,
            VirtualKeyCode::S => Key::S,
            VirtualKeyCode::T => Key::T,
            VirtualKeyCode::U => Key::U,
            VirtualKeyCode::V => Key::V,
            VirtualKeyCode::W => Key::W,
            VirtualKeyCode::X => Key::X,
            VirtualKeyCode::Y => Key::Y,
            VirtualKeyCode::Z => Key::Z,

            VirtualKeyCode::Escape => Key::Esc,

            VirtualKeyCode::F1 => Key::F1,
            VirtualKeyCode::F2 => Key::F2,
            VirtualKeyCode::F3 => Key::F3,
            VirtualKeyCode::F4 => Key::F4,
            VirtualKeyCode::F5 => Key::F5,
            VirtualKeyCode::F6 => Key::F6,
            VirtualKeyCode::F7 => Key::F7,
            VirtualKeyCode::F8 => Key::F8,
            VirtualKeyCode::F9 => Key::F9,
            VirtualKeyCode::F10 => Key::F10,
            VirtualKeyCode::F11 => Key::F11,
            VirtualKeyCode::F12 => Key::F12,
            VirtualKeyCode::F13 => Key::F13,
            VirtualKeyCode::F14 => Key::F14,
            VirtualKeyCode::F15 => Key::F15,
            VirtualKeyCode::F16 => Key::F16,
            VirtualKeyCode::F17 => Key::F17,
            VirtualKeyCode::F18 => Key::F18,
            VirtualKeyCode::F19 => Key::F19,
            VirtualKeyCode::F20 => Key::F20,
            VirtualKeyCode::F21 => Key::F21,
            VirtualKeyCode::F22 => Key::F22,
            VirtualKeyCode::F23 => Key::F23,
            VirtualKeyCode::F24 => Key::F24,

            VirtualKeyCode::Home => Key::Home,
            VirtualKeyCode::Delete => Key::Delete,
            VirtualKeyCode::End => Key::End,
            VirtualKeyCode::PageDown => Key::PageDown,
            VirtualKeyCode::PageUp => Key::PageUp,
            VirtualKeyCode::Back => Key::Backspace,
            VirtualKeyCode::Return => Key::Return,
            VirtualKeyCode::Space => Key::Space,

            VirtualKeyCode::Left => Key::LeftArrow,
            VirtualKeyCode::Up => Key::UpArrow,
            VirtualKeyCode::Right => Key::RightArrow,
            VirtualKeyCode::Down => Key::DownArrow,

            VirtualKeyCode::Numpad0 => Key::Numpad0,
            VirtualKeyCode::Numpad1 => Key::Numpad1,
            VirtualKeyCode::Numpad2 => Key::Numpad2,
            VirtualKeyCode::Numpad3 => Key::Numpad3,
            VirtualKeyCode::Numpad4 => Key::Numpad4,
            VirtualKeyCode::Numpad5 => Key::Numpad5,
            VirtualKeyCode::Numpad6 => Key::Numpad6,
            VirtualKeyCode::Numpad7 => Key::Numpad7,
            VirtualKeyCode::Numpad8 => Key::Numpad8,
            VirtualKeyCode::Numpad9 => Key::Numpad9,

            VirtualKeyCode::Apostrophe => Key::Apostrophe,
            VirtualKeyCode::At => Key::At,
            VirtualKeyCode::Backslash => Key::Backslash,
            VirtualKeyCode::Colon => Key::Colon,
            VirtualKeyCode::Comma => Key::Comma,
            VirtualKeyCode::Equals => Key::Equals,
            VirtualKeyCode::Grave => Key::Backtick,
            VirtualKeyCode::LBracket => Key::LeftBracket,
            VirtualKeyCode::Minus => Key::Minus,
            VirtualKeyCode::NumpadAdd => Key::Plus,
            VirtualKeyCode::NumpadComma => Key::NumpadComma,
            VirtualKeyCode::NumpadDecimal => Key::Decimal,
            VirtualKeyCode::NumpadDivide => Key::Divide,
            VirtualKeyCode::NumpadEnter => Key::NumpadEnter,
            VirtualKeyCode::NumpadEquals => Key::NumpadEquals,
            VirtualKeyCode::NumpadMultiply => Key::Multiply,
            VirtualKeyCode::NumpadSubtract => Key::Minus,
            VirtualKeyCode::Period => Key::Period,
            VirtualKeyCode::RBracket => Key::RightBracket,
            VirtualKeyCode::Semicolon => Key::Semicolon,
            VirtualKeyCode::Slash => Key::Slash,
            VirtualKeyCode::Tab => Key::Tab,

            // Unsupported keys (could be changed in the future)
            _ => return None,
        })
    }
}

//TODO: Documentation
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub enum MouseButton {
    /// The left mouse button
    LeftButton,
    /// The middle mouse button
    MiddleButton,
    /// The right mouse button
    RightButton,
}

impl MouseButton {
    #[cfg_attr(any(feature = "test", test), allow(dead_code))]
    fn from_button(button: glutin_event::MouseButton) -> Option<Self> {
        match button {
            glutin_event::MouseButton::Left => Some(MouseButton::LeftButton),
            glutin_event::MouseButton::Middle => Some(MouseButton::MiddleButton),
            glutin_event::MouseButton::Right => Some(MouseButton::RightButton),
            glutin_event::MouseButton::Other(_) => None,
        }
    }
}
<|endoftext|>


<commit_before>// SPDX-License-Identifier: MIT
//
// Copyright (C) 2022 Jason Ish

use crate::config::{Config, SpoolConfig};
use anyhow::{anyhow, bail};
use axum::body::StreamBody;
use axum::extract::{Form, Query};
use axum::http::{HeaderMap, HeaderValue, StatusCode};
use axum::response::{IntoResponse, Response};
use axum::Extension;
use serde::Deserialize;
use std::ops::Add;
use std::ops::Sub;
use std::process::Stdio;
use thiserror::Error;
use time::format_description::well_known::Rfc3339;
use time::{Duration, OffsetDateTime};
use tokio::io::AsyncBufReadExt;
use tokio::io::AsyncReadExt;
use tokio::io::BufReader;
use tokio_stream::wrappers::UnboundedReceiverStream;
use tracing::{error, info};

const DEFAULT_DURATION: &str = "1m";

#[derive(Deserialize, Debug, Clone)]
#[allow(dead_code)]
pub struct FetchRequest {
    #[serde(rename = "query-type")]
    query_type: String,
    filter: Option<String>,
    #[serde(rename = "start-time")]
    start_time: Option<String>,
    duration: Option<String>,
    event: Option<String>,
    #[serde(rename = "duration-before")]
    duration_before: Option<String>,
    #[serde(rename = "duration-after")]
    duration_after: Option<String>,
    spool: String,
}

#[derive(Debug, Error)]
#[error("{reason:?}")]
pub struct BadRequest {
    reason: String,
}

impl BadRequest {
    fn new<S: AsRef<str>>(reason: S) -> Self {
        Self {
            reason: reason.as_ref().to_string(),
        }
    }
}

impl IntoResponse for BadRequest {
    fn into_response(self) -> Response {
        (StatusCode::BAD_REQUEST, self.reason).into_response()
    }
}

fn get_spool_config<'a>(config: &'a Config, name: &str) -> Option<&'a SpoolConfig> {
    for spool in &config.spools {
        if spool.name == name {
            return Some(spool);
        }
    }
    None
}

pub async fn fetch_get(
    Extension(config): Extension<Config>,
    Query(params): Query<FetchRequest>,
) -> Result<impl IntoResponse, BadRequest> {
    fetch(config, params).await
}

pub async fn fetch_post(
    Extension(config): Extension<Config>,
    Form(params): Form<FetchRequest>,
) -> Result<impl IntoResponse, BadRequest> {
    fetch(config, params).await
}

pub async fn fetch(config: Config, params: FetchRequest) -> Result<impl IntoResponse, BadRequest> {
    let spool_config = get_spool_config(&config, &params.spool).ok_or_else(|| {
        error!("No spool with the name: {}", &params.spool);
        BadRequest::new(&format!("invalid spool name: {}", &params.spool))
    })?;

    let start_time;
    let duration;
    let filter;
    let filename;

    match params.query_type.as_ref() {
        "filter" => {
            let stime = match params.start_time.as_ref() {
                Some(stime) => stime,
                None => {
                    return Err(BadRequest::new("start-time is required"));
                }
            };

            start_time = OffsetDateTime::parse(stime, &Rfc3339).map_err(|err| {
                error!("Invalid start-time: {} -- {:?}", &stime, err);
                BadRequest::new("invalid start-time")
            })?;
            let pduration = params.duration.as_deref().unwrap_or(DEFAULT_DURATION);
            duration = parse_duration(pduration).map_err(|err| {
                error!("Invalid duration: {} -- {:?}", &pduration, err);
                BadRequest::new("invalid duration")
            })?;
            filter = params.clone().filter.unwrap();
            filename = format!("{}.pcap", start_time.unix_timestamp());
        }
        "event" => {
            if let Some(event) = &params.event {
                let event: EveEvent = serde_json::from_str(event).map_err(|err| {
                    error!("Failed to decode event: {:?} -- event={}", err, &event);
                    BadRequest::new("bad event")
                })?;

                (start_time, duration) = parse_event_timeframe(&event, &params)?;

                if event.src_port.is_some() && event.dest_port.is_some() {
                    filter = format!(
                        "{} and ((host {} and port {}) and (host {} and port {}))",
                        &event.proto.to_lowercase(),
                        &event.src_ip,
                        event.src_port.as_ref().unwrap(),
                        &event.dest_ip,
                        event.dest_port.as_ref().unwrap(),
                    );
                } else {
                    filter = format!(
                        "{} and host {} and host {}",
                        &event.proto, &event.src_ip, &event.dest_ip
                    );
                }

                // Create a filename based on event details.
                let signature_id = event.alert.map(|a| a.signature_id).unwrap_or(0);
                filename = format!(
                    "{}-{}-{}-{}-{}-{}.pcap",
                    signature_id,
                    start_time.unix_timestamp(),
                    &event.src_ip,
                    event.src_port.unwrap_or(0),
                    &event.dest_ip,
                    event.dest_port.unwrap_or(0)
                );
            } else {
                return Err(BadRequest::new("no event provided for event query"));
            }
        }
        _ => {
            error!("Invalid query type: {}", &params.query_type);
            return Err(BadRequest::new("bad query type"));
        }
    }

    let process_name = std::env::args().next().as_ref().unwrap().to_string();
    let mut command = tokio::process::Command::new(process_name);
    command
        .arg("export")
        .arg("--json")
        .arg("-v")
        .arg("--directory")
        .arg(&spool_config.directory)
        .arg("--filter")
        .arg(filter)
        .arg("--start-time")
        .arg(format!("{}", start_time.unix_timestamp()))
        .arg("--duration")
        .arg(format!("{}", duration.as_seconds_f64() as i64))
        .arg("--output")
        .arg("-");
    if let Some(prefix) = &spool_config.prefix {
        command.arg("--prefix").arg(prefix);
    }
    command.stdout(Stdio::piped());
    command.stderr(Stdio::piped());
    let mut child = command.spawn().unwrap();
    let mut stdout = child.stdout.take().unwrap();
    let stderr = child.stderr.take().unwrap();

    let (tx, rx) =
        tokio::sync::mpsc::unbounded_channel::<std::result::Result<Vec<u8>, std::io::Error>>();
    let rx = UnboundedReceiverStream::new(rx);
    let body = StreamBody::new(rx);
    let mut stderr_reader = BufReader::new(stderr).lines();
    let (wait_tx, wait_rx) = tokio::sync::oneshot::channel::<&str>();

    tokio::spawn(async move {
        let mut bytes = 0;
        let mut wait_tx = Some(wait_tx);
        let mut client_closed = false;
        loop {
            let mut buf = Vec::with_capacity(8192);
            tokio::select! {
                _ = child.wait() => {
                    break;
                }
                _ = tx.closed() => {
                    client_closed = true;
                    break;
                }
                closed = read_stderr(&mut stderr_reader) => {
                    if closed {
                        break;
                    }
                }
                x = stdout.read_buf(&mut buf) => {
                    match x {
                        Ok(n) => {
                            if n == 0 {
                                break;
                            } else {
                                if let Some(wait_tx) = wait_tx.take() {
                                    wait_tx.send("ok").unwrap();
                                }
                                if tx.send(Ok(buf)).is_err() {
                                    error!("Failed to write to body stream, client must have closed connection");
                                    client_closed = true;
                                    break;
                                }
                                bytes += n;
                            }
                        }
                        Err(err) => {
                            error!("Error reading export process stdout: {:?}", err);
                            break;
                        }
                    }
                }
            }
        }

        if client_closed {
            let _ = child.start_kill();
        }
        let status = child.wait().await.unwrap();
        if status.success() {
            info!(
                "Export process exited successfully, bytes written: {}",
                bytes
            );
        } else {
            error!("Export process exited with error code: {:?}", status);
        }
        if let Some(wait_tx) = wait_tx.take() {
            if status.success() {
                wait_tx.send("nopkt").unwrap();
            } else {
                wait_tx.send("err").unwrap();
            }
        }
    });

    Ok(match wait_rx.await {
        Err(err) => {
            error!("Error on wait channel: {:?}", err);
            (
                StatusCode::INTERNAL_SERVER_ERROR,
                "An error occurred. See server logs for details",
            )
                .into_response()
        }
        Ok(status) => {
            if status == "ok" {
                let mut headers = HeaderMap::new();
                headers.insert(
                    axum::http::header::CONTENT_TYPE,
                    HeaderValue::from_str("application/vnd.tcpdump.pcap").unwrap(),
                );
                headers.insert(
                    axum::http::header::CONTENT_DISPOSITION,
                    HeaderValue::from_str(&format!("attachment; filename={}", filename)).unwrap(),
                );

                (headers, body).into_response()
            } else if status == "nopkt" {
                (StatusCode::NOT_FOUND, "No packets found.").into_response()
            } else {
                (
                    StatusCode::INTERNAL_SERVER_ERROR,
                    "An error occurred. See server logs for details",
                )
                    .into_response()
            }
        }
    })
}

async fn read_stderr(
    reader: &mut tokio::io::Lines<tokio::io::BufReader<tokio::process::ChildStderr>>,
) -> bool {
    match reader.next_line().await {
        Ok(Some(next)) => {
            if let Ok(v) = serde_json::from_str::<serde_json::Value>(&next) {
                let level = v["level"].as_str().unwrap_or("INFO");
                if let Some(message) = v["fields"]["message"].as_str() {
                    match level {
                        "ERROR" => error!("{}", message),
                        _ => info!("{}", message),
                    }
                    return false;
                }
            }
            error!("{}", &next);
            false
        }
        Ok(None) | Err(_) => true,
    }
}

fn parse_duration(s: &str) -> anyhow::Result<Duration> {
    let re = regex::Regex::new(r"^(\d+)(.+)$")?;
    let captures = re
        .captures(s)
        .ok_or_else(|| anyhow!("invalid duration string: {}", s))?;
    let value = captures.get(1).unwrap().as_str().parse::<i64>()?;
    let unit = captures.get(2).unwrap().as_str();
    match unit {
        "s" => Ok(time::Duration::seconds(value)),
        "m" => Ok(time::Duration::minutes(value)),
        "h" => Ok(time::Duration::hours(value)),
        _ => bail!("invalid duration unit: {}", unit),
    }
}

fn parse_timestamp(s: &str) -> Result<OffsetDateTime, time::Error> {
    let re = regex::Regex::new(r"^(?P<leader>.*?)(?P<sep>[+-])(?P<H>\d\d)(?P<M>\d\d)").unwrap();
    let fixed = re.replace_all(s, r"$leader$sep$H:$M");
    let parsed = OffsetDateTime::parse(&fixed, &Rfc3339)?;
    Ok(parsed)
}

/// Parse out a start time and a duration from the flow or netflow fields.
///
/// The times are adjusted to be one second before and one second after the provided
/// start and end times which seems to be required to catch the complete flow.
fn parse_flow_timeframe(flow: &EveEventFlow) -> Option<(OffsetDateTime, Duration)> {
    if let Ok(start_time) = parse_timestamp(&flow.start) {
        let adjusted_start_time = start_time - Duration::seconds(1);
        if let Some(end_time) = &flow.end {
            if let Ok(end_time) = parse_timestamp(end_time) {
                let duration = (end_time - start_time) + Duration::seconds(2);
                return Some((adjusted_start_time, duration));
            }
        }
        return Some((
            adjusted_start_time,
            parse_duration(DEFAULT_DURATION).unwrap(),
        ));
    }
    None
}

fn parse_event_timeframe(
    event: &EveEvent,
    params: &FetchRequest,
) -> Result<(OffsetDateTime, Duration), BadRequest> {
    if event.event_type == "flow" {
        if let Some(flow) = &event.flow {
            if let Some(timeinfo) = parse_flow_timeframe(flow) {
                return Ok(timeinfo);
            }
        }
    } else if event.event_type == "netflow" {
        if let Some(netflow) = &event.netflow {
            if let Some(timeinfo) = parse_flow_timeframe(netflow) {
                return Ok(timeinfo);
            }
        }
    }

    let timestamp = parse_timestamp(&event.timestamp).map_err(|err| {
        error!(
            "Failed to parse event timestamp: {} -- {:?}",
            &event.timestamp, err
        );
        BadRequest::new("failed to parse event timestamp")
    })?;

    let duration_before = params
        .duration_before
        .as_deref()
        .unwrap_or(DEFAULT_DURATION);
    let duration_before =
        parse_duration(duration_before).map_err(|_| BadRequest::new("invalid duration-before"))?;

    let duration_after = params.duration_after.as_deref().unwrap_or(DEFAULT_DURATION);
    let duration_after =
        parse_duration(duration_after).map_err(|_| BadRequest::new("invalid duration-after"))?;

    let start_time = timestamp.sub(duration_before);
    let end_time = timestamp.add(duration_after);
    let duration = end_time.sub(start_time);

    Ok((start_time, duration))
}

#[derive(Debug, Deserialize)]
struct EveEvent {
    timestamp: String,
    event_type: String,
    proto: String,
    src_ip: String,
    src_port: Option<u16>,
    dest_ip: String,
    dest_port: Option<u16>,
    alert: Option<EveEventAlert>,
    flow: Option<EveEventFlow>,
    netflow: Option<EveEventFlow>,
}

#[derive(Debug, Deserialize)]
struct EveEventAlert {
    signature_id: u64,
}

#[derive(Debug, Deserialize)]
struct EveEventFlow {
    start: String,
    end: Option<String>,
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_parse_duration() {
        let duration = parse_duration("1s").unwrap();
        assert_eq!(duration, Duration::seconds(1));

        let duration = parse_duration("1m").unwrap();
        assert_eq!(duration, Duration::seconds(60));

        let duration = parse_duration("1h").unwrap();
        assert_eq!(duration, Duration::seconds(3600));

        assert!(parse_duration("1").is_err());
        assert!(parse_duration("1z").is_err());
        assert!(parse_duration("am").is_err());
        assert!(parse_duration("99999999999999999999999999999m").is_err());
    }

    #[test]
    fn test_parse_timestamp() {
        let eve_format = "2022-04-12T17:20:42.294911-0600";
        parse_timestamp(eve_format).unwrap();

        let eve_format = "2022-04-12T17:20:42.294911-06:00";
        parse_timestamp(eve_format).unwrap();
    }
}
<commit_msg>Remove code construct only available in Rust 1.60.<commit_after>// SPDX-License-Identifier: MIT
//
// Copyright (C) 2022 Jason Ish

use crate::config::{Config, SpoolConfig};
use anyhow::{anyhow, bail};
use axum::body::StreamBody;
use axum::extract::{Form, Query};
use axum::http::{HeaderMap, HeaderValue, StatusCode};
use axum::response::{IntoResponse, Response};
use axum::Extension;
use serde::Deserialize;
use std::ops::Add;
use std::ops::Sub;
use std::process::Stdio;
use thiserror::Error;
use time::format_description::well_known::Rfc3339;
use time::{Duration, OffsetDateTime};
use tokio::io::AsyncBufReadExt;
use tokio::io::AsyncReadExt;
use tokio::io::BufReader;
use tokio_stream::wrappers::UnboundedReceiverStream;
use tracing::{error, info};

const DEFAULT_DURATION: &str = "1m";

#[derive(Deserialize, Debug, Clone)]
#[allow(dead_code)]
pub struct FetchRequest {
    #[serde(rename = "query-type")]
    query_type: String,
    filter: Option<String>,
    #[serde(rename = "start-time")]
    start_time: Option<String>,
    duration: Option<String>,
    event: Option<String>,
    #[serde(rename = "duration-before")]
    duration_before: Option<String>,
    #[serde(rename = "duration-after")]
    duration_after: Option<String>,
    spool: String,
}

#[derive(Debug, Error)]
#[error("{reason:?}")]
pub struct BadRequest {
    reason: String,
}

impl BadRequest {
    fn new<S: AsRef<str>>(reason: S) -> Self {
        Self {
            reason: reason.as_ref().to_string(),
        }
    }
}

impl IntoResponse for BadRequest {
    fn into_response(self) -> Response {
        (StatusCode::BAD_REQUEST, self.reason).into_response()
    }
}

fn get_spool_config<'a>(config: &'a Config, name: &str) -> Option<&'a SpoolConfig> {
    for spool in &config.spools {
        if spool.name == name {
            return Some(spool);
        }
    }
    None
}

pub async fn fetch_get(
    Extension(config): Extension<Config>,
    Query(params): Query<FetchRequest>,
) -> Result<impl IntoResponse, BadRequest> {
    fetch(config, params).await
}

pub async fn fetch_post(
    Extension(config): Extension<Config>,
    Form(params): Form<FetchRequest>,
) -> Result<impl IntoResponse, BadRequest> {
    fetch(config, params).await
}

pub async fn fetch(config: Config, params: FetchRequest) -> Result<impl IntoResponse, BadRequest> {
    let spool_config = get_spool_config(&config, &params.spool).ok_or_else(|| {
        error!("No spool with the name: {}", &params.spool);
        BadRequest::new(&format!("invalid spool name: {}", &params.spool))
    })?;

    let start_time;
    let duration;
    let filter;
    let filename;

    match params.query_type.as_ref() {
        "filter" => {
            let stime = match params.start_time.as_ref() {
                Some(stime) => stime,
                None => {
                    return Err(BadRequest::new("start-time is required"));
                }
            };

            start_time = OffsetDateTime::parse(stime, &Rfc3339).map_err(|err| {
                error!("Invalid start-time: {} -- {:?}", &stime, err);
                BadRequest::new("invalid start-time")
            })?;
            let pduration = params.duration.as_deref().unwrap_or(DEFAULT_DURATION);
            duration = parse_duration(pduration).map_err(|err| {
                error!("Invalid duration: {} -- {:?}", &pduration, err);
                BadRequest::new("invalid duration")
            })?;
            filter = params.clone().filter.unwrap();
            filename = format!("{}.pcap", start_time.unix_timestamp());
        }
        "event" => {
            if let Some(event) = &params.event {
                let event: EveEvent = serde_json::from_str(event).map_err(|err| {
                    error!("Failed to decode event: {:?} -- event={}", err, &event);
                    BadRequest::new("bad event")
                })?;

                let timeframes = parse_event_timeframe(&event, &params)?;
                start_time = timeframes.0;
                duration = timeframes.1;

                if event.src_port.is_some() && event.dest_port.is_some() {
                    filter = format!(
                        "{} and ((host {} and port {}) and (host {} and port {}))",
                        &event.proto.to_lowercase(),
                        &event.src_ip,
                        event.src_port.as_ref().unwrap(),
                        &event.dest_ip,
                        event.dest_port.as_ref().unwrap(),
                    );
                } else {
                    filter = format!(
                        "{} and host {} and host {}",
                        &event.proto, &event.src_ip, &event.dest_ip
                    );
                }

                // Create a filename based on event details.
                let signature_id = event.alert.map(|a| a.signature_id).unwrap_or(0);
                filename = format!(
                    "{}-{}-{}-{}-{}-{}.pcap",
                    signature_id,
                    start_time.unix_timestamp(),
                    &event.src_ip,
                    event.src_port.unwrap_or(0),
                    &event.dest_ip,
                    event.dest_port.unwrap_or(0)
                );
            } else {
                return Err(BadRequest::new("no event provided for event query"));
            }
        }
        _ => {
            error!("Invalid query type: {}", &params.query_type);
            return Err(BadRequest::new("bad query type"));
        }
    }

    let process_name = std::env::args().next().as_ref().unwrap().to_string();
    let mut command = tokio::process::Command::new(process_name);
    command
        .arg("export")
        .arg("--json")
        .arg("-v")
        .arg("--directory")
        .arg(&spool_config.directory)
        .arg("--filter")
        .arg(filter)
        .arg("--start-time")
        .arg(format!("{}", start_time.unix_timestamp()))
        .arg("--duration")
        .arg(format!("{}", duration.as_seconds_f64() as i64))
        .arg("--output")
        .arg("-");
    if let Some(prefix) = &spool_config.prefix {
        command.arg("--prefix").arg(prefix);
    }
    command.stdout(Stdio::piped());
    command.stderr(Stdio::piped());
    let mut child = command.spawn().unwrap();
    let mut stdout = child.stdout.take().unwrap();
    let stderr = child.stderr.take().unwrap();

    let (tx, rx) =
        tokio::sync::mpsc::unbounded_channel::<std::result::Result<Vec<u8>, std::io::Error>>();
    let rx = UnboundedReceiverStream::new(rx);
    let body = StreamBody::new(rx);
    let mut stderr_reader = BufReader::new(stderr).lines();
    let (wait_tx, wait_rx) = tokio::sync::oneshot::channel::<&str>();

    tokio::spawn(async move {
        let mut bytes = 0;
        let mut wait_tx = Some(wait_tx);
        let mut client_closed = false;
        loop {
            let mut buf = Vec::with_capacity(8192);
            tokio::select! {
                _ = child.wait() => {
                    break;
                }
                _ = tx.closed() => {
                    client_closed = true;
                    break;
                }
                closed = read_stderr(&mut stderr_reader) => {
                    if closed {
                        break;
                    }
                }
                x = stdout.read_buf(&mut buf) => {
                    match x {
                        Ok(n) => {
                            if n == 0 {
                                break;
                            } else {
                                if let Some(wait_tx) = wait_tx.take() {
                                    wait_tx.send("ok").unwrap();
                                }
                                if tx.send(Ok(buf)).is_err() {
                                    error!("Failed to write to body stream, client must have closed connection");
                                    client_closed = true;
                                    break;
                                }
                                bytes += n;
                            }
                        }
                        Err(err) => {
                            error!("Error reading export process stdout: {:?}", err);
                            break;
                        }
                    }
                }
            }
        }

        if client_closed {
            let _ = child.start_kill();
        }
        let status = child.wait().await.unwrap();
        if status.success() {
            info!(
                "Export process exited successfully, bytes written: {}",
                bytes
            );
        } else {
            error!("Export process exited with error code: {:?}", status);
        }
        if let Some(wait_tx) = wait_tx.take() {
            if status.success() {
                wait_tx.send("nopkt").unwrap();
            } else {
                wait_tx.send("err").unwrap();
            }
        }
    });

    Ok(match wait_rx.await {
        Err(err) => {
            error!("Error on wait channel: {:?}", err);
            (
                StatusCode::INTERNAL_SERVER_ERROR,
                "An error occurred. See server logs for details",
            )
                .into_response()
        }
        Ok(status) => {
            if status == "ok" {
                let mut headers = HeaderMap::new();
                headers.insert(
                    axum::http::header::CONTENT_TYPE,
                    HeaderValue::from_str("application/vnd.tcpdump.pcap").unwrap(),
                );
                headers.insert(
                    axum::http::header::CONTENT_DISPOSITION,
                    HeaderValue::from_str(&format!("attachment; filename={}", filename)).unwrap(),
                );

                (headers, body).into_response()
            } else if status == "nopkt" {
                (StatusCode::NOT_FOUND, "No packets found.").into_response()
            } else {
                (
                    StatusCode::INTERNAL_SERVER_ERROR,
                    "An error occurred. See server logs for details",
                )
                    .into_response()
            }
        }
    })
}

async fn read_stderr(
    reader: &mut tokio::io::Lines<tokio::io::BufReader<tokio::process::ChildStderr>>,
) -> bool {
    match reader.next_line().await {
        Ok(Some(next)) => {
            if let Ok(v) = serde_json::from_str::<serde_json::Value>(&next) {
                let level = v["level"].as_str().unwrap_or("INFO");
                if let Some(message) = v["fields"]["message"].as_str() {
                    match level {
                        "ERROR" => error!("{}", message),
                        _ => info!("{}", message),
                    }
                    return false;
                }
            }
            error!("{}", &next);
            false
        }
        Ok(None) | Err(_) => true,
    }
}

fn parse_duration(s: &str) -> anyhow::Result<Duration> {
    let re = regex::Regex::new(r"^(\d+)(.+)$")?;
    let captures = re
        .captures(s)
        .ok_or_else(|| anyhow!("invalid duration string: {}", s))?;
    let value = captures.get(1).unwrap().as_str().parse::<i64>()?;
    let unit = captures.get(2).unwrap().as_str();
    match unit {
        "s" => Ok(time::Duration::seconds(value)),
        "m" => Ok(time::Duration::minutes(value)),
        "h" => Ok(time::Duration::hours(value)),
        _ => bail!("invalid duration unit: {}", unit),
    }
}

fn parse_timestamp(s: &str) -> Result<OffsetDateTime, time::Error> {
    let re = regex::Regex::new(r"^(?P<leader>.*?)(?P<sep>[+-])(?P<H>\d\d)(?P<M>\d\d)").unwrap();
    let fixed = re.replace_all(s, r"$leader$sep$H:$M");
    let parsed = OffsetDateTime::parse(&fixed, &Rfc3339)?;
    Ok(parsed)
}

/// Parse out a start time and a duration from the flow or netflow fields.
///
/// The times are adjusted to be one second before and one second after the provided
/// start and end times which seems to be required to catch the complete flow.
fn parse_flow_timeframe(flow: &EveEventFlow) -> Option<(OffsetDateTime, Duration)> {
    if let Ok(start_time) = parse_timestamp(&flow.start) {
        let adjusted_start_time = start_time - Duration::seconds(1);
        if let Some(end_time) = &flow.end {
            if let Ok(end_time) = parse_timestamp(end_time) {
                let duration = (end_time - start_time) + Duration::seconds(2);
                return Some((adjusted_start_time, duration));
            }
        }
        return Some((
            adjusted_start_time,
            parse_duration(DEFAULT_DURATION).unwrap(),
        ));
    }
    None
}

fn parse_event_timeframe(
    event: &EveEvent,
    params: &FetchRequest,
) -> Result<(OffsetDateTime, Duration), BadRequest> {
    if event.event_type == "flow" {
        if let Some(flow) = &event.flow {
            if let Some(timeinfo) = parse_flow_timeframe(flow) {
                return Ok(timeinfo);
            }
        }
    } else if event.event_type == "netflow" {
        if let Some(netflow) = &event.netflow {
            if let Some(timeinfo) = parse_flow_timeframe(netflow) {
                return Ok(timeinfo);
            }
        }
    }

    let timestamp = parse_timestamp(&event.timestamp).map_err(|err| {
        error!(
            "Failed to parse event timestamp: {} -- {:?}",
            &event.timestamp, err
        );
        BadRequest::new("failed to parse event timestamp")
    })?;

    let duration_before = params
        .duration_before
        .as_deref()
        .unwrap_or(DEFAULT_DURATION);
    let duration_before =
        parse_duration(duration_before).map_err(|_| BadRequest::new("invalid duration-before"))?;

    let duration_after = params.duration_after.as_deref().unwrap_or(DEFAULT_DURATION);
    let duration_after =
        parse_duration(duration_after).map_err(|_| BadRequest::new("invalid duration-after"))?;

    let start_time = timestamp.sub(duration_before);
    let end_time = timestamp.add(duration_after);
    let duration = end_time.sub(start_time);

    Ok((start_time, duration))
}

#[derive(Debug, Deserialize)]
struct EveEvent {
    timestamp: String,
    event_type: String,
    proto: String,
    src_ip: String,
    src_port: Option<u16>,
    dest_ip: String,
    dest_port: Option<u16>,
    alert: Option<EveEventAlert>,
    flow: Option<EveEventFlow>,
    netflow: Option<EveEventFlow>,
}

#[derive(Debug, Deserialize)]
struct EveEventAlert {
    signature_id: u64,
}

#[derive(Debug, Deserialize)]
struct EveEventFlow {
    start: String,
    end: Option<String>,
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_parse_duration() {
        let duration = parse_duration("1s").unwrap();
        assert_eq!(duration, Duration::seconds(1));

        let duration = parse_duration("1m").unwrap();
        assert_eq!(duration, Duration::seconds(60));

        let duration = parse_duration("1h").unwrap();
        assert_eq!(duration, Duration::seconds(3600));

        assert!(parse_duration("1").is_err());
        assert!(parse_duration("1z").is_err());
        assert!(parse_duration("am").is_err());
        assert!(parse_duration("99999999999999999999999999999m").is_err());
    }

    #[test]
    fn test_parse_timestamp() {
        let eve_format = "2022-04-12T17:20:42.294911-0600";
        parse_timestamp(eve_format).unwrap();

        let eve_format = "2022-04-12T17:20:42.294911-06:00";
        parse_timestamp(eve_format).unwrap();
    }
}
<|endoftext|>