<commit_before>use crate::ignore::{Ignore, IgnoreGit, IgnoreVcs};
use crate::pipeline::{PipelineFork, PipelineInfo};
use crossbeam_channel::{Receiver, Sender};
use std::fs;
use std::path::PathBuf;
use std::time::{Duration, Instant};

// ---------------------------------------------------------------------------------------------------------------------
// PathInfo
// ---------------------------------------------------------------------------------------------------------------------

#[derive(Debug, Clone)]
pub struct PathInfo {
    pub path: PathBuf,
}

// ---------------------------------------------------------------------------------------------------------------------
// PipelineFinder
// ---------------------------------------------------------------------------------------------------------------------

pub struct PipelineFinder {
    pub is_recursive: bool,
    pub follow_symlink: bool,
    pub skip_vcs: bool,
    pub skip_gitignore: bool,
    pub skip_hgignore: bool,
    pub skip_ambignore: bool,
    pub print_skipped: bool,
    pub find_parent_ignore: bool,
    pub infos: Vec<String>,
    pub errors: Vec<String>,
    time_beg: Instant,
    time_bsy: Duration,
    seq_no: usize,
    current_tx: usize,
    ignore_vcs: IgnoreVcs,
    ignore_git: Vec<IgnoreGit>,
}

impl PipelineFinder {
    pub fn new() -> Self {
        PipelineFinder {
            is_recursive: true,
            follow_symlink: true,
            skip_vcs: true,
            skip_gitignore: true,
            skip_hgignore: true,
            skip_ambignore: true,
            print_skipped: false,
            find_parent_ignore: true,
            infos: Vec::new(),
            errors: Vec::new(),
            time_beg: Instant::now(),
            time_bsy: Duration::new(0, 0),
            seq_no: 0,
            current_tx: 0,
            ignore_vcs: IgnoreVcs::new(),
            ignore_git: Vec::new(),
        }
    }

    fn find_path(&mut self, base: PathBuf, tx: &Vec<Sender<PipelineInfo<PathInfo>>>, is_symlink: bool) {
        let attr = match fs::metadata(&base) {
            Ok(x) => x,
            Err(e) => {
                if !is_symlink {
                    self.errors.push(format!("Error: {} @ {}", e, base.to_str().unwrap()));
                }
                return;
            }
        };

        if attr.is_file() {
            if attr.len() != 0 {
                self.send_path(base, &tx);
            }
        } else {
            let reader = match fs::read_dir(&base) {
                Ok(x) => x,
                Err(e) => {
                    self.errors.push(format!("Error: {} @ {}", e, base.to_str().unwrap()));
                    return;
                }
            };

            let gitignore_exist = self.push_gitignore(&base);

            for i in reader {
                match i {
                    Ok(entry) => {
                        let file_type = match entry.file_type() {
                            Ok(x) => x,
                            Err(e) => {
                                self.errors.push(format!("Error: {}", e));
                                continue;
                            }
                        };
                        if file_type.is_file() {
                            self.send_path(entry.path(), &tx);
                        } else {
                            let find_dir = file_type.is_dir() & self.is_recursive;
                            let find_symlink = file_type.is_symlink() & self.is_recursive & self.follow_symlink;
                            if (find_dir | find_symlink) & self.check_path(&entry.path(), true) {
                                self.find_path(entry.path(), &tx, find_symlink);
                            }
                        }
                    }
                    Err(e) => self.errors.push(format!("Error: {}", e)),
                };
            }

            self.pop_gitignore(gitignore_exist)
        }
    }

    fn send_path(&mut self, path: PathBuf, tx: &Vec<Sender<PipelineInfo<PathInfo>>>) {
        if self.check_path(&path, false) {
            let _ = tx[self.current_tx].send(PipelineInfo::SeqDat(self.seq_no, PathInfo { path: path }));
            self.seq_no += 1;
            self.current_tx = if self.current_tx == tx.len() - 1 {
                0
            } else {
                self.current_tx + 1
            };
        }
    }

    fn push_gitignore(&mut self, path: &PathBuf) -> bool {
        if !self.skip_gitignore {
            return false;
        }

        let reader = fs::read_dir(&path).unwrap();
        for i in reader {
            match i {
                Ok(entry) => {
                    if entry.path().ends_with(".gitignore") {
                        self.ignore_git.push(IgnoreGit::new(&entry.path()));
                        return true;
                    }
                }
                Err(e) => self.errors.push(format!("Error: {}", e)),
            }
        }
        false
    }

    fn pop_gitignore(&mut self, exist: bool) {
        if exist {
            let _ = self.ignore_git.pop();
        }
    }

    fn check_path(&mut self, path: &PathBuf, is_dir: bool) -> bool {
        let ok_vcs = if self.skip_vcs {
            !self.ignore_vcs.is_ignore(&path, is_dir)
        } else {
            true
        };

        let ok_git = if self.skip_gitignore && !self.ignore_git.is_empty() {
            !self.ignore_git.last().unwrap().is_ignore(&path, is_dir)
        } else {
            true
        };

        if !ok_vcs & self.print_skipped {
            self.infos.push(format!("Skipped: {:?} ( vcs file )\n", path));
        }

        if !ok_git & self.print_skipped {
            self.infos.push(format!("Skipped: {:?} ( .gitignore )\n", path));
        }

        ok_vcs && ok_git
    }

    fn set_default_gitignore(&mut self, base: &PathBuf) -> PathBuf {
        if !self.skip_gitignore {
            return base.clone();
        }
        if !self.find_parent_ignore {
            return base.clone();
        }

        let base_abs = match base.canonicalize() {
            Ok(x) => x,
            Err(e) => {
                self.errors.push(format!("Error: {} @ {}", e, base.to_str().unwrap()));
                return base.clone();
            }
        };

        let mut parent = base_abs.parent();
        while parent.is_some() {
            if self.push_gitignore(&PathBuf::from(parent.unwrap())) {
                self.infos.push(format!(
                    "Found .gitignore at the parent directory: {:?}\n",
                    parent.unwrap()
                ));
                return base_abs.clone();
            }
            parent = parent.unwrap().parent();
        }

        return base.clone();
    }
}

impl PipelineFork<PathBuf, PathInfo> for PipelineFinder {
    fn setup(&mut self, id: usize, rx: Receiver<PipelineInfo<PathBuf>>, tx: Vec<Sender<PipelineInfo<PathInfo>>>) {
        self.infos = Vec::new();
        self.errors = Vec::new();
        let mut seq_beg_arrived = false;

        loop {
            match rx.recv() {
                Ok(PipelineInfo::SeqDat(_, p)) => {
                    watch_time!(self.time_bsy, {
                        let p = self.set_default_gitignore(&p);
                        self.find_path(p, &tx, false);
                    });
                }

                Ok(PipelineInfo::SeqBeg(x)) => {
                    if !seq_beg_arrived {
                        self.seq_no = x;
                        self.time_beg = Instant::now();

                        for tx in &tx {
                            let _ = tx.send(PipelineInfo::SeqBeg(x));
                        }

                        seq_beg_arrived = true;
                    }
                }

                Ok(PipelineInfo::SeqEnd(_)) => {
                    for i in &self.infos {
                        let _ = tx[0].send(PipelineInfo::MsgInfo(id, i.clone()));
                    }
                    for e in &self.errors {
                        let _ = tx[0].send(PipelineInfo::MsgErr(id, e.clone()));
                    }

                    let _ = tx[0].send(PipelineInfo::MsgTime(id, self.time_bsy, self.time_beg.elapsed()));

                    for tx in &tx {
                        let _ = tx.send(PipelineInfo::SeqEnd(self.seq_no));
                    }

                    break;
                }

                Ok(PipelineInfo::MsgInfo(i, e)) => {
                    let _ = tx[0].send(PipelineInfo::MsgInfo(i, e));
                }
                Ok(PipelineInfo::MsgErr(i, e)) => {
                    let _ = tx[0].send(PipelineInfo::MsgErr(i, e));
                }
                Ok(PipelineInfo::MsgTime(i, t0, t1)) => {
                    let _ = tx[0].send(PipelineInfo::MsgTime(i, t0, t1));
                }
                Err(_) => break,
            }
        }
    }
}

// ---------------------------------------------------------------------------------------------------------------------
// Test
// ---------------------------------------------------------------------------------------------------------------------

#[cfg(test)]
mod tests {
    use super::*;
    use crate::pipeline::{PipelineFork, PipelineInfo};
    use crossbeam_channel::unbounded;
    use std::path::{Path, PathBuf};
    use std::thread;

    fn test<T: 'static + PipelineFork<PathBuf, PathInfo> + Send>(mut finder: T, path: String) -> Vec<PathInfo> {
        let (in_tx, in_rx) = unbounded();
        let (out_tx, out_rx) = unbounded();
        thread::spawn(move || {
            finder.setup(0, in_rx, vec![out_tx]);
        });
        let _ = in_tx.send(PipelineInfo::SeqBeg(0));
        let _ = in_tx.send(PipelineInfo::SeqDat(0, PathBuf::from(path)));
        let _ = in_tx.send(PipelineInfo::SeqEnd(1));

        let mut ret = Vec::new();
        loop {
            match out_rx.recv().unwrap() {
                PipelineInfo::SeqDat(_, x) => ret.push(x),
                PipelineInfo::SeqEnd(_) => break,
                _ => (),
            }
        }

        ret
    }

    #[test]
    fn pipeline_finder_default() {
        if !Path::new("./.git/config").exists() {
            fs::create_dir_all("./.git").unwrap();
            fs::File::create("./.git/config").unwrap();
        }

        let finder = PipelineFinder::new();
        let ret = test(finder, "./".to_string());

        assert!(ret.iter().any(|x| x.path == PathBuf::from("./Cargo.toml")));
        assert!(ret.iter().any(|x| x.path == PathBuf::from("./src/ambr.rs")));
        assert!(ret.iter().any(|x| x.path == PathBuf::from("./src/ambs.rs")));
        assert!(ret.iter().any(|x| x.path == PathBuf::from("./src/console.rs")));
        assert!(ret.iter().any(|x| x.path == PathBuf::from("./src/lib.rs")));
        assert!(ret.iter().any(|x| x.path == PathBuf::from("./src/matcher.rs")));
        assert!(ret.iter().any(|x| x.path == PathBuf::from("./src/util.rs")));
        assert!(!ret.iter().any(|x| x.path == PathBuf::from("./.git/config")));
    }

    #[test]
    fn pipeline_finder_not_skip_vcs() {
        if !Path::new("./.git/config").exists() {
            fs::create_dir_all("./.git").unwrap();
            fs::File::create("./.git/config").unwrap();
        }

        let mut finder = PipelineFinder::new();
        finder.skip_vcs = false;
        let ret = test(finder, "./".to_string());

        assert!(ret.iter().any(|x| x.path == PathBuf::from("./Cargo.toml")));
        assert!(ret.iter().any(|x| x.path == PathBuf::from("./src/ambr.rs")));
        assert!(ret.iter().any(|x| x.path == PathBuf::from("./src/ambs.rs")));
        assert!(ret.iter().any(|x| x.path == PathBuf::from("./src/console.rs")));
        assert!(ret.iter().any(|x| x.path == PathBuf::from("./src/lib.rs")));
        assert!(ret.iter().any(|x| x.path == PathBuf::from("./src/matcher.rs")));
        assert!(ret.iter().any(|x| x.path == PathBuf::from("./src/util.rs")));
        assert!(ret.iter().any(|x| x.path == PathBuf::from("./.git/config")));
    }
}
<commit_msg>Fix output path #31<commit_after>use crate::ignore::{Ignore, IgnoreGit, IgnoreVcs};
use crate::pipeline::{PipelineFork, PipelineInfo};
use crossbeam_channel::{Receiver, Sender};
use std::fs;
use std::path::PathBuf;
use std::time::{Duration, Instant};

// ---------------------------------------------------------------------------------------------------------------------
// PathInfo
// ---------------------------------------------------------------------------------------------------------------------

#[derive(Debug, Clone)]
pub struct PathInfo {
    pub path: PathBuf,
}

// ---------------------------------------------------------------------------------------------------------------------
// PipelineFinder
// ---------------------------------------------------------------------------------------------------------------------

pub struct PipelineFinder {
    pub is_recursive: bool,
    pub follow_symlink: bool,
    pub skip_vcs: bool,
    pub skip_gitignore: bool,
    pub skip_hgignore: bool,
    pub skip_ambignore: bool,
    pub print_skipped: bool,
    pub find_parent_ignore: bool,
    pub infos: Vec<String>,
    pub errors: Vec<String>,
    time_beg: Instant,
    time_bsy: Duration,
    seq_no: usize,
    current_tx: usize,
    ignore_vcs: IgnoreVcs,
    ignore_git: Vec<IgnoreGit>,
}

impl PipelineFinder {
    pub fn new() -> Self {
        PipelineFinder {
            is_recursive: true,
            follow_symlink: true,
            skip_vcs: true,
            skip_gitignore: true,
            skip_hgignore: true,
            skip_ambignore: true,
            print_skipped: false,
            find_parent_ignore: true,
            infos: Vec::new(),
            errors: Vec::new(),
            time_beg: Instant::now(),
            time_bsy: Duration::new(0, 0),
            seq_no: 0,
            current_tx: 0,
            ignore_vcs: IgnoreVcs::new(),
            ignore_git: Vec::new(),
        }
    }

    fn find_path(&mut self, base: PathBuf, tx: &Vec<Sender<PipelineInfo<PathInfo>>>, is_symlink: bool) {
        let attr = match fs::metadata(&base) {
            Ok(x) => x,
            Err(e) => {
                if !is_symlink {
                    self.errors.push(format!("Error: {} @ {}", e, base.to_str().unwrap()));
                }
                return;
            }
        };

        if attr.is_file() {
            if attr.len() != 0 {
                self.send_path(base, &tx);
            }
        } else {
            let reader = match fs::read_dir(&base) {
                Ok(x) => x,
                Err(e) => {
                    self.errors.push(format!("Error: {} @ {}", e, base.to_str().unwrap()));
                    return;
                }
            };

            let gitignore_exist = self.push_gitignore(&base);

            for i in reader {
                match i {
                    Ok(entry) => {
                        let file_type = match entry.file_type() {
                            Ok(x) => x,
                            Err(e) => {
                                self.errors.push(format!("Error: {}", e));
                                continue;
                            }
                        };
                        if file_type.is_file() {
                            self.send_path(entry.path(), &tx);
                        } else {
                            let find_dir = file_type.is_dir() & self.is_recursive;
                            let find_symlink = file_type.is_symlink() & self.is_recursive & self.follow_symlink;
                            if (find_dir | find_symlink) & self.check_path(&entry.path(), true) {
                                self.find_path(entry.path(), &tx, find_symlink);
                            }
                        }
                    }
                    Err(e) => self.errors.push(format!("Error: {}", e)),
                };
            }

            self.pop_gitignore(gitignore_exist)
        }
    }

    fn send_path(&mut self, path: PathBuf, tx: &Vec<Sender<PipelineInfo<PathInfo>>>) {
        if self.check_path(&path, false) {
            let _ = tx[self.current_tx].send(PipelineInfo::SeqDat(self.seq_no, PathInfo { path: path }));
            self.seq_no += 1;
            self.current_tx = if self.current_tx == tx.len() - 1 {
                0
            } else {
                self.current_tx + 1
            };
        }
    }

    fn push_gitignore(&mut self, path: &PathBuf) -> bool {
        if !self.skip_gitignore {
            return false;
        }

        let reader = fs::read_dir(&path).unwrap();
        for i in reader {
            match i {
                Ok(entry) => {
                    if entry.path().ends_with(".gitignore") {
                        self.ignore_git.push(IgnoreGit::new(&entry.path()));
                        return true;
                    }
                }
                Err(e) => self.errors.push(format!("Error: {}", e)),
            }
        }
        false
    }

    fn pop_gitignore(&mut self, exist: bool) {
        if exist {
            let _ = self.ignore_git.pop();
        }
    }

    fn check_path(&mut self, path: &PathBuf, is_dir: bool) -> bool {
        let ok_vcs = if self.skip_vcs {
            !self.ignore_vcs.is_ignore(&path, is_dir)
        } else {
            true
        };

        let ok_git = if self.skip_gitignore && !self.ignore_git.is_empty() {
            !self.ignore_git.last().unwrap().is_ignore(&path, is_dir)
        } else {
            true
        };

        if !ok_vcs & self.print_skipped {
            self.infos.push(format!("Skipped: {:?} ( vcs file )\n", path));
        }

        if !ok_git & self.print_skipped {
            self.infos.push(format!("Skipped: {:?} ( .gitignore )\n", path));
        }

        ok_vcs && ok_git
    }

    fn set_default_gitignore(&mut self, base: &PathBuf) -> PathBuf {
        if !self.skip_gitignore {
            return base.clone();
        }
        if !self.find_parent_ignore {
            return base.clone();
        }

        let base_abs = match base.canonicalize() {
            Ok(x) => x,
            Err(e) => {
                self.errors.push(format!("Error: {} @ {}", e, base.to_str().unwrap()));
                return base.clone();
            }
        };

        let mut parent_abs = base_abs.parent();
        let mut parent = base.clone();
        if parent.is_dir() {
            parent.push("..");
        } else {
            parent = parent.parent().unwrap().to_path_buf();
        }
        while parent_abs.is_some() {
            if self.push_gitignore(&PathBuf::from(&parent)) {
                self.infos
                    .push(format!("Found .gitignore at the parent directory: {:?}\n", parent));
                return base.clone();
            }
            parent_abs = parent_abs.unwrap().parent();
            parent.push("..");
        }

        return base.clone();
    }
}

impl PipelineFork<PathBuf, PathInfo> for PipelineFinder {
    fn setup(&mut self, id: usize, rx: Receiver<PipelineInfo<PathBuf>>, tx: Vec<Sender<PipelineInfo<PathInfo>>>) {
        self.infos = Vec::new();
        self.errors = Vec::new();
        let mut seq_beg_arrived = false;

        loop {
            match rx.recv() {
                Ok(PipelineInfo::SeqDat(_, p)) => {
                    watch_time!(self.time_bsy, {
                        let p = self.set_default_gitignore(&p);
                        self.find_path(p, &tx, false);
                    });
                }

                Ok(PipelineInfo::SeqBeg(x)) => {
                    if !seq_beg_arrived {
                        self.seq_no = x;
                        self.time_beg = Instant::now();

                        for tx in &tx {
                            let _ = tx.send(PipelineInfo::SeqBeg(x));
                        }

                        seq_beg_arrived = true;
                    }
                }

                Ok(PipelineInfo::SeqEnd(_)) => {
                    for i in &self.infos {
                        let _ = tx[0].send(PipelineInfo::MsgInfo(id, i.clone()));
                    }
                    for e in &self.errors {
                        let _ = tx[0].send(PipelineInfo::MsgErr(id, e.clone()));
                    }

                    let _ = tx[0].send(PipelineInfo::MsgTime(id, self.time_bsy, self.time_beg.elapsed()));

                    for tx in &tx {
                        let _ = tx.send(PipelineInfo::SeqEnd(self.seq_no));
                    }

                    break;
                }

                Ok(PipelineInfo::MsgInfo(i, e)) => {
                    let _ = tx[0].send(PipelineInfo::MsgInfo(i, e));
                }
                Ok(PipelineInfo::MsgErr(i, e)) => {
                    let _ = tx[0].send(PipelineInfo::MsgErr(i, e));
                }
                Ok(PipelineInfo::MsgTime(i, t0, t1)) => {
                    let _ = tx[0].send(PipelineInfo::MsgTime(i, t0, t1));
                }
                Err(_) => break,
            }
        }
    }
}

// ---------------------------------------------------------------------------------------------------------------------
// Test
// ---------------------------------------------------------------------------------------------------------------------

#[cfg(test)]
mod tests {
    use super::*;
    use crate::pipeline::{PipelineFork, PipelineInfo};
    use crossbeam_channel::unbounded;
    use std::path::{Path, PathBuf};
    use std::thread;

    fn test<T: 'static + PipelineFork<PathBuf, PathInfo> + Send>(mut finder: T, path: String) -> Vec<PathInfo> {
        let (in_tx, in_rx) = unbounded();
        let (out_tx, out_rx) = unbounded();
        thread::spawn(move || {
            finder.setup(0, in_rx, vec![out_tx]);
        });
        let _ = in_tx.send(PipelineInfo::SeqBeg(0));
        let _ = in_tx.send(PipelineInfo::SeqDat(0, PathBuf::from(path)));
        let _ = in_tx.send(PipelineInfo::SeqEnd(1));

        let mut ret = Vec::new();
        loop {
            match out_rx.recv().unwrap() {
                PipelineInfo::SeqDat(_, x) => ret.push(x),
                PipelineInfo::SeqEnd(_) => break,
                _ => (),
            }
        }

        ret
    }

    #[test]
    fn pipeline_finder_default() {
        if !Path::new("./.git/config").exists() {
            fs::create_dir_all("./.git").unwrap();
            fs::File::create("./.git/config").unwrap();
        }

        let finder = PipelineFinder::new();
        let ret = test(finder, "./".to_string());

        assert!(ret.iter().any(|x| x.path == PathBuf::from("./Cargo.toml")));
        assert!(ret.iter().any(|x| x.path == PathBuf::from("./src/ambr.rs")));
        assert!(ret.iter().any(|x| x.path == PathBuf::from("./src/ambs.rs")));
        assert!(ret.iter().any(|x| x.path == PathBuf::from("./src/console.rs")));
        assert!(ret.iter().any(|x| x.path == PathBuf::from("./src/lib.rs")));
        assert!(ret.iter().any(|x| x.path == PathBuf::from("./src/matcher.rs")));
        assert!(ret.iter().any(|x| x.path == PathBuf::from("./src/util.rs")));
        assert!(!ret.iter().any(|x| x.path == PathBuf::from("./.git/config")));
    }

    #[test]
    fn pipeline_finder_not_skip_vcs() {
        if !Path::new("./.git/config").exists() {
            fs::create_dir_all("./.git").unwrap();
            fs::File::create("./.git/config").unwrap();
        }

        let mut finder = PipelineFinder::new();
        finder.skip_vcs = false;
        let ret = test(finder, "./".to_string());

        assert!(ret.iter().any(|x| x.path == PathBuf::from("./Cargo.toml")));
        assert!(ret.iter().any(|x| x.path == PathBuf::from("./src/ambr.rs")));
        assert!(ret.iter().any(|x| x.path == PathBuf::from("./src/ambs.rs")));
        assert!(ret.iter().any(|x| x.path == PathBuf::from("./src/console.rs")));
        assert!(ret.iter().any(|x| x.path == PathBuf::from("./src/lib.rs")));
        assert!(ret.iter().any(|x| x.path == PathBuf::from("./src/matcher.rs")));
        assert!(ret.iter().any(|x| x.path == PathBuf::from("./src/util.rs")));
        assert!(ret.iter().any(|x| x.path == PathBuf::from("./.git/config")));
    }
}
<|endoftext|>


<commit_before>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Implementation of cross-process surfaces for Linux. This uses X pixmaps.

use platform::surface::NativeSurfaceMethods;
use texturegl::Texture;

use geom::size::Size2D;
use libc::{c_int, c_uint, c_void};
use opengles::glx::{GLXFBConfig, GLXDrawable};
use opengles::glx::{GLX_BIND_TO_TEXTURE_RGBA_EXT};
use opengles::glx::{GLX_DRAWABLE_TYPE, GLX_FRONT_EXT, GLX_PIXMAP_BIT};
use opengles::glx::{GLX_TEXTURE_2D_EXT, GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGBA_EXT};
use opengles::glx::{GLX_TEXTURE_TARGET_EXT, glXCreatePixmap, glXDestroyPixmap};
use opengles::glx::{glXGetProcAddress, glXChooseFBConfig};
use opengles::glx::{glXGetVisualFromFBConfig};
use opengles::glx::{GLX_RGBA_BIT, GLX_WINDOW_BIT, GLX_RENDER_TYPE, GLX_DOUBLEBUFFER};
use opengles::gl2::NO_ERROR;
use opengles::gl2;
use std::cast;
use std::c_str::CString;
use std::ptr;
use xlib::xlib::{Display, Pixmap, XCreateGC, XCreateImage, XCreatePixmap, XDefaultScreen};
use xlib::xlib::{XDisplayString, XFreePixmap, XGetGeometry, XOpenDisplay, XPutImage, XRootWindow};
use xlib::xlib::{XVisualInfo, ZPixmap};

/// The display and visual info. This is needed in order to upload on the painting side. This
/// holds a weak reference to the display and will not close it when done.
///
/// FIXME(pcwalton): Mark nonsendable and noncopyable.
pub struct NativePaintingGraphicsContext {
    display: *Display,
    visual_info: *XVisualInfo,
}

impl NativePaintingGraphicsContext {
    pub fn from_metadata(metadata: &NativeGraphicsMetadata) -> NativePaintingGraphicsContext {
        // FIXME(pcwalton): It would be more robust to actually have the compositor pass the
        // visual.
        let (compositor_visual_info, _) =
            NativeCompositingGraphicsContext::compositor_visual_info(metadata.display);

        NativePaintingGraphicsContext {
            display: metadata.display,
            visual_info: compositor_visual_info,
        }
    }
}

/// The display, visual info, and framebuffer configuration. This is needed in order to bind to a
/// texture on the compositor side. This holds only a *weak* reference to the display and does not
/// close it.
///
/// FIXME(pcwalton): Unchecked weak references are bad and can violate memory safety. This is hard
/// to fix because the Display is given to us by the native windowing system, but we should fix it
/// someday.
///
/// FIXME(pcwalton): Mark nonsendable.
pub struct NativeCompositingGraphicsContext {
    display: *Display,
    visual_info: *XVisualInfo,
    framebuffer_configuration: Option<GLXFBConfig>,
}

impl NativeCompositingGraphicsContext {
    /// Chooses the compositor visual info using the same algorithm that the compositor uses.
    ///
    /// FIXME(pcwalton): It would be more robust to actually have the compositor pass the visual.
    fn compositor_visual_info(display: *Display) -> (*XVisualInfo, Option<GLXFBConfig>) {
        unsafe {
            let glx_display = cast::transmute(display);

            // CONSIDER:
            // In skia, they compute the GLX_ALPHA_SIZE minimum and request
            // that as well.

            let fbconfig_attributes = [
                GLX_DOUBLEBUFFER, 0,
                GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT | GLX_WINDOW_BIT,
                GLX_BIND_TO_TEXTURE_RGBA_EXT, 1,
                GLX_RENDER_TYPE, GLX_RGBA_BIT,
                0
            ];

            let screen = XDefaultScreen(display);
            let mut configs = 0;
            let fbconfigs = glXChooseFBConfig(glx_display, screen,
                                              &fbconfig_attributes[0], &mut configs);
            if configs == 0 {
                fail!("Unable to locate a GLX FB configuration that supports RGBA.");
            }

            let fbconfig = *fbconfigs.offset(0);
            let vi = glXGetVisualFromFBConfig(glx_display, fbconfig);
            (cast::transmute(vi), Some(fbconfig))
        }
    }

    /// Creates a native graphics context from the given X display connection. This uses GLX. Only
    /// the compositor is allowed to call this.
    pub fn from_display(display: *Display) -> NativeCompositingGraphicsContext {
        let (visual_info, fbconfig) = NativeCompositingGraphicsContext::compositor_visual_info(display);

        NativeCompositingGraphicsContext {
            display: display,
            visual_info: visual_info,
            framebuffer_configuration: fbconfig,
        }
    }
}

/// The X display.
#[deriving(Clone)]
pub struct NativeGraphicsMetadata {
    pub display: *Display,
}

impl NativeGraphicsMetadata {
    /// Creates graphics metadata from a metadata descriptor.
    pub fn from_descriptor(descriptor: &NativeGraphicsMetadataDescriptor)
                           -> NativeGraphicsMetadata {
        // WARNING: We currently rely on the X display connection being the
        // same in both the Painting and Compositing contexts, as otherwise
        // the X Pixmap will not be sharable across them. Using this
        // method breaks that assumption.
        unsafe {
            let display = descriptor.display.with_c_str(|c_str| {
                    XOpenDisplay(c_str)
                });

            if display.is_null() {
                fail!("XOpenDisplay() failed!");
            }

            NativeGraphicsMetadata {
                display: display,
            }
        }
    }
}

/// A sendable form of the X display string.
#[deriving(Clone, Decodable, Encodable)]
pub struct NativeGraphicsMetadataDescriptor {
    display: ~str,
}

impl NativeGraphicsMetadataDescriptor {
    /// Creates a metadata descriptor from metadata.
    pub fn from_metadata(metadata: NativeGraphicsMetadata) -> NativeGraphicsMetadataDescriptor {
        unsafe {
            let c_str = CString::new(XDisplayString(metadata.display), false);
            NativeGraphicsMetadataDescriptor {
                display: c_str.as_str().unwrap().to_str(),
            }
        }
    }
}

#[deriving(Eq)]
pub enum NativeSurfaceTransientData {
    NoTransientData,
    RenderTaskTransientData(*Display, *XVisualInfo),
}

#[deriving(Decodable, Encodable)]
pub struct NativeSurface {
    /// The pixmap.
    pixmap: Pixmap,

    /// Whether this pixmap will leak if the destructor runs. This is for debugging purposes.
    will_leak: bool,
}

impl Drop for NativeSurface {
    fn drop(&mut self) {
        if self.will_leak {
            fail!("You should have disposed of the pixmap properly with destroy()! This pixmap \
                   will leak!");
        }
    }
}

impl NativeSurface {
    pub fn from_pixmap(pixmap: Pixmap) -> NativeSurface {
        NativeSurface {
            pixmap: pixmap,
            will_leak: true,
        }
    }
}

impl NativeSurfaceMethods for NativeSurface {
    fn new(native_context: &NativePaintingGraphicsContext, size: Size2D<i32>, stride: i32)
           -> NativeSurface {
        unsafe {
            // Create the pixmap.
            let screen = XDefaultScreen(native_context.display);
            let window = XRootWindow(native_context.display, screen);
            let pixmap = XCreatePixmap(native_context.display,
                                       window,
                                       size.width as c_uint,
                                       size.height as c_uint,
                                       ((stride / size.width) * 8) as c_uint);
            NativeSurface::from_pixmap(pixmap)
        }
    }

    /// This may only be called on the compositor side.
    fn bind_to_texture(&self,
                       native_context: &NativeCompositingGraphicsContext,
                       texture: &Texture,
                       _size: Size2D<int>) {
        unsafe {
            // Create the GLX pixmap.
            //
            // FIXME(pcwalton): RAII for exception safety?
            let pixmap_attributes = [
                GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT,
                GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGBA_EXT,
                0
            ];

            let glx_display = cast::transmute(native_context.display);

            let glx_pixmap = glXCreatePixmap(glx_display,
                                             native_context.framebuffer_configuration.expect(
                                                 "GLX 1.3 should have a framebuffer_configuration"),
                                             self.pixmap,
                                             &pixmap_attributes[0]);

            let glXBindTexImageEXT: extern "C" fn(*Display, GLXDrawable, c_int, *c_int) =
                cast::transmute(glXGetProcAddress(cast::transmute(&"glXBindTexImageEXT\x00"[0])));
            assert!(glXBindTexImageEXT as *c_void != ptr::null());
            let _bound = texture.bind();
            glXBindTexImageEXT(native_context.display,
                               cast::transmute(glx_pixmap),
                               GLX_FRONT_EXT,
                               ptr::null());
            assert_eq!(gl2::get_error(), NO_ERROR);

            // FIXME(pcwalton): Recycle these for speed?
            glXDestroyPixmap(glx_display, glx_pixmap);
        }
    }

    /// This may only be called on the painting side.
    fn upload(&self, graphics_context: &NativePaintingGraphicsContext, data: &[u8]) {
        unsafe {
            // Ensure that we're running on the render task. Take the display.
            let pixmap = self.pixmap;

            // Figure out the width, height, and depth of the pixmap.
            let mut root_window = 0;
            let mut x = 0;
            let mut y = 0;
            let mut width = 0;
            let mut height = 0;
            let mut border_width = 0;
            let mut depth = 0;
            let _ = XGetGeometry(graphics_context.display,
                                 cast::transmute(pixmap),
                                 &mut root_window,
                                 &mut x,
                                 &mut y,
                                 &mut width,
                                 &mut height,
                                 &mut border_width,
                                 &mut depth);

            // Create the image.
            let image = XCreateImage(graphics_context.display,
                                     (*graphics_context.visual_info).visual,
                                     depth,
                                     ZPixmap,
                                     0,
                                     cast::transmute(&data[0]),
                                     width as c_uint,
                                     height as c_uint,
                                     32,
                                     0);

            // Create the X graphics context.
            let gc = XCreateGC(graphics_context.display, pixmap, 0, ptr::null());

            // Draw the image.
            let _ = XPutImage(graphics_context.display,
                              pixmap,
                              gc,
                              image,
                              0,
                              0,
                              0,
                              0,
                              width,
                              height);
        }
    }

    fn get_id(&self) -> int {
        self.pixmap as int
    }

    fn destroy(&mut self, graphics_context: &NativePaintingGraphicsContext) {
        unsafe {
            assert!(self.pixmap != 0);
            XFreePixmap(graphics_context.display, self.pixmap);
            self.mark_wont_leak()
        }
    }

    fn mark_will_leak(&mut self) {
        self.will_leak = true
    }

    fn mark_wont_leak(&mut self) {
        self.will_leak = false
    }
}

<commit_msg>Choose truly 32-bit framebuffer configurations when creating X11 surfaces<commit_after>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Implementation of cross-process surfaces for Linux. This uses X pixmaps.

use platform::surface::NativeSurfaceMethods;
use texturegl::Texture;

use geom::size::Size2D;
use libc::{c_int, c_uint, c_void};
use opengles::glx::{GLXFBConfig, GLXDrawable};
use opengles::glx::{GLX_BIND_TO_TEXTURE_RGBA_EXT};
use opengles::glx::{GLX_DRAWABLE_TYPE, GLX_FRONT_EXT, GLX_PIXMAP_BIT};
use opengles::glx::{GLX_TEXTURE_2D_EXT, GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGBA_EXT};
use opengles::glx::{GLX_TEXTURE_TARGET_EXT, glXCreatePixmap, glXDestroyPixmap};
use opengles::glx::{glXGetProcAddress, glXChooseFBConfig};
use opengles::glx::{glXGetVisualFromFBConfig};
use opengles::glx::{GLX_RGBA_BIT, GLX_WINDOW_BIT, GLX_RENDER_TYPE, GLX_DOUBLEBUFFER};
use opengles::gl2::NO_ERROR;
use opengles::gl2;
use std::cast;
use std::c_str::CString;
use std::ptr;
use xlib::xlib::{Display, Pixmap, XCreateGC, XCreateImage, XCreatePixmap, XDefaultScreen};
use xlib::xlib::{XDisplayString, XFreePixmap, XGetGeometry, XOpenDisplay, XPutImage, XRootWindow};
use xlib::xlib::{XVisualInfo, ZPixmap};

/// The display and visual info. This is needed in order to upload on the painting side. This
/// holds a weak reference to the display and will not close it when done.
///
/// FIXME(pcwalton): Mark nonsendable and noncopyable.
pub struct NativePaintingGraphicsContext {
    display: *Display,
    visual_info: *XVisualInfo,
}

impl NativePaintingGraphicsContext {
    pub fn from_metadata(metadata: &NativeGraphicsMetadata) -> NativePaintingGraphicsContext {
        // FIXME(pcwalton): It would be more robust to actually have the compositor pass the
        // visual.
        let (compositor_visual_info, _) =
            NativeCompositingGraphicsContext::compositor_visual_info(metadata.display);

        NativePaintingGraphicsContext {
            display: metadata.display,
            visual_info: compositor_visual_info,
        }
    }
}

/// The display, visual info, and framebuffer configuration. This is needed in order to bind to a
/// texture on the compositor side. This holds only a *weak* reference to the display and does not
/// close it.
///
/// FIXME(pcwalton): Unchecked weak references are bad and can violate memory safety. This is hard
/// to fix because the Display is given to us by the native windowing system, but we should fix it
/// someday.
///
/// FIXME(pcwalton): Mark nonsendable.
pub struct NativeCompositingGraphicsContext {
    display: *Display,
    visual_info: *XVisualInfo,
    framebuffer_configuration: Option<GLXFBConfig>,
}

impl NativeCompositingGraphicsContext {
    /// Chooses the compositor visual info using the same algorithm that the compositor uses.
    ///
    /// FIXME(pcwalton): It would be more robust to actually have the compositor pass the visual.
    fn compositor_visual_info(display: *Display) -> (*XVisualInfo, Option<GLXFBConfig>) {
        unsafe {
            let glx_display = cast::transmute(display);

            // CONSIDER:
            // In skia, they compute the GLX_ALPHA_SIZE minimum and request
            // that as well.

            let fbconfig_attributes = [
                GLX_DOUBLEBUFFER, 0,
                GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT | GLX_WINDOW_BIT,
                GLX_BIND_TO_TEXTURE_RGBA_EXT, 1,
                GLX_RENDER_TYPE, GLX_RGBA_BIT,
                0
            ];

            let screen = XDefaultScreen(display);
            let mut number_of_configs = 0;
            let configs = glXChooseFBConfig(glx_display, screen,
                                            &fbconfig_attributes[0], &mut number_of_configs);
            // NVidia drives have RGBA configurations that use 24-bit XVisual, not capable of
            // representing an alpha-channel in Pixmap form, so we look for the configuration
            // with a full set of 32 bits.
            for i in range(0, number_of_configs as int) {
                let config = *configs.offset(i);
                let visual_info : *XVisualInfo = cast::transmute(glXGetVisualFromFBConfig(glx_display, config));
                if (*visual_info).depth == 32 {
                    return (visual_info, Some(config))
                }
            }

            fail!("Unable to locate a GLX FB configuration that supports RGBA.");
        }
    }

    /// Creates a native graphics context from the given X display connection. This uses GLX. Only
    /// the compositor is allowed to call this.
    pub fn from_display(display: *Display) -> NativeCompositingGraphicsContext {
        let (visual_info, fbconfig) = NativeCompositingGraphicsContext::compositor_visual_info(display);

        NativeCompositingGraphicsContext {
            display: display,
            visual_info: visual_info,
            framebuffer_configuration: fbconfig,
        }
    }
}

/// The X display.
#[deriving(Clone)]
pub struct NativeGraphicsMetadata {
    pub display: *Display,
}

impl NativeGraphicsMetadata {
    /// Creates graphics metadata from a metadata descriptor.
    pub fn from_descriptor(descriptor: &NativeGraphicsMetadataDescriptor)
                           -> NativeGraphicsMetadata {
        // WARNING: We currently rely on the X display connection being the
        // same in both the Painting and Compositing contexts, as otherwise
        // the X Pixmap will not be sharable across them. Using this
        // method breaks that assumption.
        unsafe {
            let display = descriptor.display.with_c_str(|c_str| {
                    XOpenDisplay(c_str)
                });

            if display.is_null() {
                fail!("XOpenDisplay() failed!");
            }

            NativeGraphicsMetadata {
                display: display,
            }
        }
    }
}

/// A sendable form of the X display string.
#[deriving(Clone, Decodable, Encodable)]
pub struct NativeGraphicsMetadataDescriptor {
    display: ~str,
}

impl NativeGraphicsMetadataDescriptor {
    /// Creates a metadata descriptor from metadata.
    pub fn from_metadata(metadata: NativeGraphicsMetadata) -> NativeGraphicsMetadataDescriptor {
        unsafe {
            let c_str = CString::new(XDisplayString(metadata.display), false);
            NativeGraphicsMetadataDescriptor {
                display: c_str.as_str().unwrap().to_str(),
            }
        }
    }
}

#[deriving(Eq)]
pub enum NativeSurfaceTransientData {
    NoTransientData,
    RenderTaskTransientData(*Display, *XVisualInfo),
}

#[deriving(Decodable, Encodable)]
pub struct NativeSurface {
    /// The pixmap.
    pixmap: Pixmap,

    /// Whether this pixmap will leak if the destructor runs. This is for debugging purposes.
    will_leak: bool,
}

impl Drop for NativeSurface {
    fn drop(&mut self) {
        if self.will_leak {
            fail!("You should have disposed of the pixmap properly with destroy()! This pixmap \
                   will leak!");
        }
    }
}

impl NativeSurface {
    pub fn from_pixmap(pixmap: Pixmap) -> NativeSurface {
        NativeSurface {
            pixmap: pixmap,
            will_leak: true,
        }
    }
}

impl NativeSurfaceMethods for NativeSurface {
    fn new(native_context: &NativePaintingGraphicsContext, size: Size2D<i32>, stride: i32)
           -> NativeSurface {
        unsafe {
            // Create the pixmap.
            let screen = XDefaultScreen(native_context.display);
            let window = XRootWindow(native_context.display, screen);
            let pixmap = XCreatePixmap(native_context.display,
                                       window,
                                       size.width as c_uint,
                                       size.height as c_uint,
                                       ((stride / size.width) * 8) as c_uint);
            NativeSurface::from_pixmap(pixmap)
        }
    }

    /// This may only be called on the compositor side.
    fn bind_to_texture(&self,
                       native_context: &NativeCompositingGraphicsContext,
                       texture: &Texture,
                       _size: Size2D<int>) {
        unsafe {
            // Create the GLX pixmap.
            //
            // FIXME(pcwalton): RAII for exception safety?
            let pixmap_attributes = [
                GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT,
                GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGBA_EXT,
                0
            ];

            let glx_display = cast::transmute(native_context.display);

            let glx_pixmap = glXCreatePixmap(glx_display,
                                             native_context.framebuffer_configuration.expect(
                                                 "GLX 1.3 should have a framebuffer_configuration"),
                                             self.pixmap,
                                             &pixmap_attributes[0]);

            let glXBindTexImageEXT: extern "C" fn(*Display, GLXDrawable, c_int, *c_int) =
                cast::transmute(glXGetProcAddress(cast::transmute(&"glXBindTexImageEXT\x00"[0])));
            assert!(glXBindTexImageEXT as *c_void != ptr::null());
            let _bound = texture.bind();
            glXBindTexImageEXT(native_context.display,
                               cast::transmute(glx_pixmap),
                               GLX_FRONT_EXT,
                               ptr::null());
            assert_eq!(gl2::get_error(), NO_ERROR);

            // FIXME(pcwalton): Recycle these for speed?
            glXDestroyPixmap(glx_display, glx_pixmap);
        }
    }

    /// This may only be called on the painting side.
    fn upload(&self, graphics_context: &NativePaintingGraphicsContext, data: &[u8]) {
        unsafe {
            // Ensure that we're running on the render task. Take the display.
            let pixmap = self.pixmap;

            // Figure out the width, height, and depth of the pixmap.
            let mut root_window = 0;
            let mut x = 0;
            let mut y = 0;
            let mut width = 0;
            let mut height = 0;
            let mut border_width = 0;
            let mut depth = 0;
            let _ = XGetGeometry(graphics_context.display,
                                 cast::transmute(pixmap),
                                 &mut root_window,
                                 &mut x,
                                 &mut y,
                                 &mut width,
                                 &mut height,
                                 &mut border_width,
                                 &mut depth);

            // Create the image.
            let image = XCreateImage(graphics_context.display,
                                     (*graphics_context.visual_info).visual,
                                     depth,
                                     ZPixmap,
                                     0,
                                     cast::transmute(&data[0]),
                                     width as c_uint,
                                     height as c_uint,
                                     32,
                                     0);

            // Create the X graphics context.
            let gc = XCreateGC(graphics_context.display, pixmap, 0, ptr::null());

            // Draw the image.
            let _ = XPutImage(graphics_context.display,
                              pixmap,
                              gc,
                              image,
                              0,
                              0,
                              0,
                              0,
                              width,
                              height);
        }
    }

    fn get_id(&self) -> int {
        self.pixmap as int
    }

    fn destroy(&mut self, graphics_context: &NativePaintingGraphicsContext) {
        unsafe {
            assert!(self.pixmap != 0);
            XFreePixmap(graphics_context.display, self.pixmap);
            self.mark_wont_leak()
        }
    }

    fn mark_will_leak(&mut self) {
        self.will_leak = true
    }

    fn mark_wont_leak(&mut self) {
        self.will_leak = false
    }
}

<|endoftext|>


<commit_before>use ::std::sync::mpsc::Sender;

use ::pueue::message::*;
use ::pueue::state::SharedState;
use ::pueue::task::{Task, TaskStatus};

static SENDER_ERR: &str = "Failed to send message to task handler thread";

pub fn handle_message(message: Message, sender: &Sender<Message>, state: &SharedState) -> Message {
    match message {
        Message::Add(message) => add_task(message, sender, state),
        Message::Remove(message) => remove(message, state),
        Message::Start(message) => start(message, sender, state),
        Message::Restart(message) => restart(message, sender, state),

        Message::Pause(message) => pause(message, sender, state),
        Message::Stash(message) => stash(message, state),
        Message::Enqueue(message) => enqueue(message, state),
        Message::Kill(message) => kill(message, sender, state),

        Message::Send(message) => send(message, sender, state),
        Message::EditRequest(message) => edit_request(message, state),
        Message::Edit(message) => edit(message, state),

        Message::Clean => clean(state),
        Message::Reset => reset(sender),
        Message::Status => get_status(state),
        _ => create_failure_message("Not implemented yet"),
    }
}

/// Queues a new task to the state.
/// If the start_immediately flag is set, send a StartMessage to the task handler
fn add_task(message: AddMessage, sender: &Sender<Message>, state: &SharedState) -> Message {
    let task = Task::new(message.command, message.path);
    let task_id: i32;
    {
        let mut state = state.lock().unwrap();
        task_id = state.add_task(task);
    }
    if message.start_immediately {
        let start_message = StartMessage {
            task_ids: Some(vec![task_id]),
        };
        sender
            .send(Message::Start(start_message))
            .expect(SENDER_ERR);
    }

    create_success_message("New task added.")
}

/// Remove tasks from the queue
fn remove(message: RemoveMessage, state: &SharedState) -> Message {
    let (matching, mismatching) = {
        let statuses = vec![TaskStatus::Running, TaskStatus::Paused];
        let mut state = state.lock().unwrap();
        state.tasks_not_in_statuses(Some(message.task_ids), statuses)
    };

    let message = "Tasks removed from list";
    let message = compile_task_response(message, matching, mismatching);
    create_success_message(message)
}

/// Simply return the current state to the client
fn get_status(state: &SharedState) -> Message {
    let state = state.lock().unwrap();
    Message::StatusResponse(state.clone())
}

/// Forward the start message to the task handler and respond to the client
fn start(message: StartMessage, sender: &Sender<Message>, state: &SharedState) -> Message {
    sender
        .send(Message::Start(message.clone()))
        .expect(SENDER_ERR);
    if let Some(task_ids) = message.task_ids {
        let response = task_response_helper(
            "Tasks are being started",
            task_ids,
            vec![TaskStatus::Paused, TaskStatus::Queued, TaskStatus::Stashed],
            state,
        );
        return create_success_message(response);
    }

    return create_success_message("Daemon and all tasks are being resumed.");
}

/// Create and enqueue tasks from already finished tasks
/// The user can specify to immediately start the newly created tasks.
fn restart(message: RestartMessage, sender: &Sender<Message>, state: &SharedState) -> Message {
    let response: String;
    let new_ids = {
        let mut state = state.lock().unwrap();
        let statuses = vec![TaskStatus::Done, TaskStatus::Failed];
        let (matching, mismatching) = state.tasks_in_statuses(Some(message.task_ids), statuses);

        let mut new_ids = Vec::new();
        for task_id in &matching {
            let task = state.tasks.get(task_id).unwrap();
            let new_task = Task::from_task(task);
            new_ids.push(state.add_task(new_task));
        }

        // Already create the response string in here. Otherwise we would
        // need to get matching/mismatching out of this scope
        let message = "Restarted tasks";
        response = compile_task_response(message, matching, mismatching);

        new_ids
    };

    // If the restarted tasks should be started immediately, send a message
    // with the new task ids to the task handler.
    if message.start_immediately {
        let start_message = StartMessage {
            task_ids: Some(new_ids),
        };
        sender
            .send(Message::Start(start_message))
            .expect(SENDER_ERR);
    }

    return create_success_message(response);
}

/// Forward the pause message to the task handler and respond to the client
fn pause(message: PauseMessage, sender: &Sender<Message>, state: &SharedState) -> Message {
    sender
        .send(Message::Pause(message.clone()))
        .expect(SENDER_ERR);
    if let Some(task_ids) = message.task_ids {
        let response = task_response_helper(
            "Tasks are being paused",
            task_ids,
            vec![TaskStatus::Running],
            state,
        );
        return create_success_message(response);
    }

    return create_success_message("Daemon and all tasks are being paused.");
}

/// Stash specific queued tasks.
/// They won't be executed until enqueued again or explicitely started
fn stash(message: StashMessage, state: &SharedState) -> Message {
    let (matching, mismatching) = {
        let mut state = state.lock().unwrap();
        let (matching, mismatching) =
            state.tasks_in_statuses(Some(message.task_ids), vec![TaskStatus::Queued]);

        for task_id in &matching {
            state.change_status(*task_id, TaskStatus::Stashed);
        }

        (matching, mismatching)
    };

    let message = "Tasks are stashed";
    let response = compile_task_response(message, matching, mismatching);
    return create_success_message(response);
}

/// Enqueue specific stashed tasks.
/// They will be normally handled afterwards.
fn enqueue(message: EnqueueMessage, state: &SharedState) -> Message {
    let (matching, mismatching) = {
        let mut state = state.lock().unwrap();
        let (matching, mismatching) = state.tasks_in_statuses(
            Some(message.task_ids),
            vec![TaskStatus::Stashed, TaskStatus::Locked],
        );

        for task_id in &matching {
            state.change_status(*task_id, TaskStatus::Queued);
        }

        (matching, mismatching)
    };

    let message = "Tasks are enqueued";
    let response = compile_task_response(message, matching, mismatching);
    return create_success_message(response);
}

/// Forward the kill message to the task handler and respond to the client
fn kill(message: KillMessage, sender: &Sender<Message>, state: &SharedState) -> Message {
    sender
        .send(Message::Kill(message.clone()))
        .expect(SENDER_ERR);

    if !message.task_ids.is_empty() {
        let response = task_response_helper(
            "Tasks are being killed",
            message.task_ids,
            vec![TaskStatus::Running, TaskStatus::Paused],
            state,
        );
        return create_success_message(response);
    }

    return create_success_message("All tasks are being killed.");
}

/// Remove all failed or done tasks from the state
fn clean(state: &SharedState) -> Message {
    let mut state = state.lock().unwrap();
    let statuses = vec![TaskStatus::Done, TaskStatus::Failed];
    let (matching, _) = state.tasks_in_statuses(None, statuses);

    for task_id in &matching {
        let _ = state.tasks.remove(task_id).unwrap();
    }

    return create_success_message("All finished tasks have been removed");
}

// Forward the reset request to the task handler
// The handler then kills all children and clears the task queue
fn reset(sender: &Sender<Message>) -> Message {
    sender.send(Message::Reset).expect(SENDER_ERR);
    return create_success_message("Everything is being reset right now.");
}

fn task_response_helper(
    message: &'static str,
    task_ids: Vec<i32>,
    statuses: Vec<TaskStatus>,
    state: &SharedState,
) -> String {
    // Get all matching/mismatching task_ids for all given ids and statuses
    let (matching, mismatching) = {
        let mut state = state.lock().unwrap();
        state.tasks_in_statuses(Some(task_ids), statuses)
    };

    compile_task_response(message, matching, mismatching)
}

// Send some user defined input to a process
// The message will be forwarded to the task handler.
// In here we only do some error handling.
fn send(message: SendMessage, sender: &Sender<Message>, state: &SharedState) -> Message {
    // Check whether the task exists and is running, abort if that's not the case
    {
        let state = state.lock().unwrap();
        match state.tasks.get(&message.task_id) {
            Some(task) => {
                if task.status != TaskStatus::Running {
                    return create_failure_message("You can only send input to a running task");
                }
            }
            None => return create_failure_message("No task with this id."),
        }
    }

    // Check whether the task exists and is running, abort if that's not the case
    sender.send(Message::Send(message)).expect(SENDER_ERR);

    return create_success_message("Message is being send to the process.");
}

// If a user wants to edit a message, we need to send him the current command
// and lock the task to prevent execution, before the user has finished editing the command
fn edit_request(message: EditRequestMessage, state: &SharedState) -> Message {
    // Check whether the task exists and is queued/stashed, abort if that's not the case
    let mut state = state.lock().unwrap();
    match state.tasks.get_mut(&message.task_id) {
        Some(task) => {
            if !task.is_queued() {
                return create_failure_message("You can only send input to a queued/stashed task");
            }
            task.prev_status = task.status.clone();
            task.status = TaskStatus::Locked;

            let message = EditResponseMessage {
                task_id: task.id,
                command: task.command.clone(),
            };
            return Message::EditResponse(message);
        }
        None => return create_failure_message("No task with this id."),
    }
}

// Handle the actual updated command
fn edit(message: EditMessage, state: &SharedState) -> Message {
    // Check whether the task exists and is queued/stashed, abort if that's not the case
    let mut state = state.lock().unwrap();
    match state.tasks.get_mut(&message.task_id) {
        Some(task) => {
            if !(task.status == TaskStatus::Locked) {
                return create_failure_message("Task is no longer locked.");
            }

            task.status = task.prev_status.clone();
            task.command = message.command.clone();

            return create_success_message("Command has been updated");
        }
        None => {
            return create_failure_message(format!(
                "Task to edit has gone away: {}",
                message.task_id
            ))
        }
    }
}

/// Compile a response for instructions with multiple tasks ids
/// A custom message will be combined with a text about all matching tasks
/// and possibly tasks for which the instruction cannot be executed.
fn compile_task_response(
    message: &'static str,
    matching: Vec<i32>,
    mismatching: Vec<i32>,
) -> String {
    let matching: Vec<String> = matching.iter().map(|id| id.to_string()).collect();
    let mismatching: Vec<String> = mismatching.iter().map(|id| id.to_string()).collect();
    let matching_string = matching.join(", ");

    // We don't have any mismatching ids, return the simple message
    if mismatching.is_empty() {
        return format!("{}: {}", message, matching_string);
    }

    let mismatched_message = "The command couldn't be executed for these tasks";
    let mismatching_string = mismatching.join(", ");

    // All given ids are invalid
    if matching.is_empty() {
        return format!("{}: {}", mismatched_message, mismatching_string);
    }

    // Some ids were valid, some were invalid
    format!(
        "{}: {}\n{}: {}",
        message, matching_string, mismatched_message, mismatching_string
    )
}
<commit_msg>Implement switch command, code restructuring<commit_after>use ::std::sync::mpsc::Sender;

use ::pueue::message::*;
use ::pueue::state::SharedState;
use ::pueue::task::{Task, TaskStatus};

static SENDER_ERR: &str = "Failed to send message to task handler thread";

pub fn handle_message(message: Message, sender: &Sender<Message>, state: &SharedState) -> Message {
    match message {
        Message::Add(message) => add_task(message, sender, state),
        Message::Remove(message) => remove(message, state),
        Message::Switch(message) => switch(message, state),
        Message::Stash(message) => stash(message, state),
        Message::Enqueue(message) => enqueue(message, state),

        Message::Start(message) => start(message, sender, state),
        Message::Restart(message) => restart(message, sender, state),
        Message::Pause(message) => pause(message, sender, state),
        Message::Kill(message) => kill(message, sender, state),

        Message::Send(message) => send(message, sender, state),
        Message::EditRequest(message) => edit_request(message, state),
        Message::Edit(message) => edit(message, state),

        Message::Clean => clean(state),
        Message::Reset => reset(sender),
        Message::Status => get_status(state),
        _ => create_failure_message("Not implemented yet"),
    }
}

/// Queues a new task to the state.
/// If the start_immediately flag is set, send a StartMessage to the task handler
fn add_task(message: AddMessage, sender: &Sender<Message>, state: &SharedState) -> Message {
    let task = Task::new(message.command, message.path);
    let task_id: i32;
    {
        let mut state = state.lock().unwrap();
        task_id = state.add_task(task);
    }
    if message.start_immediately {
        let start_message = StartMessage {
            task_ids: Some(vec![task_id]),
        };
        sender
            .send(Message::Start(start_message))
            .expect(SENDER_ERR);
    }

    create_success_message("New task added.")
}

/// Remove tasks from the queue
fn remove(message: RemoveMessage, state: &SharedState) -> Message {
    let (matching, mismatching) = {
        let statuses = vec![TaskStatus::Running, TaskStatus::Paused];
        let mut state = state.lock().unwrap();
        state.tasks_not_in_statuses(Some(message.task_ids), statuses)
    };

    let message = "Tasks removed from list";
    let message = compile_task_response(message, matching, mismatching);
    create_success_message(message)
}

/// Switch the position of two tasks in the upcoming queue
fn switch(message: SwitchMessage, state: &SharedState) -> Message {
    let task_ids = vec![message.task_id_1, message.task_id_2];
    let statuses = vec![TaskStatus::Queued, TaskStatus::Stashed];
    let mut state = state.lock().unwrap();
    let (_, mismatching) = state.tasks_in_statuses(Some(task_ids.clone().to_vec()), statuses);
    if !mismatching.is_empty() {
        return create_failure_message("Tasks have to be either queued or stashed.");
    }

    // Get the tasks. Expect them to be there, since we found no mismatch
    let mut first_task = state.tasks.remove(&task_ids[0]).unwrap();
    let mut second_task = state.tasks.remove(&task_ids[1]).unwrap();

    // Switch task ids
    let temp_id = first_task.id;
    first_task.id = second_task.id;
    second_task.id = temp_id;

    // Put tasks back in again
    state.tasks.insert(first_task.id, first_task);
    state.tasks.insert(second_task.id, second_task);

    create_success_message("Tasks have been switched")
}

/// Stash specific queued tasks.
/// They won't be executed until enqueued again or explicitely started
fn stash(message: StashMessage, state: &SharedState) -> Message {
    let (matching, mismatching) = {
        let mut state = state.lock().unwrap();
        let (matching, mismatching) =
            state.tasks_in_statuses(Some(message.task_ids), vec![TaskStatus::Queued]);

        for task_id in &matching {
            state.change_status(*task_id, TaskStatus::Stashed);
        }

        (matching, mismatching)
    };

    let message = "Tasks are stashed";
    let response = compile_task_response(message, matching, mismatching);
    return create_success_message(response);
}

/// Enqueue specific stashed tasks.
/// They will be normally handled afterwards.
fn enqueue(message: EnqueueMessage, state: &SharedState) -> Message {
    let (matching, mismatching) = {
        let mut state = state.lock().unwrap();
        let (matching, mismatching) = state.tasks_in_statuses(
            Some(message.task_ids),
            vec![TaskStatus::Stashed, TaskStatus::Locked],
        );

        for task_id in &matching {
            state.change_status(*task_id, TaskStatus::Queued);
        }

        (matching, mismatching)
    };

    let message = "Tasks are enqueued";
    let response = compile_task_response(message, matching, mismatching);
    return create_success_message(response);
}

/// Forward the start message to the task handler and respond to the client
fn start(message: StartMessage, sender: &Sender<Message>, state: &SharedState) -> Message {
    sender
        .send(Message::Start(message.clone()))
        .expect(SENDER_ERR);
    if let Some(task_ids) = message.task_ids {
        let response = task_response_helper(
            "Tasks are being started",
            task_ids,
            vec![TaskStatus::Paused, TaskStatus::Queued, TaskStatus::Stashed],
            state,
        );
        return create_success_message(response);
    }

    return create_success_message("Daemon and all tasks are being resumed.");
}

/// Create and enqueue tasks from already finished tasks
/// The user can specify to immediately start the newly created tasks.
fn restart(message: RestartMessage, sender: &Sender<Message>, state: &SharedState) -> Message {
    let response: String;
    let new_ids = {
        let mut state = state.lock().unwrap();
        let statuses = vec![TaskStatus::Done, TaskStatus::Failed];
        let (matching, mismatching) = state.tasks_in_statuses(Some(message.task_ids), statuses);

        let mut new_ids = Vec::new();
        for task_id in &matching {
            let task = state.tasks.get(task_id).unwrap();
            let new_task = Task::from_task(task);
            new_ids.push(state.add_task(new_task));
        }

        // Already create the response string in here. Otherwise we would
        // need to get matching/mismatching out of this scope
        let message = "Restarted tasks";
        response = compile_task_response(message, matching, mismatching);

        new_ids
    };

    // If the restarted tasks should be started immediately, send a message
    // with the new task ids to the task handler.
    if message.start_immediately {
        let start_message = StartMessage {
            task_ids: Some(new_ids),
        };
        sender
            .send(Message::Start(start_message))
            .expect(SENDER_ERR);
    }

    return create_success_message(response);
}

/// Forward the pause message to the task handler and respond to the client
fn pause(message: PauseMessage, sender: &Sender<Message>, state: &SharedState) -> Message {
    sender
        .send(Message::Pause(message.clone()))
        .expect(SENDER_ERR);
    if let Some(task_ids) = message.task_ids {
        let response = task_response_helper(
            "Tasks are being paused",
            task_ids,
            vec![TaskStatus::Running],
            state,
        );
        return create_success_message(response);
    }

    return create_success_message("Daemon and all tasks are being paused.");
}

/// Forward the kill message to the task handler and respond to the client
fn kill(message: KillMessage, sender: &Sender<Message>, state: &SharedState) -> Message {
    sender
        .send(Message::Kill(message.clone()))
        .expect(SENDER_ERR);

    if !message.task_ids.is_empty() {
        let response = task_response_helper(
            "Tasks are being killed",
            message.task_ids,
            vec![TaskStatus::Running, TaskStatus::Paused],
            state,
        );
        return create_success_message(response);
    }

    return create_success_message("All tasks are being killed.");
}

// Send some user defined input to a process
// The message will be forwarded to the task handler.
// In here we only do some error handling.
fn send(message: SendMessage, sender: &Sender<Message>, state: &SharedState) -> Message {
    // Check whether the task exists and is running, abort if that's not the case
    {
        let state = state.lock().unwrap();
        match state.tasks.get(&message.task_id) {
            Some(task) => {
                if task.status != TaskStatus::Running {
                    return create_failure_message("You can only send input to a running task");
                }
            }
            None => return create_failure_message("No task with this id."),
        }
    }

    // Check whether the task exists and is running, abort if that's not the case
    sender.send(Message::Send(message)).expect(SENDER_ERR);

    return create_success_message("Message is being send to the process.");
}

// If a user wants to edit a message, we need to send him the current command
// and lock the task to prevent execution, before the user has finished editing the command
fn edit_request(message: EditRequestMessage, state: &SharedState) -> Message {
    // Check whether the task exists and is queued/stashed, abort if that's not the case
    let mut state = state.lock().unwrap();
    match state.tasks.get_mut(&message.task_id) {
        Some(task) => {
            if !task.is_queued() {
                return create_failure_message("You can only send input to a queued/stashed task");
            }
            task.prev_status = task.status.clone();
            task.status = TaskStatus::Locked;

            let message = EditResponseMessage {
                task_id: task.id,
                command: task.command.clone(),
            };
            return Message::EditResponse(message);
        }
        None => return create_failure_message("No task with this id."),
    }
}

// Handle the actual updated command
fn edit(message: EditMessage, state: &SharedState) -> Message {
    // Check whether the task exists and is queued/stashed, abort if that's not the case
    let mut state = state.lock().unwrap();
    match state.tasks.get_mut(&message.task_id) {
        Some(task) => {
            if !(task.status == TaskStatus::Locked) {
                return create_failure_message("Task is no longer locked.");
            }

            task.status = task.prev_status.clone();
            task.command = message.command.clone();

            return create_success_message("Command has been updated");
        }
        None => {
            return create_failure_message(format!(
                "Task to edit has gone away: {}",
                message.task_id
            ))
        }
    }
}

/// Remove all failed or done tasks from the state
fn clean(state: &SharedState) -> Message {
    let mut state = state.lock().unwrap();
    let statuses = vec![TaskStatus::Done, TaskStatus::Failed];
    let (matching, _) = state.tasks_in_statuses(None, statuses);

    for task_id in &matching {
        let _ = state.tasks.remove(task_id).unwrap();
    }

    return create_success_message("All finished tasks have been removed");
}

// Forward the reset request to the task handler
// The handler then kills all children and clears the task queue
fn reset(sender: &Sender<Message>) -> Message {
    sender.send(Message::Reset).expect(SENDER_ERR);
    return create_success_message("Everything is being reset right now.");
}

/// Simply return the current state to the client
fn get_status(state: &SharedState) -> Message {
    let state = state.lock().unwrap();
    Message::StatusResponse(state.clone())
}

fn task_response_helper(
    message: &'static str,
    task_ids: Vec<i32>,
    statuses: Vec<TaskStatus>,
    state: &SharedState,
) -> String {
    // Get all matching/mismatching task_ids for all given ids and statuses
    let (matching, mismatching) = {
        let mut state = state.lock().unwrap();
        state.tasks_in_statuses(Some(task_ids), statuses)
    };

    compile_task_response(message, matching, mismatching)
}

/// Compile a response for instructions with multiple tasks ids
/// A custom message will be combined with a text about all matching tasks
/// and possibly tasks for which the instruction cannot be executed.
fn compile_task_response(
    message: &'static str,
    matching: Vec<i32>,
    mismatching: Vec<i32>,
) -> String {
    let matching: Vec<String> = matching.iter().map(|id| id.to_string()).collect();
    let mismatching: Vec<String> = mismatching.iter().map(|id| id.to_string()).collect();
    let matching_string = matching.join(", ");

    // We don't have any mismatching ids, return the simple message
    if mismatching.is_empty() {
        return format!("{}: {}", message, matching_string);
    }

    let mismatched_message = "The command couldn't be executed for these tasks";
    let mismatching_string = mismatching.join(", ");

    // All given ids are invalid
    if matching.is_empty() {
        return format!("{}: {}", mismatched_message, mismatching_string);
    }

    // Some ids were valid, some were invalid
    format!(
        "{}: {}\n{}: {}",
        message, matching_string, mismatched_message, mismatching_string
    )
}
<|endoftext|>


<commit_before>use std::error::Error as StdError;
use std::marker::Unpin;

use pin_project::{pin_project, project};
use h2::Reason;
use h2::server::{Builder, Connection, Handshake, SendResponse};
use tokio_io::{AsyncRead, AsyncWrite};

use crate::body::Payload;
use crate::common::exec::H2Exec;
use crate::common::{Future, Pin, Poll, task};
use crate::headers;
use crate::headers::content_length_parse_all;
use crate::service::HttpService;
use crate::proto::Dispatched;
use super::{PipeToSendStream, SendBuf};

use crate::{Body, Response};

pub(crate) struct Server<T, S, B, E>
where
    S: HttpService<Body>,
    B: Payload,
{
    exec: E,
    service: S,
    state: State<T, B>,
}

// TODO: fix me
impl<T, S: HttpService<Body>, B: Payload, E> Unpin for Server<T, S, B, E> {}

enum State<T, B>
where
    B: Payload,
{
    Handshaking(Handshake<T, SendBuf<B::Data>>),
    Serving(Serving<T, B>),
    Closed,
}

struct Serving<T, B>
where
    B: Payload,
{
    conn: Connection<T, SendBuf<B::Data>>,
    closing: Option<crate::Error>,
}

impl<T, S, B, E> Server<T, S, B, E>
where
    T: AsyncRead + AsyncWrite + Unpin,
    S: HttpService<Body, ResBody=B>,
    S::Error: Into<Box<dyn StdError + Send + Sync>>,
    B: Payload,
    B::Data: Unpin,
    E: H2Exec<S::Future, B>,
{
    pub(crate) fn new(io: T, service: S, builder: &Builder, exec: E) -> Server<T, S, B, E> {
        let handshake = builder.handshake(io);
        Server {
            exec,
            state: State::Handshaking(handshake),
            service,
        }
    }

    pub fn graceful_shutdown(&mut self) {
        trace!("graceful_shutdown");
        match self.state {
            State::Handshaking(..) => {
                // fall-through, to replace state with Closed
            },
            State::Serving(ref mut srv) => {
                if srv.closing.is_none() {
                    srv.conn.graceful_shutdown();
                }
                return;
            },
            State::Closed => {
                return;
            }
        }
        self.state = State::Closed;
    }
}

impl<T, S, B, E> Future for Server<T, S, B, E>
where
    T: AsyncRead + AsyncWrite + Unpin,
    S: HttpService<Body, ResBody=B>,
    S::Error: Into<Box<dyn StdError + Send + Sync>>,
    B: Payload,
    B::Data: Unpin,
    E: H2Exec<S::Future, B>,
{
    type Output = crate::Result<Dispatched>;

    fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
        let me = &mut *self;
        loop {
            let next = match me.state {
                State::Handshaking(ref mut h) => {
                    let conn = ready!(Pin::new(h).poll(cx).map_err(crate::Error::new_h2))?;
                    State::Serving(Serving {
                        conn,
                        closing: None,
                    })
                },
                State::Serving(ref mut srv) => {
                    ready!(srv.poll_server(cx, &mut me.service, &mut me.exec))?;
                    return Poll::Ready(Ok(Dispatched::Shutdown));
                }
                State::Closed => {
                    // graceful_shutdown was called before handshaking finished,
                    // nothing to do here...
                    return Poll::Ready(Ok(Dispatched::Shutdown));
                }
            };
            me.state = next;
        }
    }
}

impl<T, B> Serving<T, B>
where
    T: AsyncRead + AsyncWrite + Unpin,
    B: Payload,
    B::Data: Unpin,
{
    fn poll_server<S, E>(&mut self, cx: &mut task::Context<'_>, service: &mut S, exec: &mut E) -> Poll<crate::Result<()>>
    where
        S: HttpService<
            Body,
            ResBody=B,
        >,
        S::Error: Into<Box<dyn StdError + Send + Sync>>,
        E: H2Exec<S::Future, B>,
    {
        if self.closing.is_none() {
            loop {
                // At first, polls the readiness of supplied service.
                match service.poll_ready(cx) {
                    Poll::Ready(Ok(())) => (),
                    Poll::Pending => {
                        // use `poll_closed` instead of `poll_accept`,
                        // in order to avoid accepting a request.
                        ready!(self.conn.poll_closed(cx).map_err(crate::Error::new_h2))?;
                        trace!("incoming connection complete");
                        return Poll::Ready(Ok(()));
                    }
                    Poll::Ready(Err(err)) => {
                        let err = crate::Error::new_user_service(err);
                        debug!("service closed: {}", err);

                        let reason = err.h2_reason();
                        if reason == Reason::NO_ERROR {
                            // NO_ERROR is only used for graceful shutdowns...
                            trace!("interpretting NO_ERROR user error as graceful_shutdown");
                            self.conn.graceful_shutdown();
                        } else {
                            trace!("abruptly shutting down with {:?}", reason);
                            self.conn.abrupt_shutdown(reason);
                        }
                        self.closing = Some(err);
                        break;
                    }
                }

                // When the service is ready, accepts an incoming request.
                match ready!(self.conn.poll_accept(cx)) {
                    Some(Ok((req, respond))) => {
                        trace!("incoming request");
                        let content_length = content_length_parse_all(req.headers());
                        let req = req.map(|stream| {
                            crate::Body::h2(stream, content_length)
                        });
                        let fut = H2Stream::new(service.call(req), respond);
                        exec.execute_h2stream(fut)?;
                    },
                    Some(Err(e)) => {
                        return Poll::Ready(Err(crate::Error::new_h2(e)));
                    },
                    None => {
                        // no more incoming streams...
                        trace!("incoming connection complete");
                        return Poll::Ready(Ok(()));
                    },
                }
            }
        }

        debug_assert!(self.closing.is_some(), "poll_server broke loop without closing");

        ready!(self.conn.poll_closed(cx).map_err(crate::Error::new_h2))?;

        Poll::Ready(Err(self.closing.take().expect("polled after error")))
    }
}

#[allow(missing_debug_implementations)]
#[pin_project]
pub struct H2Stream<F, B>
where
    B: Payload,
{
    reply: SendResponse<SendBuf<B::Data>>,
    #[pin]
    state: H2StreamState<F, B>,
}

#[pin_project]
enum H2StreamState<F, B>
where
    B: Payload,
{
    Service(#[pin] F),
    Body(PipeToSendStream<B>),
}

impl<F, B> H2Stream<F, B>
where
    //F: Future<Item=Response<B>>,
    //F::Error: Into<Box<dyn StdError + Send + Sync>>,
    B: Payload,
{
    fn new(fut: F, respond: SendResponse<SendBuf<B::Data>>) -> H2Stream<F, B> {
        H2Stream {
            reply: respond,
            state: H2StreamState::Service(fut),
        }
    }
}

macro_rules! reply {
    ($me:expr, $res:expr, $eos:expr) => ({
        match $me.reply.send_response($res, $eos) {
            Ok(tx) => tx,
            Err(e) => {
                debug!("send response error: {}", e);
                $me.reply.send_reset(Reason::INTERNAL_ERROR);
                return Poll::Ready(Err(crate::Error::new_h2(e)));
            }
        }
    })
}

impl<F, B, E> H2Stream<F, B>
where
    F: Future<Output = Result<Response<B>, E>>,
    B: Payload + Unpin,
    B::Data: Unpin,
    E: Into<Box<dyn StdError + Send + Sync>>,
{
    #[project]
    fn poll2(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<crate::Result<()>> {
        let mut me = self.project();
        loop {
            #[project]
            let next = match me.state.as_mut().project() {
                H2StreamState::Service(h) => {
                    let res = match h.poll(cx) {
                        Poll::Ready(Ok(r)) => r,
                        Poll::Pending => {
                            // Response is not yet ready, so we want to check if the client has sent a
                            // RST_STREAM frame which would cancel the current request.
                            if let Poll::Ready(reason) =
                                me.reply.poll_reset(cx).map_err(|e| crate::Error::new_h2(e))?
                            {
                                debug!("stream received RST_STREAM: {:?}", reason);
                                return Poll::Ready(Err(crate::Error::new_h2(reason.into())));
                            }
                            return Poll::Pending;
                        }
                        Poll::Ready(Err(e)) => {
                            let err = crate::Error::new_user_service(e);
                            warn!("http2 service errored: {}", err);
                            me.reply.send_reset(err.h2_reason());
                            return Poll::Ready(Err(err));
                        },
                    };

                    let (head, body) = res.into_parts();
                    let mut res = ::http::Response::from_parts(head, ());
                    super::strip_connection_headers(res.headers_mut(), false);

                    // set Date header if it isn't already set...
                    res
                        .headers_mut()
                        .entry(::http::header::DATE)
                        .expect("DATE is a valid HeaderName")
                        .or_insert_with(crate::proto::h1::date::update_and_header_value);

                    // automatically set Content-Length from body...
                    if let Some(len) = body.size_hint().exact() {
                        headers::set_content_length_if_missing(res.headers_mut(), len);
                    }

                    if !body.is_end_stream() {
                        let body_tx = reply!(me, res, false);
                        H2StreamState::Body(PipeToSendStream::new(body, body_tx))
                    } else {
                        reply!(me, res, true);
                        return Poll::Ready(Ok(()));
                    }
                },
                H2StreamState::Body(ref mut pipe) => {
                    return Pin::new(pipe).poll(cx);
                }
            };
            me.state.set(next);
        }
    }
}

impl<F, B, E> Future for H2Stream<F, B>
where
    F: Future<Output = Result<Response<B>, E>>,
    B: Payload + Unpin,
    B::Data: Unpin,
    E: Into<Box<dyn StdError + Send + Sync>>,
{
    type Output = ();

    fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
        self.poll2(cx).map(|res| {
            if let Err(e) = res {
                debug!("stream error: {}", e);
            }
        })
    }
}
<commit_msg>refactor(http2): replace server Unpin impl with pin_project<commit_after>use std::error::Error as StdError;
use std::marker::Unpin;

use pin_project::{pin_project, project};
use h2::Reason;
use h2::server::{Builder, Connection, Handshake, SendResponse};
use tokio_io::{AsyncRead, AsyncWrite};

use crate::body::Payload;
use crate::common::exec::H2Exec;
use crate::common::{Future, Pin, Poll, task};
use crate::headers;
use crate::headers::content_length_parse_all;
use crate::service::HttpService;
use crate::proto::Dispatched;
use super::{PipeToSendStream, SendBuf};

use crate::{Body, Response};

#[pin_project]
pub(crate) struct Server<T, S, B, E>
where
    S: HttpService<Body>,
    B: Payload,
{
    exec: E,
    service: S,
    state: State<T, B>,
}

enum State<T, B>
where
    B: Payload,
{
    Handshaking(Handshake<T, SendBuf<B::Data>>),
    Serving(Serving<T, B>),
    Closed,
}

struct Serving<T, B>
where
    B: Payload,
{
    conn: Connection<T, SendBuf<B::Data>>,
    closing: Option<crate::Error>,
}

impl<T, S, B, E> Server<T, S, B, E>
where
    T: AsyncRead + AsyncWrite + Unpin,
    S: HttpService<Body, ResBody=B>,
    S::Error: Into<Box<dyn StdError + Send + Sync>>,
    B: Payload,
    B::Data: Unpin,
    E: H2Exec<S::Future, B>,
{
    pub(crate) fn new(io: T, service: S, builder: &Builder, exec: E) -> Server<T, S, B, E> {
        let handshake = builder.handshake(io);
        Server {
            exec,
            state: State::Handshaking(handshake),
            service,
        }
    }

    pub fn graceful_shutdown(&mut self) {
        trace!("graceful_shutdown");
        match self.state {
            State::Handshaking(..) => {
                // fall-through, to replace state with Closed
            },
            State::Serving(ref mut srv) => {
                if srv.closing.is_none() {
                    srv.conn.graceful_shutdown();
                }
                return;
            },
            State::Closed => {
                return;
            }
        }
        self.state = State::Closed;
    }
}

impl<T, S, B, E> Future for Server<T, S, B, E>
where
    T: AsyncRead + AsyncWrite + Unpin,
    S: HttpService<Body, ResBody=B>,
    S::Error: Into<Box<dyn StdError + Send + Sync>>,
    B: Payload,
    B::Data: Unpin,
    E: H2Exec<S::Future, B>,
{
    type Output = crate::Result<Dispatched>;

    fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
        let me = &mut *self;
        loop {
            let next = match me.state {
                State::Handshaking(ref mut h) => {
                    let conn = ready!(Pin::new(h).poll(cx).map_err(crate::Error::new_h2))?;
                    State::Serving(Serving {
                        conn,
                        closing: None,
                    })
                },
                State::Serving(ref mut srv) => {
                    ready!(srv.poll_server(cx, &mut me.service, &mut me.exec))?;
                    return Poll::Ready(Ok(Dispatched::Shutdown));
                }
                State::Closed => {
                    // graceful_shutdown was called before handshaking finished,
                    // nothing to do here...
                    return Poll::Ready(Ok(Dispatched::Shutdown));
                }
            };
            me.state = next;
        }
    }
}

impl<T, B> Serving<T, B>
where
    T: AsyncRead + AsyncWrite + Unpin,
    B: Payload,
    B::Data: Unpin,
{
    fn poll_server<S, E>(&mut self, cx: &mut task::Context<'_>, service: &mut S, exec: &mut E) -> Poll<crate::Result<()>>
    where
        S: HttpService<
            Body,
            ResBody=B,
        >,
        S::Error: Into<Box<dyn StdError + Send + Sync>>,
        E: H2Exec<S::Future, B>,
    {
        if self.closing.is_none() {
            loop {
                // At first, polls the readiness of supplied service.
                match service.poll_ready(cx) {
                    Poll::Ready(Ok(())) => (),
                    Poll::Pending => {
                        // use `poll_closed` instead of `poll_accept`,
                        // in order to avoid accepting a request.
                        ready!(self.conn.poll_closed(cx).map_err(crate::Error::new_h2))?;
                        trace!("incoming connection complete");
                        return Poll::Ready(Ok(()));
                    }
                    Poll::Ready(Err(err)) => {
                        let err = crate::Error::new_user_service(err);
                        debug!("service closed: {}", err);

                        let reason = err.h2_reason();
                        if reason == Reason::NO_ERROR {
                            // NO_ERROR is only used for graceful shutdowns...
                            trace!("interpretting NO_ERROR user error as graceful_shutdown");
                            self.conn.graceful_shutdown();
                        } else {
                            trace!("abruptly shutting down with {:?}", reason);
                            self.conn.abrupt_shutdown(reason);
                        }
                        self.closing = Some(err);
                        break;
                    }
                }

                // When the service is ready, accepts an incoming request.
                match ready!(self.conn.poll_accept(cx)) {
                    Some(Ok((req, respond))) => {
                        trace!("incoming request");
                        let content_length = content_length_parse_all(req.headers());
                        let req = req.map(|stream| {
                            crate::Body::h2(stream, content_length)
                        });
                        let fut = H2Stream::new(service.call(req), respond);
                        exec.execute_h2stream(fut)?;
                    },
                    Some(Err(e)) => {
                        return Poll::Ready(Err(crate::Error::new_h2(e)));
                    },
                    None => {
                        // no more incoming streams...
                        trace!("incoming connection complete");
                        return Poll::Ready(Ok(()));
                    },
                }
            }
        }

        debug_assert!(self.closing.is_some(), "poll_server broke loop without closing");

        ready!(self.conn.poll_closed(cx).map_err(crate::Error::new_h2))?;

        Poll::Ready(Err(self.closing.take().expect("polled after error")))
    }
}

#[allow(missing_debug_implementations)]
#[pin_project]
pub struct H2Stream<F, B>
where
    B: Payload,
{
    reply: SendResponse<SendBuf<B::Data>>,
    #[pin]
    state: H2StreamState<F, B>,
}

#[pin_project]
enum H2StreamState<F, B>
where
    B: Payload,
{
    Service(#[pin] F),
    Body(PipeToSendStream<B>),
}

impl<F, B> H2Stream<F, B>
where
    B: Payload,
{
    fn new(fut: F, respond: SendResponse<SendBuf<B::Data>>) -> H2Stream<F, B> {
        H2Stream {
            reply: respond,
            state: H2StreamState::Service(fut),
        }
    }
}

macro_rules! reply {
    ($me:expr, $res:expr, $eos:expr) => ({
        match $me.reply.send_response($res, $eos) {
            Ok(tx) => tx,
            Err(e) => {
                debug!("send response error: {}", e);
                $me.reply.send_reset(Reason::INTERNAL_ERROR);
                return Poll::Ready(Err(crate::Error::new_h2(e)));
            }
        }
    })
}

impl<F, B, E> H2Stream<F, B>
where
    F: Future<Output = Result<Response<B>, E>>,
    B: Payload + Unpin,
    B::Data: Unpin,
    E: Into<Box<dyn StdError + Send + Sync>>,
{
    #[project]
    fn poll2(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<crate::Result<()>> {
        let mut me = self.project();
        loop {
            #[project]
            let next = match me.state.as_mut().project() {
                H2StreamState::Service(h) => {
                    let res = match h.poll(cx) {
                        Poll::Ready(Ok(r)) => r,
                        Poll::Pending => {
                            // Response is not yet ready, so we want to check if the client has sent a
                            // RST_STREAM frame which would cancel the current request.
                            if let Poll::Ready(reason) =
                                me.reply.poll_reset(cx).map_err(|e| crate::Error::new_h2(e))?
                            {
                                debug!("stream received RST_STREAM: {:?}", reason);
                                return Poll::Ready(Err(crate::Error::new_h2(reason.into())));
                            }
                            return Poll::Pending;
                        }
                        Poll::Ready(Err(e)) => {
                            let err = crate::Error::new_user_service(e);
                            warn!("http2 service errored: {}", err);
                            me.reply.send_reset(err.h2_reason());
                            return Poll::Ready(Err(err));
                        },
                    };

                    let (head, body) = res.into_parts();
                    let mut res = ::http::Response::from_parts(head, ());
                    super::strip_connection_headers(res.headers_mut(), false);

                    // set Date header if it isn't already set...
                    res
                        .headers_mut()
                        .entry(::http::header::DATE)
                        .expect("DATE is a valid HeaderName")
                        .or_insert_with(crate::proto::h1::date::update_and_header_value);

                    // automatically set Content-Length from body...
                    if let Some(len) = body.size_hint().exact() {
                        headers::set_content_length_if_missing(res.headers_mut(), len);
                    }

                    if !body.is_end_stream() {
                        let body_tx = reply!(me, res, false);
                        H2StreamState::Body(PipeToSendStream::new(body, body_tx))
                    } else {
                        reply!(me, res, true);
                        return Poll::Ready(Ok(()));
                    }
                },
                H2StreamState::Body(ref mut pipe) => {
                    return Pin::new(pipe).poll(cx);
                }
            };
            me.state.set(next);
        }
    }
}

impl<F, B, E> Future for H2Stream<F, B>
where
    F: Future<Output = Result<Response<B>, E>>,
    B: Payload + Unpin,
    B::Data: Unpin,
    E: Into<Box<dyn StdError + Send + Sync>>,
{
    type Output = ();

    fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
        self.poll2(cx).map(|res| {
            if let Err(e) = res {
                debug!("stream error: {}", e);
            }
        })
    }
}
<|endoftext|>