<commit_before>use std::fmt;
use std::mem;
use std::net::Ipv6Addr;
use std::os::raw::{
    c_int,
    c_uchar,
    c_void,
};
use std::ptr;
use std::slice;

use c_ares_sys;
use itertools::Itertools;

use error::{
    Error,
    Result,
};
use panic;
use types::MAX_ADDRTTLS;

/// The result of a successful AAAA lookup.
#[derive(Clone, Copy)]
pub struct AAAAResults {
    naddr6ttls: usize,
    addr6ttls: [c_ares_sys::ares_addr6ttl; MAX_ADDRTTLS],
}

/// The contents of a single AAAA record.
#[derive(Clone, Copy)]
pub struct AAAAResult<'a> {
    addr6ttl: &'a c_ares_sys::ares_addr6ttl,
}

impl AAAAResults {
    /// Obtain an `AAAAResults` from the response to an AAAA lookup.
    pub fn parse_from(data: &[u8]) -> Result<AAAAResults> {
        let mut results: AAAAResults = AAAAResults {
            naddr6ttls: MAX_ADDRTTLS,
            addr6ttls: unsafe { mem::uninitialized() },
        };
        let parse_status = unsafe {
            c_ares_sys::ares_parse_aaaa_reply(
                data.as_ptr(),
                data.len() as c_int,
                ptr::null_mut(),
                results.addr6ttls.as_mut_ptr(),
                &mut results.naddr6ttls as *mut _ as *mut c_int)
        };
        if parse_status == c_ares_sys::ARES_SUCCESS {
            Ok(results)
        } else {
            Err(Error::from(parse_status))
        }
    }

    /// Returns an iterator over the `AAAAResult` values in this `AAAAResults`.
    pub fn iter(&self) -> AAAAResultsIter {
        AAAAResultsIter {
            addr6ttls: self.addr6ttls[0 .. self.naddr6ttls].iter()
        }
    }
}

impl fmt::Display for AAAAResults {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        let results = self.iter().format("}, {");
        write!(fmt, "[{{{}}}]", results)?;
        Ok(())
    }
}

/// Iterator of `AAAAResult`s.
#[derive(Clone)]
pub struct AAAAResultsIter<'a> {
    addr6ttls: slice::Iter<'a, c_ares_sys::ares_addr6ttl>,
}

impl<'a> Iterator for AAAAResultsIter<'a> {
    type Item = AAAAResult<'a>;
    fn next(&mut self) -> Option<Self::Item> {
        self.addr6ttls.next().map(|addr6ttl| AAAAResult { addr6ttl: addr6ttl })
    }
}

impl<'a> IntoIterator for &'a AAAAResults {
    type Item = AAAAResult<'a>;
    type IntoIter = AAAAResultsIter<'a>;

    fn into_iter(self) -> Self::IntoIter {
        self.iter()
    }
}

impl<'a> AAAAResult<'a> {
    /// Returns the IPv6 address in this `AAAAResult`.
    #[cfg_attr(feature="clippy", allow(used_underscore_binding))]
    pub fn ipv6(&self) -> Ipv6Addr {
        let bytes = unsafe { self.addr6ttl.ip6addr._S6_un._S6_u8 };
        Ipv6Addr::from(bytes)
    }

    /// Returns the time-to-live in this `AAAAResult`.
    pub fn ttl(&self) -> i32 {
        self.addr6ttl.ttl as i32
    }
}

impl<'a> fmt::Display for AAAAResult<'a> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt, "IPv6: {}, ", self.ipv6())?;
        write!(fmt, "TTL: {}", self.ttl())?;
        Ok(())
    }
}

pub unsafe extern "C" fn query_aaaa_callback<F>(
    arg: *mut c_void,
    status: c_int,
    _timeouts: c_int,
    abuf: *mut c_uchar,
    alen: c_int)
    where F: FnOnce(Result<AAAAResults>) + Send + 'static {
    panic::catch(|| {
        let result = if status == c_ares_sys::ARES_SUCCESS {
            let data = slice::from_raw_parts(abuf, alen as usize);
            AAAAResults::parse_from(data)
        } else {
            Err(Error::from(status))
        };
        let handler = Box::from_raw(arg as *mut F);
        handler(result);
    });
}
<commit_msg>Apparently clippy doesn't care about the unused thing any more<commit_after>use std::fmt;
use std::mem;
use std::net::Ipv6Addr;
use std::os::raw::{
    c_int,
    c_uchar,
    c_void,
};
use std::ptr;
use std::slice;

use c_ares_sys;
use itertools::Itertools;

use error::{
    Error,
    Result,
};
use panic;
use types::MAX_ADDRTTLS;

/// The result of a successful AAAA lookup.
#[derive(Clone, Copy)]
pub struct AAAAResults {
    naddr6ttls: usize,
    addr6ttls: [c_ares_sys::ares_addr6ttl; MAX_ADDRTTLS],
}

/// The contents of a single AAAA record.
#[derive(Clone, Copy)]
pub struct AAAAResult<'a> {
    addr6ttl: &'a c_ares_sys::ares_addr6ttl,
}

impl AAAAResults {
    /// Obtain an `AAAAResults` from the response to an AAAA lookup.
    pub fn parse_from(data: &[u8]) -> Result<AAAAResults> {
        let mut results: AAAAResults = AAAAResults {
            naddr6ttls: MAX_ADDRTTLS,
            addr6ttls: unsafe { mem::uninitialized() },
        };
        let parse_status = unsafe {
            c_ares_sys::ares_parse_aaaa_reply(
                data.as_ptr(),
                data.len() as c_int,
                ptr::null_mut(),
                results.addr6ttls.as_mut_ptr(),
                &mut results.naddr6ttls as *mut _ as *mut c_int)
        };
        if parse_status == c_ares_sys::ARES_SUCCESS {
            Ok(results)
        } else {
            Err(Error::from(parse_status))
        }
    }

    /// Returns an iterator over the `AAAAResult` values in this `AAAAResults`.
    pub fn iter(&self) -> AAAAResultsIter {
        AAAAResultsIter {
            addr6ttls: self.addr6ttls[0 .. self.naddr6ttls].iter()
        }
    }
}

impl fmt::Display for AAAAResults {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        let results = self.iter().format("}, {");
        write!(fmt, "[{{{}}}]", results)?;
        Ok(())
    }
}

/// Iterator of `AAAAResult`s.
#[derive(Clone)]
pub struct AAAAResultsIter<'a> {
    addr6ttls: slice::Iter<'a, c_ares_sys::ares_addr6ttl>,
}

impl<'a> Iterator for AAAAResultsIter<'a> {
    type Item = AAAAResult<'a>;
    fn next(&mut self) -> Option<Self::Item> {
        self.addr6ttls.next().map(|addr6ttl| AAAAResult { addr6ttl: addr6ttl })
    }
}

impl<'a> IntoIterator for &'a AAAAResults {
    type Item = AAAAResult<'a>;
    type IntoIter = AAAAResultsIter<'a>;

    fn into_iter(self) -> Self::IntoIter {
        self.iter()
    }
}

impl<'a> AAAAResult<'a> {
    /// Returns the IPv6 address in this `AAAAResult`.
    pub fn ipv6(&self) -> Ipv6Addr {
        let bytes = unsafe { self.addr6ttl.ip6addr._S6_un._S6_u8 };
        Ipv6Addr::from(bytes)
    }

    /// Returns the time-to-live in this `AAAAResult`.
    pub fn ttl(&self) -> i32 {
        self.addr6ttl.ttl as i32
    }
}

impl<'a> fmt::Display for AAAAResult<'a> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt, "IPv6: {}, ", self.ipv6())?;
        write!(fmt, "TTL: {}", self.ttl())?;
        Ok(())
    }
}

pub unsafe extern "C" fn query_aaaa_callback<F>(
    arg: *mut c_void,
    status: c_int,
    _timeouts: c_int,
    abuf: *mut c_uchar,
    alen: c_int)
    where F: FnOnce(Result<AAAAResults>) + Send + 'static {
    panic::catch(|| {
        let result = if status == c_ares_sys::ARES_SUCCESS {
            let data = slice::from_raw_parts(abuf, alen as usize);
            AAAAResults::parse_from(data)
        } else {
            Err(Error::from(status))
        };
        let handler = Box::from_raw(arg as *mut F);
        handler(result);
    });
}
<|endoftext|>


<commit_before>mod wall;
mod cell;
mod maze;
mod grid;
mod solver;

extern crate turtle;

use turtle::{self, Turtle, Color};

pub use maze::Maze;
use grid::{GridCellIter};
use solver::solve;

// Dimensions of the maze in turtle steps (pixels)
const WIDTH: f64 = 600.0; // px
const HEIGHT: f64 = 600.0; // px

fn main() {
    turtle::start();

    let maze = Maze::generate();

    let mut turtle = Turtle::new();
    turtle.set_speed(8);
    turtle.set_background_color("#BDBDBD");
    turtle.set_pen_color("#03A9F4");
    turtle.set_pen_size(2.0);

    // Get to the top left corner
    turtle.pen_up();
    turtle.forward(HEIGHT / 2.0);
    turtle.right(90.0);
    turtle.backward(WIDTH / 2.0);
    turtle.pen_down();

    let cell_width = WIDTH/(maze.row_size() as f64);
    let cell_height = HEIGHT/(maze.col_size() as f64);

    // Draw rows
    draw_rows(
        &mut turtle,
        cell_width,
        cell_height,
        maze.first_row().map(|cell| cell.north.is_closed()),
        maze.rows(),
        |row| row.map(|cell| cell.south.is_closed()),
        false,
    );

    // Draw columns
    turtle.left(90.0);
    draw_rows(
        &mut turtle,
        cell_height,
        cell_width,
        maze.last_col().rev().map(|cell| cell.east.is_closed()),
        maze.cols().rev(),
        |col| col.map(|cell| cell.west.is_closed()).rev(),
        true,
    );

    turtle.right(90.0);
    draw_marker(&mut turtle, maze.start(), cell_width, cell_height, "#C5E1A5");
    draw_marker(&mut turtle, maze.finish(), cell_width, cell_height, "#FFAB91");

    turtle.pen_up();
    turtle.forward(cell_width * 0.5);
    turtle.right(90.0);
    turtle.forward(cell_height * 0.5);
    turtle.left(90.0);
    turtle.pen_down();

    turtle.set_speed(5);
    turtle.set_pen_size(2.0);
    solve(&mut turtle, maze, cell_width, cell_height);
}

fn draw_rows<
    'a,
    R: Iterator<Item=bool>,
    G: Iterator<Item=GridCellIter<'a>>,
    F: Fn(GridCellIter<'a>) -> X,
    X: Iterator<Item=bool> + DoubleEndedIterator
>(
    turtle: &mut Turtle,
    // size of each cell in the row
    cell_size: f64,
    // gap between rows
    cell_gap: f64,
    first_row: R,
    rows: G,
    row_walls: F,
    rotate_left: bool,
) {
    draw_row(turtle, cell_size, first_row);

    // Direction of rotation for all turns
    let rotation = if rotate_left { -1.0 } else { 1.0 };
    for (i, row) in rows.enumerate() {
        turtle.pen_up();

        // Direction of rotation for these turns
        let direction = rotation * if i % 2 == 0 { 1.0 } else { -1.0 };
        turtle.right(direction * 90.0);
        turtle.forward(cell_gap);
        turtle.right(direction * 90.0);
        turtle.pen_down();

        let walls = row_walls(row);

        // Every second row needs to be reversed so the turtle can zig-zag back and
        // forth instead of wasting too much time moving all the way to the left of
        // each row
        if i % 2 == 0 {
            draw_row(turtle, cell_size, walls.rev());
        }
        else {
            draw_row(turtle, cell_size, walls);
        }
    }
}

fn draw_row<I: Iterator<Item=bool>>(turtle: &mut Turtle, wall_size: f64, walls: I) {
    for should_draw in walls {
        if !should_draw {
            turtle.pen_up();
        }
        turtle.forward(wall_size);
        turtle.pen_down();
    }
}

fn draw_marker<C: Into<Color>>(
    turtle: &mut Turtle,
    (row, col): (usize, usize),
    cell_width: f64,
    cell_height: f64,
    color: C,
) {
    turtle.pen_up();

    let row = row as f64;
    let col = col as f64;
    let speed = turtle.speed();

    turtle.set_speed("instant");
    turtle.forward(col * cell_width);
    turtle.right(90.0);
    turtle.forward(row * cell_height);
    turtle.left(90.0);
    turtle.set_speed(speed);

    // The padding around the start and finish markers
    let marker_padding = turtle.pen_size(); // px

    turtle.set_fill_color(color);
    turtle.forward(marker_padding);
    turtle.right(90.0);
    turtle.forward(marker_padding);
    turtle.left(90.0);

    turtle.begin_fill();
    for _ in 0..2 {
        turtle.forward(cell_width - marker_padding * 2.0);
        turtle.right(90.0);
        turtle.forward(cell_height - marker_padding * 2.0);
        turtle.right(90.0);
    }
    turtle.end_fill();

    turtle.backward(marker_padding);
    turtle.right(90.0);
    turtle.backward(marker_padding);
    turtle.left(90.0);

    turtle.set_speed("instant");
    turtle.backward(col * cell_width);
    turtle.right(90.0);
    turtle.backward(row * cell_height);
    turtle.left(90.0);
    turtle.set_speed(speed);

    turtle.pen_down();
}
<commit_msg>Removing duplicate import from maze example<commit_after>mod wall;
mod cell;
mod maze;
mod grid;
mod solver;

extern crate turtle;

use turtle::{Turtle, Color};

pub use maze::Maze;
use grid::{GridCellIter};
use solver::solve;

// Dimensions of the maze in turtle steps (pixels)
const WIDTH: f64 = 600.0; // px
const HEIGHT: f64 = 600.0; // px

fn main() {
    turtle::start();

    let maze = Maze::generate();

    let mut turtle = Turtle::new();
    turtle.set_speed(8);
    turtle.set_background_color("#BDBDBD");
    turtle.set_pen_color("#03A9F4");
    turtle.set_pen_size(2.0);

    // Get to the top left corner
    turtle.pen_up();
    turtle.forward(HEIGHT / 2.0);
    turtle.right(90.0);
    turtle.backward(WIDTH / 2.0);
    turtle.pen_down();

    let cell_width = WIDTH/(maze.row_size() as f64);
    let cell_height = HEIGHT/(maze.col_size() as f64);

    // Draw rows
    draw_rows(
        &mut turtle,
        cell_width,
        cell_height,
        maze.first_row().map(|cell| cell.north.is_closed()),
        maze.rows(),
        |row| row.map(|cell| cell.south.is_closed()),
        false,
    );

    // Draw columns
    turtle.left(90.0);
    draw_rows(
        &mut turtle,
        cell_height,
        cell_width,
        maze.last_col().rev().map(|cell| cell.east.is_closed()),
        maze.cols().rev(),
        |col| col.map(|cell| cell.west.is_closed()).rev(),
        true,
    );

    turtle.right(90.0);
    draw_marker(&mut turtle, maze.start(), cell_width, cell_height, "#C5E1A5");
    draw_marker(&mut turtle, maze.finish(), cell_width, cell_height, "#FFAB91");

    turtle.pen_up();
    turtle.forward(cell_width * 0.5);
    turtle.right(90.0);
    turtle.forward(cell_height * 0.5);
    turtle.left(90.0);
    turtle.pen_down();

    turtle.set_speed(5);
    turtle.set_pen_size(2.0);
    solve(&mut turtle, maze, cell_width, cell_height);
}

fn draw_rows<
    'a,
    R: Iterator<Item=bool>,
    G: Iterator<Item=GridCellIter<'a>>,
    F: Fn(GridCellIter<'a>) -> X,
    X: Iterator<Item=bool> + DoubleEndedIterator
>(
    turtle: &mut Turtle,
    // size of each cell in the row
    cell_size: f64,
    // gap between rows
    cell_gap: f64,
    first_row: R,
    rows: G,
    row_walls: F,
    rotate_left: bool,
) {
    draw_row(turtle, cell_size, first_row);

    // Direction of rotation for all turns
    let rotation = if rotate_left { -1.0 } else { 1.0 };
    for (i, row) in rows.enumerate() {
        turtle.pen_up();

        // Direction of rotation for these turns
        let direction = rotation * if i % 2 == 0 { 1.0 } else { -1.0 };
        turtle.right(direction * 90.0);
        turtle.forward(cell_gap);
        turtle.right(direction * 90.0);
        turtle.pen_down();

        let walls = row_walls(row);

        // Every second row needs to be reversed so the turtle can zig-zag back and
        // forth instead of wasting too much time moving all the way to the left of
        // each row
        if i % 2 == 0 {
            draw_row(turtle, cell_size, walls.rev());
        }
        else {
            draw_row(turtle, cell_size, walls);
        }
    }
}

fn draw_row<I: Iterator<Item=bool>>(turtle: &mut Turtle, wall_size: f64, walls: I) {
    for should_draw in walls {
        if !should_draw {
            turtle.pen_up();
        }
        turtle.forward(wall_size);
        turtle.pen_down();
    }
}

fn draw_marker<C: Into<Color>>(
    turtle: &mut Turtle,
    (row, col): (usize, usize),
    cell_width: f64,
    cell_height: f64,
    color: C,
) {
    turtle.pen_up();

    let row = row as f64;
    let col = col as f64;
    let speed = turtle.speed();

    turtle.set_speed("instant");
    turtle.forward(col * cell_width);
    turtle.right(90.0);
    turtle.forward(row * cell_height);
    turtle.left(90.0);
    turtle.set_speed(speed);

    // The padding around the start and finish markers
    let marker_padding = turtle.pen_size(); // px

    turtle.set_fill_color(color);
    turtle.forward(marker_padding);
    turtle.right(90.0);
    turtle.forward(marker_padding);
    turtle.left(90.0);

    turtle.begin_fill();
    for _ in 0..2 {
        turtle.forward(cell_width - marker_padding * 2.0);
        turtle.right(90.0);
        turtle.forward(cell_height - marker_padding * 2.0);
        turtle.right(90.0);
    }
    turtle.end_fill();

    turtle.backward(marker_padding);
    turtle.right(90.0);
    turtle.backward(marker_padding);
    turtle.left(90.0);

    turtle.set_speed("instant");
    turtle.backward(col * cell_width);
    turtle.right(90.0);
    turtle.backward(row * cell_height);
    turtle.left(90.0);
    turtle.set_speed(speed);

    turtle.pen_down();
}
<|endoftext|>


<commit_before>use clap::{App, Arg};
use json5;
use regex::Regex;

use std::collections::BTreeMap as Map;
use std::net::{IpAddr, SocketAddr};
use std::path::Path;
use std::{env, fs, str};

use crate::{
    config::{Auth, Cert, Config, ProxyRoute, Route},
    consts::*,
    logger::{logger_init, JoinHandle},
    process_exit,
};

/// Get `Config` by `parse` `args`
pub fn parse() -> (Config, JoinHandle) {
    let mut config = Config::default();
    let mut server = Server::default();
    let mut routes: Vec<String> = vec!["./".to_owned()];

    let default_addr = server.ip.to_string();
    let default_port = server.port.to_string();
    let default_cache = config.cache_secs.to_string();
    let default_compress = config.compress_level.to_string();
    let default_magic_size = config.magic_limit.to_string();
    let app = {
        App::new(NAME)
            .version(VERSION)
            .author(env!("CARGO_PKG_AUTHORS"))
            .about(env!("CARGO_PKG_DESCRIPTION"))
            .about(DESC)
            .arg(
                Arg::with_name("qr")
                    .short("Q")
                    .long("qr-code")
                    .help("Show URL's QR code at startup"),
            )
            .arg(
                Arg::with_name("verbose")
                    .short("v")
                    .multiple(true)
                    .help("Increases logging verbosity each use for up to 3 times(warn0_info1_debug2_trace3+)"),
            )
            .arg(
                Arg::with_name("config")
                    .long("config")
                    .short("c")
                    .takes_value(true)
                    .help("Set the path to use a custom config file\ndefault path: ~/.config/fht2p/fht2p.json"),
            )
            .arg(
                Arg::with_name("auth")
                    .long("auth")
                    .short("a")
                    .takes_value(true)
                    .help("Set the username:password for authorization")
                    .validator(|s| {
                        s.parse::<Auth>()
                            .map(|_| ())
                            .map_err(|e| format!("invalid value for auth: {}", e))
                    }),
            )
            .arg(
                Arg::with_name("cert")
                    .long("cert")
                    .short("C")
                    .takes_value(true)
                    .help("Set the cert for https,  public_cert_file:private_key_file")
                    .validator(|s| {
                        s.parse::<Cert>()
                            .map(|_| ())
                            .map_err(|e| format!("invalid value for cert: {}", e))
                    }),
            )
            .arg(
                Arg::with_name("compress")
                    .long("compress")
                    .takes_value(true)
                    .help("Set the level for index compress, should between 0~9, use 0 to close")
                    .default_value(&default_compress)
                    .validator(|s| {
                        s.parse::<u32>()
                            .map_err(|e| format!("invalid value for compress: {}", e))
                            .and_then(|x| {
                                if x <= 9 {
                                    Ok(())
                                } else {
                                    Err(format!("invalid level for compress: {}", x))
                                }
                            })
                    }),
            )
            .arg(
                Arg::with_name("config-print")
                    .short("F")
                    .long("config-print")
                    .help("Print the content of default config file"),
            )
            .arg(
                Arg::with_name("redirect-html")
                    .long("redirect-html")
                    .short("r")
                    .help("Redirect dir to `index.html` or `index.htm` if it exists"),
            )
            .arg(
                Arg::with_name("show-hider")
                    .long("show-hider")
                    .short("s")
                    .help("Show entries starts with '.'"),
            )
            .arg(
                Arg::with_name("keepalive")
                    .long("keepalive")
                    .short("k")
                    .help("Close HTTP keep alive"),
            )
            .arg(
                Arg::with_name("disable-index")
                    .long("disable-index")
                    .short("d")
                    .help("Disable index(directory) view(will return 403)"),
            )
            .arg(
                Arg::with_name("follow-links")
                    .long("follow-links")
                    .short("f")
                    .help("Enable follow links"),
            )
            .arg(
                Arg::with_name("upload")
                    .long("upload")
                    .short("u")
                    .help("Enable upload function"),
            )
            .arg(Arg::with_name("mkdir").short("m").long("mkdir").help("Enable mkdir function"))
            .arg(
                Arg::with_name("magic-limit")
                    .long("magic-limit")
                    .short("M")
                    .takes_value(true)
                    .default_value(&default_magic_size)
                    .help("The size limit for detect file ContenType(use 0 to close)")
                    .validator(|s| {
                        s.parse::<u64>()
                            .map(|_| ())
                            .map_err(|e| format!("invalid value for magic-limit: {}", e))
                    }),
            )
            .arg(
                Arg::with_name("cache-secs")
                    .short("S")
                    .long("cache-secs")
                    .takes_value(true)
                    .default_value(&default_cache)
                    .help("Set the secs of cache(use 0 to close)")
                    .validator(|s| {
                        s.parse::<u32>()
                            .map(|_| ())
                            .map_err(|e| format!("invalid value for cache-secs: {}", e))
                    }),
            )
            .arg(
                Arg::with_name("proxy")
                    .long("proxy")
                    .short("P")
                    .takes_value(true)
                    .help("Enable http proxy(Regular for allowed domains, empty string can allow all)")
                    .validator(|s| {
                        Regex::new(&s)
                            .map(|_| ())
                            .map_err(|e| format!("invalid value for proxy: {}", e))
                    }),
            )
            .arg(
                Arg::with_name("ip")
                    .long("ip")
                    .short("i")
                    .default_value(&default_addr)
                    .takes_value(true)
                    .help("Set listenning ip address")
                    .validator(|s| {
                        s.parse::<IpAddr>()
                            .map(|_| ())
                            .map_err(|e| format!("invalid value for ip: {}", e))
                    }),
            )
            .arg(
                Arg::with_name("port")
                    .long("port")
                    .short("p")
                    .default_value(&default_port)
                    .takes_value(true)
                    .help("Set listenning port")
                    .validator(|s| {
                        s.parse::<u16>()
                            .map(|_| ())
                            .map_err(|e| format!("invalid value for port: {}", e))
                    }),
            )
            .arg(
                Arg::with_name("PATH")
                    .index(1)
                    .multiple(true)
                    .help(r#"Set the paths to share [Default: "."]"#),
            )
    };

    // clap's NOTE: The first argument will be parsed as the binary name unless AppSettings::NoBinaryName is used
    let args = env::args().collect::<Vec<_>>();
    // not contains other than -v* or -Q/--qr-code
    let args_is_empty = args
        .iter()
        .skip(1)
        .all(|arg| arg.starts_with("-v") || ["-Q", "--qr-code"].contains(&arg.as_str()));
    let matches = app.get_matches_from(args);
    let qr = matches.is_present("qr");

    // -P/--config-print
    if matches.is_present("config-print") {
        config_print();
    }

    let mut join_handle = logger_init(matches.occurrences_of("verbose"));
    let exit_with_msg = |e: String| {
        error!("{}", e);
        join_handle.join();
        process_exit(1);
    };

    //-c/--config选项，如果有就载入该文件。
    if let Some(s) = matches.value_of("config") {
        let config = Config::load_from_file(&s).map_err(exit_with_msg).unwrap();
        return (config.show_qrcode(qr), join_handle);
    }

    // 命令行有没有参数？有就解析参数，没有就寻找配置文件，再没有就使用默认配置。
    let conf = if args_is_empty {
        match get_config_path() {
            Some(s) => Config::load_from_file(&s).map_err(exit_with_msg).unwrap(),
            None => Config::load_from_STR(),
        }
    } else {
        let redirect_html = matches.is_present("redirect-html");
        let follow_links = matches.is_present("follow-links");
        let disable_index = matches.is_present("disable-index");
        let show_hider = matches.is_present("show-hider");
        let upload = matches.is_present("upload");
        let mkdir = matches.is_present("mkdir");
        let authorized = matches.is_present("auth");

        matches.value_of("ip").map(|p| server.ip = p.parse().unwrap());
        matches.value_of("port").map(|p| server.port = p.parse().unwrap());
        matches
            .value_of("cache-secs")
            .map(|cs| config.cache_secs = cs.parse::<u32>().unwrap());
        matches
            .value_of("compress")
            .map(|cp| config.compress_level = cp.parse::<u32>().unwrap());

        config.addr = SocketAddr::new(server.ip, server.port);
        config.auth = matches.value_of("auth").map(|cp| cp.parse::<Auth>().unwrap());
        config.cert = matches.value_of("cert").map(|cp| cp.parse::<Cert>().unwrap());
        config.proxy = matches.value_of("proxy").map(|s| (&ProxyRoute::new(true, s)).into());
        config.keep_alive = !matches.is_present("keepalive");
        matches.values_of_lossy("PATH").map(|args| routes = args);

        config.routes = args_paths_to_route(
            &routes[..],
            redirect_html,
            follow_links,
            show_hider,
            disable_index,
            upload,
            mkdir,
            authorized,
        )
        .map_err(exit_with_msg)
        .unwrap();
        config
    };

    (conf.show_qrcode(qr), join_handle)
}

#[derive(Debug, Clone)]
pub struct Server {
    pub ip: IpAddr,
    pub port: u16,
}

impl Default for Server {
    fn default() -> Server {
        let addr = SERVER_ADDR.get();
        Self::new(addr.ip(), addr.port())
    }
}
impl Server {
    fn new(ip: IpAddr, port: u16) -> Self {
        Server { ip, port }
    }
    fn get_sa() -> SocketAddr {
        Self::default().into()
    }
}

impl Into<SocketAddr> for Server {
    fn into(self) -> SocketAddr {
        SocketAddr::new(self.ip, self.port)
    }
}

// 关键是结构体的字段名，和 json 的[name]对应
#[serde(rename_all = "camelCase")]
#[derive(Debug, Deserialize, Serialize)]
pub struct Fht2p {
    setting: Setting,
    proxy: Option<ProxyRoute>,
    routes: Map<String, Route>,
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Setting {
    #[serde(default = "Server::get_sa")]
    addr: SocketAddr,
    keep_alive: bool,
    magic_limit: u64,
    cache_secs: u32,
    compress_level: u32,
    auth: Option<Auth>,
    cert: Option<Cert>,
}

impl Config {
    fn load_from_file(path: &str) -> Result<Self, String> {
        debug!("Config.load_from_file: {}", path);
        let str = fs::read_to_string(path).map_err(|e| format!("config file('{}') read failed: {}", path, e))?;
        Self::load_from_str(path, &str)
    }
    fn load_from_str(file_name: &str, json: &str) -> Result<Config, String> {
        let mut config = Self::default();
        config.routes.clear();

        let json: Fht2p = json5::from_str(json).map_err(|e| format!("config file('{}') parse failed: {}", file_name, e))?;
        config.keep_alive = json.setting.keep_alive;
        config.magic_limit = json.setting.magic_limit;
        config.compress_level = json.setting.compress_level;
        config.cache_secs = json.setting.cache_secs;
        config.addr = json.setting.addr;
        config.cert = json.setting.cert.clone();
        config.auth = json.setting.auth.clone();
        config.proxy = json.proxy.map(|ref pc| pc.into());

        for (url, route) in &json.routes {
            if !Path::new(&route.path).exists() {
                warn!("'{}''s routes({:?}: {:?}) is not exists", file_name, url, route.path);
            }
            config.routes.insert(
                url.clone(),
                Route::new(
                    url,
                    &route.path,
                    route.redirect_html,
                    route.follow_links,
                    route.show_hider,
                    route.disable_index,
                    route.upload,
                    route.mkdir,
                    route.authorized,
                ),
            );
        }
        if config.routes.is_empty() {
            return Err(format!("'{}''s routes is empty", file_name));
        }
        Ok(config)
    }
    #[allow(non_snake_case)]
    fn load_from_STR() -> Self {
        Config::load_from_str("CONFIG-STR", CONFIG_STR).unwrap()
    }
}

// 打印默认配置文件。
fn config_print() {
    println!("{}", CONFIG_STR);
    process_exit(0);
}

// 家目录 ～/.config/fht2p/fht2p.json
fn get_config_path() -> Option<String> {
    // using the home_dir function from https://crates.io/crates/dirs instead.
    #[allow(deprecated)]
    let home = std::env::home_dir()?;
    let confp = home.as_path().join(".config/fht2p").join(CONFIG_STR_PATH);
    if confp.exists() {
        Some(confp.to_string_lossy().into_owned())
    } else {
        None
    }
}

// 参数转换为Route url, path
fn args_paths_to_route(
    map: &[String],
    redirect_html: bool,
    follow_links: bool,
    show_hider: bool,
    disable_index: bool,
    upload: bool,
    mkdir: bool,
    authorized: bool,
) -> Result<Map<String, Route>, String> {
    let mut routes = Map::new();
    for (idx, path) in map.iter().enumerate() {
        if !Path::new(&path).exists() {
            warn!("{:?} is not exists", &path);
        }
        if idx == 0 {
            let route = Route::new(
                "/",
                path,
                redirect_html,
                follow_links,
                show_hider,
                disable_index,
                upload,
                mkdir,
                authorized,
            );
            routes.insert("/".to_owned(), route);
        } else {
            let route_url = route_name(path)?;
            let route = Route::new(
                &route_url,
                path,
                redirect_html,
                follow_links,
                show_hider,
                disable_index,
                upload,
                mkdir,
                authorized,
            );
            if routes.insert(route_url, route).is_some() {
                return Err(format!("{} already defined", route_name(path).unwrap()));
            }
        }
    }
    fn route_name(msg: &str) -> Result<String, String> {
        let path = Path::new(msg);
        path.file_name()
            .map(|s| "/".to_owned() + s.to_str().unwrap())
            .map(|mut s| {
                if Path::new(msg).is_dir() {
                    s.push('/');
                }
                s
            })
            .ok_or_else(|| format!("Path '{}' dost not have name", msg))
    }
    Ok(routes)
}
<commit_msg>add a option --verbose<commit_after>use clap::{App, Arg};
use json5;
use regex::Regex;

use std::collections::BTreeMap as Map;
use std::net::{IpAddr, SocketAddr};
use std::path::Path;
use std::{env, fs, str};

use crate::{
    config::{Auth, Cert, Config, ProxyRoute, Route},
    consts::*,
    logger::{logger_init, JoinHandle},
    process_exit,
};

/// Get `Config` by `parse` `args`
pub fn parse() -> (Config, JoinHandle) {
    let mut config = Config::default();
    let mut server = Server::default();
    let mut routes: Vec<String> = vec!["./".to_owned()];

    let default_addr = server.ip.to_string();
    let default_port = server.port.to_string();
    let default_cache = config.cache_secs.to_string();
    let default_compress = config.compress_level.to_string();
    let default_magic_size = config.magic_limit.to_string();
    let app = {
        App::new(NAME)
            .version(VERSION)
            .author(env!("CARGO_PKG_AUTHORS"))
            .about(env!("CARGO_PKG_DESCRIPTION"))
            .about(DESC)
            .arg(
                Arg::with_name("qr")
                    .short("Q")
                    .long("qr-code")
                    .help("Show URL's QR code at startup"),
            )
            .arg(
                Arg::with_name("verbose")
                    .short("v")
                    .long("verbose")
                    .multiple(true)
                    .help("Increases logging verbosity each use for up to 3 times(warn0_info1_debug2_trace3+)"),
            )
            .arg(
                Arg::with_name("config")
                    .long("config")
                    .short("c")
                    .takes_value(true)
                    .help("Set the path to use a custom config file\ndefault path: ~/.config/fht2p/fht2p.json"),
            )
            .arg(
                Arg::with_name("auth")
                    .long("auth")
                    .short("a")
                    .takes_value(true)
                    .help("Set the username:password for authorization")
                    .validator(|s| {
                        s.parse::<Auth>()
                            .map(|_| ())
                            .map_err(|e| format!("invalid value for auth: {}", e))
                    }),
            )
            .arg(
                Arg::with_name("cert")
                    .long("cert")
                    .short("C")
                    .takes_value(true)
                    .help("Set the cert for https,  public_cert_file:private_key_file")
                    .validator(|s| {
                        s.parse::<Cert>()
                            .map(|_| ())
                            .map_err(|e| format!("invalid value for cert: {}", e))
                    }),
            )
            .arg(
                Arg::with_name("compress")
                    .long("compress")
                    .takes_value(true)
                    .help("Set the level for index compress, should between 0~9, use 0 to close")
                    .default_value(&default_compress)
                    .validator(|s| {
                        s.parse::<u32>()
                            .map_err(|e| format!("invalid value for compress: {}", e))
                            .and_then(|x| {
                                if x <= 9 {
                                    Ok(())
                                } else {
                                    Err(format!("invalid level for compress: {}", x))
                                }
                            })
                    }),
            )
            .arg(
                Arg::with_name("config-print")
                    .short("F")
                    .long("config-print")
                    .help("Print the content of default config file"),
            )
            .arg(
                Arg::with_name("redirect-html")
                    .long("redirect-html")
                    .short("r")
                    .help("Redirect dir to `index.html` or `index.htm` if it exists"),
            )
            .arg(
                Arg::with_name("show-hider")
                    .long("show-hider")
                    .short("s")
                    .help("Show entries starts with '.'"),
            )
            .arg(
                Arg::with_name("keepalive")
                    .long("keepalive")
                    .short("k")
                    .help("Close HTTP keep alive"),
            )
            .arg(
                Arg::with_name("disable-index")
                    .long("disable-index")
                    .short("d")
                    .help("Disable index(directory) view(will return 403)"),
            )
            .arg(
                Arg::with_name("follow-links")
                    .long("follow-links")
                    .short("f")
                    .help("Enable follow links"),
            )
            .arg(
                Arg::with_name("upload")
                    .long("upload")
                    .short("u")
                    .help("Enable upload function"),
            )
            .arg(Arg::with_name("mkdir").short("m").long("mkdir").help("Enable mkdir function"))
            .arg(
                Arg::with_name("magic-limit")
                    .long("magic-limit")
                    .short("M")
                    .takes_value(true)
                    .default_value(&default_magic_size)
                    .help("The size limit for detect file ContenType(use 0 to close)")
                    .validator(|s| {
                        s.parse::<u64>()
                            .map(|_| ())
                            .map_err(|e| format!("invalid value for magic-limit: {}", e))
                    }),
            )
            .arg(
                Arg::with_name("cache-secs")
                    .short("S")
                    .long("cache-secs")
                    .takes_value(true)
                    .default_value(&default_cache)
                    .help("Set the secs of cache(use 0 to close)")
                    .validator(|s| {
                        s.parse::<u32>()
                            .map(|_| ())
                            .map_err(|e| format!("invalid value for cache-secs: {}", e))
                    }),
            )
            .arg(
                Arg::with_name("proxy")
                    .long("proxy")
                    .short("P")
                    .takes_value(true)
                    .help("Enable http proxy(Regular for allowed domains, empty string can allow all)")
                    .validator(|s| {
                        Regex::new(&s)
                            .map(|_| ())
                            .map_err(|e| format!("invalid value for proxy: {}", e))
                    }),
            )
            .arg(
                Arg::with_name("ip")
                    .long("ip")
                    .short("i")
                    .default_value(&default_addr)
                    .takes_value(true)
                    .help("Set listenning ip address")
                    .validator(|s| {
                        s.parse::<IpAddr>()
                            .map(|_| ())
                            .map_err(|e| format!("invalid value for ip: {}", e))
                    }),
            )
            .arg(
                Arg::with_name("port")
                    .long("port")
                    .short("p")
                    .default_value(&default_port)
                    .takes_value(true)
                    .help("Set listenning port")
                    .validator(|s| {
                        s.parse::<u16>()
                            .map(|_| ())
                            .map_err(|e| format!("invalid value for port: {}", e))
                    }),
            )
            .arg(
                Arg::with_name("PATH")
                    .index(1)
                    .multiple(true)
                    .help(r#"Set the paths to share [Default: "."]"#),
            )
    };

    // clap's NOTE: The first argument will be parsed as the binary name unless AppSettings::NoBinaryName is used
    let args = env::args().collect::<Vec<_>>();
    // not contains other than -v*, -Q, --qr-code/--verbose, but -vs ?
    let args_is_empty = args
        .iter()
        .skip(1)
        .all(|arg| arg.starts_with("-v") || arg.starts_with("-Q") || ["--verbose", "--qr-code"].contains(&arg.as_str()));
    let matches = app.get_matches_from(args);
    let qr = matches.is_present("qr");

    // -P/--config-print
    if matches.is_present("config-print") {
        config_print();
    }

    let mut join_handle = logger_init(matches.occurrences_of("verbose"));
    let exit_with_msg = |e: String| {
        error!("{}", e);
        join_handle.join();
        process_exit(1);
    };

    //-c/--config选项，如果有就载入该文件。
    if let Some(s) = matches.value_of("config") {
        let config = Config::load_from_file(&s).map_err(exit_with_msg).unwrap();
        return (config.show_qrcode(qr), join_handle);
    }

    // 命令行有没有参数？有就解析参数，没有就寻找配置文件，再没有就使用默认配置。
    let conf = if args_is_empty {
        match get_config_path() {
            Some(s) => Config::load_from_file(&s).map_err(exit_with_msg).unwrap(),
            None => Config::load_from_STR(),
        }
    } else {
        let redirect_html = matches.is_present("redirect-html");
        let follow_links = matches.is_present("follow-links");
        let disable_index = matches.is_present("disable-index");
        let show_hider = matches.is_present("show-hider");
        let upload = matches.is_present("upload");
        let mkdir = matches.is_present("mkdir");
        let authorized = matches.is_present("auth");

        matches.value_of("ip").map(|p| server.ip = p.parse().unwrap());
        matches.value_of("port").map(|p| server.port = p.parse().unwrap());
        matches
            .value_of("cache-secs")
            .map(|cs| config.cache_secs = cs.parse::<u32>().unwrap());
        matches
            .value_of("compress")
            .map(|cp| config.compress_level = cp.parse::<u32>().unwrap());

        config.addr = SocketAddr::new(server.ip, server.port);
        config.auth = matches.value_of("auth").map(|cp| cp.parse::<Auth>().unwrap());
        config.cert = matches.value_of("cert").map(|cp| cp.parse::<Cert>().unwrap());
        config.proxy = matches.value_of("proxy").map(|s| (&ProxyRoute::new(true, s)).into());
        config.keep_alive = !matches.is_present("keepalive");
        matches.values_of_lossy("PATH").map(|args| routes = args);

        config.routes = args_paths_to_route(
            &routes[..],
            redirect_html,
            follow_links,
            show_hider,
            disable_index,
            upload,
            mkdir,
            authorized,
        )
        .map_err(exit_with_msg)
        .unwrap();
        config
    };

    (conf.show_qrcode(qr), join_handle)
}

#[derive(Debug, Clone)]
pub struct Server {
    pub ip: IpAddr,
    pub port: u16,
}

impl Default for Server {
    fn default() -> Server {
        let addr = SERVER_ADDR.get();
        Self::new(addr.ip(), addr.port())
    }
}
impl Server {
    fn new(ip: IpAddr, port: u16) -> Self {
        Server { ip, port }
    }
    fn get_sa() -> SocketAddr {
        Self::default().into()
    }
}

impl Into<SocketAddr> for Server {
    fn into(self) -> SocketAddr {
        SocketAddr::new(self.ip, self.port)
    }
}

// 关键是结构体的字段名，和 json 的[name]对应
#[serde(rename_all = "camelCase")]
#[derive(Debug, Deserialize, Serialize)]
pub struct Fht2p {
    setting: Setting,
    proxy: Option<ProxyRoute>,
    routes: Map<String, Route>,
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Setting {
    #[serde(default = "Server::get_sa")]
    addr: SocketAddr,
    keep_alive: bool,
    magic_limit: u64,
    cache_secs: u32,
    compress_level: u32,
    auth: Option<Auth>,
    cert: Option<Cert>,
}

impl Config {
    fn load_from_file(path: &str) -> Result<Self, String> {
        debug!("Config.load_from_file: {}", path);
        let str = fs::read_to_string(path).map_err(|e| format!("config file('{}') read failed: {}", path, e))?;
        Self::load_from_str(path, &str)
    }
    fn load_from_str(file_name: &str, json: &str) -> Result<Config, String> {
        let mut config = Self::default();
        config.routes.clear();

        let json: Fht2p = json5::from_str(json).map_err(|e| format!("config file('{}') parse failed: {}", file_name, e))?;
        config.keep_alive = json.setting.keep_alive;
        config.magic_limit = json.setting.magic_limit;
        config.compress_level = json.setting.compress_level;
        config.cache_secs = json.setting.cache_secs;
        config.addr = json.setting.addr;
        config.cert = json.setting.cert.clone();
        config.auth = json.setting.auth.clone();
        config.proxy = json.proxy.map(|ref pc| pc.into());

        for (url, route) in &json.routes {
            if !Path::new(&route.path).exists() {
                warn!("'{}''s routes({:?}: {:?}) is not exists", file_name, url, route.path);
            }
            config.routes.insert(
                url.clone(),
                Route::new(
                    url,
                    &route.path,
                    route.redirect_html,
                    route.follow_links,
                    route.show_hider,
                    route.disable_index,
                    route.upload,
                    route.mkdir,
                    route.authorized,
                ),
            );
        }
        if config.routes.is_empty() {
            return Err(format!("'{}''s routes is empty", file_name));
        }
        Ok(config)
    }
    #[allow(non_snake_case)]
    fn load_from_STR() -> Self {
        Config::load_from_str("CONFIG-STR", CONFIG_STR).unwrap()
    }
}

// 打印默认配置文件。
fn config_print() {
    println!("{}", CONFIG_STR);
    process_exit(0);
}

// 家目录 ～/.config/fht2p/fht2p.json
fn get_config_path() -> Option<String> {
    // using the home_dir function from https://crates.io/crates/dirs instead.
    #[allow(deprecated)]
    let home = std::env::home_dir()?;
    let confp = home.as_path().join(".config/fht2p").join(CONFIG_STR_PATH);
    if confp.exists() {
        Some(confp.to_string_lossy().into_owned())
    } else {
        None
    }
}

// 参数转换为Route url, path
fn args_paths_to_route(
    map: &[String],
    redirect_html: bool,
    follow_links: bool,
    show_hider: bool,
    disable_index: bool,
    upload: bool,
    mkdir: bool,
    authorized: bool,
) -> Result<Map<String, Route>, String> {
    let mut routes = Map::new();
    for (idx, path) in map.iter().enumerate() {
        if !Path::new(&path).exists() {
            warn!("{:?} is not exists", &path);
        }
        if idx == 0 {
            let route = Route::new(
                "/",
                path,
                redirect_html,
                follow_links,
                show_hider,
                disable_index,
                upload,
                mkdir,
                authorized,
            );
            routes.insert("/".to_owned(), route);
        } else {
            let route_url = route_name(path)?;
            let route = Route::new(
                &route_url,
                path,
                redirect_html,
                follow_links,
                show_hider,
                disable_index,
                upload,
                mkdir,
                authorized,
            );
            if routes.insert(route_url, route).is_some() {
                return Err(format!("{} already defined", route_name(path).unwrap()));
            }
        }
    }
    fn route_name(msg: &str) -> Result<String, String> {
        let path = Path::new(msg);
        path.file_name()
            .map(|s| "/".to_owned() + s.to_str().unwrap())
            .map(|mut s| {
                if Path::new(msg).is_dir() {
                    s.push('/');
                }
                s
            })
            .ok_or_else(|| format!("Path '{}' dost not have name", msg))
    }
    Ok(routes)
}
<|endoftext|>


<commit_before>use future::{Future, IntoFuture};
use stream::Stream;
use poll::Poll;
use Async;
use stack::{Stack, Drain};
use std::sync::Arc;
use task::{self, UnparkEvent};

use std::prelude::v1::*;

/// An adaptor for a stream of futures to execute the futures concurrently, if
/// possible, delivering results as they become available.
///
/// This adaptor will return their results in the order that they complete.
/// This is created by the `futures` method.
///
#[must_use = "streams do nothing unless polled"]
pub struct FuturesUnordered<F>
    where F: Future
{
    futures: Vec<Option<F>>,
    stack: Arc<Stack<usize>>,
    pending: Option<Drain<usize>>,
    active: usize,
}

/// Converts a list of futures into a `Stream` of results from the futures.
///
/// This function will take an list of futures (e.g. a vector, an iterator,
/// etc), and return a stream. The stream will yield items as they become
/// available on the futures internally, in the order that they become
/// available. This function is similar to `buffer_unordered` in that it may
/// return items in a different order than in the list specified.
pub fn futures_unordered<I>(futures: I) -> FuturesUnordered<<I::Item as IntoFuture>::Future>
    where I: IntoIterator,
          I::Item: IntoFuture
{
    let futures = futures.into_iter()
                         .map(IntoFuture::into_future)
                         .map(Some)
                         .collect::<Vec<_>>();
    let stack = Arc::new(Stack::new());
    for i in 0..futures.len() {
        stack.push(i);
    }
    FuturesUnordered {
        active: futures.len(),
        futures: futures,
        pending: None,
        stack: stack,
    }
}

impl<F> FuturesUnordered<F>
    where F: Future
{
    fn poll_pending(&mut self, mut drain: Drain<usize>)
                    -> Option<Poll<Option<F::Item>, F::Error>> {
        while let Some(id) = drain.next() {
            // If this future was already done just skip the notification
            if self.futures[id].is_none() {
                continue
            }
            let event = UnparkEvent::new(self.stack.clone(), id);
            let ret = match task::with_unpark_event(event, || {
                self.futures[id]
                    .as_mut()
                    .unwrap()
                    .poll()
            }) {
                Ok(Async::NotReady) => continue,
                Ok(Async::Ready(val)) => Ok(Async::Ready(Some(val))),
                Err(e) => Err(e),
            };
            self.pending = Some(drain);
            self.active -= 1;
            self.futures[id] = None;
            return Some(ret)
        }
        None
    }
}

impl<F> Stream for FuturesUnordered<F>
    where F: Future
{
    type Item = F::Item;
    type Error = F::Error;

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        if self.active == 0 {
            return Ok(Async::Ready(None))
        }
        if let Some(drain) = self.pending.take() {
            if let Some(ret) = self.poll_pending(drain){
                return ret
            }
        }
        let drain = self.stack.drain();
        if let Some(ret) = self.poll_pending(drain) {
            return ret
        }
        assert!(self.active > 0);
        Ok(Async::NotReady)
    }
}
<commit_msg>Minor style nit<commit_after>use future::{Future, IntoFuture};
use stream::Stream;
use poll::Poll;
use Async;
use stack::{Stack, Drain};
use std::sync::Arc;
use task::{self, UnparkEvent};

use std::prelude::v1::*;

/// An adaptor for a stream of futures to execute the futures concurrently, if
/// possible, delivering results as they become available.
///
/// This adaptor will return their results in the order that they complete.
/// This is created by the `futures` method.
///
#[must_use = "streams do nothing unless polled"]
pub struct FuturesUnordered<F>
    where F: Future
{
    futures: Vec<Option<F>>,
    stack: Arc<Stack<usize>>,
    pending: Option<Drain<usize>>,
    active: usize,
}

/// Converts a list of futures into a `Stream` of results from the futures.
///
/// This function will take an list of futures (e.g. a vector, an iterator,
/// etc), and return a stream. The stream will yield items as they become
/// available on the futures internally, in the order that they become
/// available. This function is similar to `buffer_unordered` in that it may
/// return items in a different order than in the list specified.
pub fn futures_unordered<I>(futures: I) -> FuturesUnordered<<I::Item as IntoFuture>::Future>
    where I: IntoIterator,
          I::Item: IntoFuture
{
    let futures = futures.into_iter()
                         .map(IntoFuture::into_future)
                         .map(Some)
                         .collect::<Vec<_>>();
    let stack = Arc::new(Stack::new());
    for i in 0..futures.len() {
        stack.push(i);
    }
    FuturesUnordered {
        active: futures.len(),
        futures: futures,
        pending: None,
        stack: stack,
    }
}

impl<F> FuturesUnordered<F>
    where F: Future
{
    fn poll_pending(&mut self, mut drain: Drain<usize>)
                    -> Option<Poll<Option<F::Item>, F::Error>> {
        while let Some(id) = drain.next() {
            // If this future was already done just skip the notification
            if self.futures[id].is_none() {
                continue
            }
            let event = UnparkEvent::new(self.stack.clone(), id);
            let ret = match task::with_unpark_event(event, || {
                self.futures[id]
                    .as_mut()
                    .unwrap()
                    .poll()
            }) {
                Ok(Async::NotReady) => continue,
                Ok(Async::Ready(val)) => Ok(Async::Ready(Some(val))),
                Err(e) => Err(e),
            };
            self.pending = Some(drain);
            self.active -= 1;
            self.futures[id] = None;
            return Some(ret)
        }
        None
    }
}

impl<F> Stream for FuturesUnordered<F>
    where F: Future
{
    type Item = F::Item;
    type Error = F::Error;

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        if self.active == 0 {
            return Ok(Async::Ready(None))
        }
        if let Some(drain) = self.pending.take() {
            if let Some(ret) = self.poll_pending(drain) {
                return ret
            }
        }
        let drain = self.stack.drain();
        if let Some(ret) = self.poll_pending(drain) {
            return ret
        }
        assert!(self.active > 0);
        Ok(Async::NotReady)
    }
}
<|endoftext|>


<commit_before>// Copyright 2013-2015, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

use ffi;
use glib::translate::*;

#[allow(raw_pointer_derive)]
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct Atom(ffi::GdkAtom);

pub const NONE: Atom = Atom(0 as *mut _);

impl Atom {
    pub fn intern(atom_name: &str) -> Atom {
        assert_initialized_main_thread!();
        unsafe { Atom(ffi::gdk_atom_intern(atom_name.to_glib_none().0, false.to_glib())) }
    }

    pub fn name(&self) -> String {
        unsafe { from_glib_full(ffi::gdk_atom_name(self.0)) }
    }
}

impl<'a> ToGlibPtr<'a, ffi::GdkAtom> for Atom {
    type Storage = ();

    #[inline]
    fn to_glib_none(&self) -> Stash<'a, ffi::GdkAtom, Atom> {
        Stash(self.0, ())
    }
}

impl FromGlibPtr<ffi::GdkAtom> for Atom {
    #[inline]
    unsafe fn from_glib_none(ptr: ffi::GdkAtom) -> Atom { Atom(ptr) }
    #[inline]
    unsafe fn from_glib_full(_: ffi::GdkAtom) -> Atom { unimplemented!() }
}
<commit_msg>Sunset the raw_pointer_derive lint<commit_after>// Copyright 2013-2015, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

use ffi;
use glib::translate::*;

#[derive(Copy, Clone, PartialEq, Eq)]
pub struct Atom(ffi::GdkAtom);

pub const NONE: Atom = Atom(0 as *mut _);

impl Atom {
    pub fn intern(atom_name: &str) -> Atom {
        assert_initialized_main_thread!();
        unsafe { Atom(ffi::gdk_atom_intern(atom_name.to_glib_none().0, false.to_glib())) }
    }

    pub fn name(&self) -> String {
        unsafe { from_glib_full(ffi::gdk_atom_name(self.0)) }
    }
}

impl<'a> ToGlibPtr<'a, ffi::GdkAtom> for Atom {
    type Storage = ();

    #[inline]
    fn to_glib_none(&self) -> Stash<'a, ffi::GdkAtom, Atom> {
        Stash(self.0, ())
    }
}

impl FromGlibPtr<ffi::GdkAtom> for Atom {
    #[inline]
    unsafe fn from_glib_none(ptr: ffi::GdkAtom) -> Atom { Atom(ptr) }
    #[inline]
    unsafe fn from_glib_full(_: ffi::GdkAtom) -> Atom { unimplemented!() }
}
<|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.

// this file defines CGFloat, as well as stubbed data types.

#![allow(non_camel_case_types)]
#![allow(non_upper_case_globals)]

use libc;

#[cfg(any(target_arch = "x86",
          target_arch = "arm",
          target_arch = "aarch64"))]
pub type boolean_t = libc::c_int;
#[cfg(target_arch = "x86_64")]
pub type boolean_t = libc::c_uint;

#[cfg(target_pointer_width = "64")]
pub type CGFloat = libc::c_double;
#[cfg(not(target_pointer_width = "64"))]
pub type CGFloat = libc::c_float;

pub type CGError = libc::int32_t;

pub type CGAffineTransform = ();

pub const kCGImageAlphaNone: u32 = 0;
pub const kCGImageAlphaPremultipliedLast: u32 = 1;
pub const kCGImageAlphaPremultipliedFirst: u32 = 2;
pub const kCGImageAlphaLast: u32 = 3;
pub const kCGImageAlphaFirst: u32 = 4;
pub const kCGImageAlphaNoneSkipLast: u32 = 5;
pub const kCGImageAlphaNoneSkipFirst: u32 = 6;

<commit_msg>Auto merge of #61 - jrmuizel:constants, r=jdm<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.

// this file defines CGFloat, as well as stubbed data types.

#![allow(non_camel_case_types)]
#![allow(non_upper_case_globals)]

use libc;

#[cfg(any(target_arch = "x86",
          target_arch = "arm",
          target_arch = "aarch64"))]
pub type boolean_t = libc::c_int;
#[cfg(target_arch = "x86_64")]
pub type boolean_t = libc::c_uint;

#[cfg(target_pointer_width = "64")]
pub type CGFloat = libc::c_double;
#[cfg(not(target_pointer_width = "64"))]
pub type CGFloat = libc::c_float;

pub type CGError = libc::int32_t;

pub type CGAffineTransform = ();

pub const kCGImageAlphaNone: u32 = 0;
pub const kCGImageAlphaPremultipliedLast: u32 = 1;
pub const kCGImageAlphaPremultipliedFirst: u32 = 2;
pub const kCGImageAlphaLast: u32 = 3;
pub const kCGImageAlphaFirst: u32 = 4;
pub const kCGImageAlphaNoneSkipLast: u32 = 5;
pub const kCGImageAlphaNoneSkipFirst: u32 = 6;

pub const kCGBitmapByteOrderDefault: u32 = (0 << 12);
pub const kCGBitmapByteOrder16Little: u32 = (1 << 12);
pub const kCGBitmapByteOrder32Little: u32 = (2 << 12);
pub const kCGBitmapByteOrder16Big: u32 = (3 << 12);
pub const kCGBitmapByteOrder32Big: u32 = (4 << 12);

#[cfg(target_endian = "big")]
pub const kCGBitmapByteOrder16Host: u32 = kCGBitmapByteOrder16Big;
#[cfg(target_endian = "big")]
pub const kCGBitmapByteOrder32Host: u32 = kCGBitmapByteOrder32Big;

#[cfg(target_endian = "little")]
pub const kCGBitmapByteOrder16Host: u32 = kCGBitmapByteOrder16Little;
#[cfg(target_endian = "little")]
pub const kCGBitmapByteOrder32Host: u32 = kCGBitmapByteOrder32Little;
<|endoftext|>


<commit_before>use getopts::Options;
use std::env::Args;

pub struct Arguments {
    pub program_name: String,
    pub exit: bool,
}

fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} [options]", program);
    print!("{}", opts.usage(&*brief));
}

pub fn parse_args(arguments: Args) -> Arguments {
    let mut opts = Options::new();
    opts.optflag("h", "help", "print this help menu");

    let matches = match opts.parse(arguments) {
        Ok(m) => { m }
        Err(f) => { panic!(f.to_string()) }
    };

    let mut args = Arguments {
        program_name: "".to_string(),
        exit: matches.opt_present("h"),
    };

    if args.exit {
        print_usage(&*args.program_name, opts);
    }

    args
}

<commit_msg>Stub out parameter list<commit_after>use getopts::Options;
use std::env::Args;

pub struct Arguments {
    pub program_name: String,
    pub month: usize,
    pub year: usize,
    pub date_only: bool,
    pub exit: bool,
}

fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} [options]", program);
    print!("{}", opts.usage(&*brief));
}

pub fn parse_args(arguments: Args) -> Arguments {
    let mut opts = Options::new();
    opts.optflag("h", "help", "print this help menu");
    opts.optflag("d", "date", "only print today's date");
    opts.optopt("y", "year", "only print the year's calendar", "YEAR");
    opts.optopt("m", "month", "only print the month's calendar", "MONTH");

    let matches = match opts.parse(arguments) {
        Ok(m) => { m }
        Err(f) => { panic!(f.to_string()) }
    };

    let mut args = Arguments {
        program_name: "".to_string(),
        date_only: matches.opt_present("d"),
        month: 0,
        year: 0,
        exit: matches.opt_present("h"),
    };

    if args.exit {
        print_usage(&*args.program_name, opts);
    }

    args
}

<|endoftext|>


<commit_before>// Comparisons between trees.

use std::collections::BTreeMap;
use std::io::prelude::*;
use std::path::Path;

use super::suretree::{AttMap, SureTree, PathAdd};

pub trait TreeUpdate {
    /// Update any sha1 hashes in `self` using `old` as a reference.
    /// This looks for files that have sufficiently similar attributes that
    /// we can assume the sha1 hash is the same.
    fn update_from(&mut self, old: &Self);
}

impl TreeUpdate for SureTree {
    fn update_from(&mut self, old: &SureTree) {
        walk(self, old);
    }
}

fn walk(new: &mut SureTree, old: &SureTree) {
    // Walk all of the directories that are possible.
    let old_children: BTreeMap<&str, &SureTree> =
        old.children.iter().map(|ch| (&ch.name[..], ch)).collect();

    for ch in &mut new.children {
        old_children.get(&ch.name[..]).map(|och| walk(ch, och));
    }

    // Walk the file nodes that are the same, and see if they can be
    // updated.
    let old_files: BTreeMap<&str, &AttMap> =
        old.files.iter().map(|ch| (&ch.name[..], &ch.atts)).collect();

    for file in &mut new.files {
        let atts = match old_files.get(&file.name[..]) {
            None => continue,
            Some(atts) => atts,
        };

        // If new already has a node, don't do anything.  This shouldn't
        // normally happen.
        if file.atts.contains_key("sha1") {
            continue;
        }

        // Only compare files.
        if file.atts["kind"] != "file" || atts["kind"] != "file" {
            continue;
        }

        if file.atts.get("ino") != atts.get("ino") ||
            file.atts.get("ctime") != atts.get("ctime") ||
            file.atts.get("size") != atts.get("size")
        {
            continue;
        }

        // Make sure there is actually a sha1 to get.
        match atts.get("sha1") {
            None => continue,
            Some(v) => {
                file.atts.insert("sha1".to_string(), v.to_string());
            },
        }
    }
}

pub trait TreeCompare {
    /// Compare two trees, reporting (to stdout) any differences between
    /// them.
    fn compare_from(&self, old: &Self, path: &Path);
}

impl TreeCompare for SureTree {
    fn compare_from(&self, old: &Self, path: &Path) {
        compwalk(self, old, path);
    }
}

fn compwalk(new: &SureTree, old: &SureTree, path: &Path) {
    // Walk and compare directories.
    let mut old_children: BTreeMap<&String, &SureTree> =
        old.children.iter().map(|ch| (&ch.name, ch)).collect();

    for ch in &new.children {
        let cpath = ch.join(&path);
        match old_children.get(&ch.name) {
            None => println!("+ {:22} {}", "dir", cpath.to_string_lossy()),
            Some(och) => compwalk(ch, och, &cpath),
        }
        old_children.remove(&ch.name);
    }

    // Print out any directories that have been removed.
    // TODO: This print out of order.
    for &name in old_children.keys() {
        println!("- {:22} {}", "dir", name.join(&path).to_string_lossy());
    }

    // Walk and compare files.
    let mut old_files: BTreeMap<&str, &AttMap> =
        old.files.iter().map(|ch| (&ch.name[..], &ch.atts)).collect();

    for file in &new.files {
        let fpath = file.join(&path);
        match old_files.get(&file.name[..]) {
            None => println!("+ {:22} {}", "file", fpath.to_string_lossy()),
            Some(atts) => attr_comp(atts, &file.atts, &fpath),
        }
        old_files.remove(&file.name[..]);
    }

    // Print out any files that have been removed.
    for name in old_files.keys() {
        println!("- {:22} {}", "file", name.join(&path).to_string_lossy());
    }
}

// Compare the old and new attributes, formatting a message if they differ.
fn attr_comp(old: &AttMap, new: &AttMap, name: &Path) {
    let mut new = new.clone();
    let mut old = old.clone();
    let mut diffs = vec![];

    // The ctime and ino will be different if a backup is restored, and
    // we'd still like to get meaningful results out of it.
    old.remove("ctime");
    new.remove("ctime");
    old.remove("ino");
    new.remove("ino");

    for (k, v) in &new {
        match old.get(k) {
            None => error!("Added attribute: {}", k),
            Some(ov) => if v != ov {
                diffs.push(k.clone());
            },
        }
        old.remove(k);
    }

    for k in old.keys() {
        error!("Missing attribute: {}", k);
    }

    if diffs.len() > 0 {
        let mut message = vec![];
        for ent in &diffs {
            write!(&mut message, ",{}", ent).unwrap();
        }
        let message = String::from_utf8(message).unwrap();
        println!("  [{:<20}] {}", &message[1..], name.to_string_lossy());
    }
}
<commit_msg>comp: Don't require 'size' to match<commit_after>// Comparisons between trees.

use std::collections::BTreeMap;
use std::io::prelude::*;
use std::path::Path;

use super::suretree::{AttMap, SureTree, PathAdd};

pub trait TreeUpdate {
    /// Update any sha1 hashes in `self` using `old` as a reference.
    /// This looks for files that have sufficiently similar attributes that
    /// we can assume the sha1 hash is the same.
    fn update_from(&mut self, old: &Self);
}

impl TreeUpdate for SureTree {
    fn update_from(&mut self, old: &SureTree) {
        walk(self, old);
    }
}

fn walk(new: &mut SureTree, old: &SureTree) {
    // Walk all of the directories that are possible.
    let old_children: BTreeMap<&str, &SureTree> =
        old.children.iter().map(|ch| (&ch.name[..], ch)).collect();

    for ch in &mut new.children {
        old_children.get(&ch.name[..]).map(|och| walk(ch, och));
    }

    // Walk the file nodes that are the same, and see if they can be
    // updated.
    let old_files: BTreeMap<&str, &AttMap> =
        old.files.iter().map(|ch| (&ch.name[..], &ch.atts)).collect();

    for file in &mut new.files {
        let atts = match old_files.get(&file.name[..]) {
            None => continue,
            Some(atts) => atts,
        };

        // If new already has a node, don't do anything.  This shouldn't
        // normally happen.
        if file.atts.contains_key("sha1") {
            continue;
        }

        // Only compare files.
        if file.atts["kind"] != "file" || atts["kind"] != "file" {
            continue;
        }

        if file.atts.get("ino") != atts.get("ino") ||
            file.atts.get("ctime") != atts.get("ctime")
        {
            continue;
        }

        // Make sure there is actually a sha1 to get.
        match atts.get("sha1") {
            None => continue,
            Some(v) => {
                file.atts.insert("sha1".to_string(), v.to_string());
            },
        }
    }
}

pub trait TreeCompare {
    /// Compare two trees, reporting (to stdout) any differences between
    /// them.
    fn compare_from(&self, old: &Self, path: &Path);
}

impl TreeCompare for SureTree {
    fn compare_from(&self, old: &Self, path: &Path) {
        compwalk(self, old, path);
    }
}

fn compwalk(new: &SureTree, old: &SureTree, path: &Path) {
    // Walk and compare directories.
    let mut old_children: BTreeMap<&String, &SureTree> =
        old.children.iter().map(|ch| (&ch.name, ch)).collect();

    for ch in &new.children {
        let cpath = ch.join(&path);
        match old_children.get(&ch.name) {
            None => println!("+ {:22} {}", "dir", cpath.to_string_lossy()),
            Some(och) => compwalk(ch, och, &cpath),
        }
        old_children.remove(&ch.name);
    }

    // Print out any directories that have been removed.
    // TODO: This print out of order.
    for &name in old_children.keys() {
        println!("- {:22} {}", "dir", name.join(&path).to_string_lossy());
    }

    // Walk and compare files.
    let mut old_files: BTreeMap<&str, &AttMap> =
        old.files.iter().map(|ch| (&ch.name[..], &ch.atts)).collect();

    for file in &new.files {
        let fpath = file.join(&path);
        match old_files.get(&file.name[..]) {
            None => println!("+ {:22} {}", "file", fpath.to_string_lossy()),
            Some(atts) => attr_comp(atts, &file.atts, &fpath),
        }
        old_files.remove(&file.name[..]);
    }

    // Print out any files that have been removed.
    for name in old_files.keys() {
        println!("- {:22} {}", "file", name.join(&path).to_string_lossy());
    }
}

// Compare the old and new attributes, formatting a message if they differ.
fn attr_comp(old: &AttMap, new: &AttMap, name: &Path) {
    let mut new = new.clone();
    let mut old = old.clone();
    let mut diffs = vec![];

    // The ctime and ino will be different if a backup is restored, and
    // we'd still like to get meaningful results out of it.
    old.remove("ctime");
    new.remove("ctime");
    old.remove("ino");
    new.remove("ino");

    for (k, v) in &new {
        match old.get(k) {
            None => error!("Added attribute: {}", k),
            Some(ov) => if v != ov {
                diffs.push(k.clone());
            },
        }
        old.remove(k);
    }

    for k in old.keys() {
        error!("Missing attribute: {}", k);
    }

    if diffs.len() > 0 {
        let mut message = vec![];
        for ent in &diffs {
            write!(&mut message, ",{}", ent).unwrap();
        }
        let message = String::from_utf8(message).unwrap();
        println!("  [{:<20}] {}", &message[1..], name.to_string_lossy());
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use crate::{
    diagnostic_reporter::{get_diagnostics_data, DiagnosticReporter},
    js_language_server::JSLanguageServer,
    lsp_runtime_error::LSPRuntimeResult,
    node_resolution_info::{get_node_resolution_info, NodeResolutionInfo},
    utils::extract_project_name_from_url,
    utils::{extract_executable_definitions_from_text, extract_executable_document_from_text},
};
use crate::{ExtensionConfig, LSPExtraDataProvider};
use common::{Diagnostic as CompilerDiagnostic, PerfLogger, SourceLocationKey, Span};
use crossbeam::channel::Sender;
use dashmap::{mapref::entry::Entry, DashMap};
use fnv::FnvBuildHasher;
use graphql_ir::{
    build_ir_with_extra_features, BuilderOptions, FragmentVariablesSemantic, Program,
};
use graphql_syntax::{
    parse_executable_with_error_recovery, ExecutableDefinition, ExecutableDocument, GraphQLSource,
};
use interner::{Intern, StringKey};
use log::debug;
use lsp_server::Message;
use lsp_types::{Diagnostic, DiagnosticSeverity, Range, TextDocumentPositionParams, Url};
use relay_compiler::{
    compiler::Compiler,
    config::{Config, ProjectConfig},
    FileCategorizer,
};
use schema::SDLSchema;
use schema_documentation::{
    CombinedSchemaDocumentation, SchemaDocumentation, SchemaDocumentationLoader,
};
use std::{collections::HashMap, path::PathBuf, sync::Arc};
use tokio::{sync::Notify, task};

use super::lsp_state_resources::LSPStateResources;

pub type Schemas = Arc<DashMap<StringKey, Arc<SDLSchema>, FnvBuildHasher>>;
pub type SourcePrograms = Arc<DashMap<StringKey, Program, FnvBuildHasher>>;
pub type ProjectStatusMap = Arc<DashMap<StringKey, ProjectStatus, FnvBuildHasher>>;

#[derive(Eq, PartialEq)]
pub enum ProjectStatus {
    Activated,
    Completed,
}

/// This structure contains all available resources that we may use in the Relay LSP message/notification
/// handlers. Such as schema, programs, extra_data_providers, etc...
pub struct LSPState<TPerfLogger: PerfLogger + 'static, TSchemaDocumentation: SchemaDocumentation> {
    config: Arc<Config>,
    compiler: Option<Compiler<TPerfLogger>>,
    root_dir: PathBuf,
    root_dir_str: String,
    pub extra_data_provider: Box<dyn LSPExtraDataProvider>,
    pub file_categorizer: FileCategorizer,
    pub schemas: Schemas,
    schema_documentation_loader: Option<Box<dyn SchemaDocumentationLoader<TSchemaDocumentation>>>,
    source_programs: SourcePrograms,
    synced_graphql_documents: HashMap<Url, Vec<GraphQLSource>>,
    perf_logger: Arc<TPerfLogger>,
    diagnostic_reporter: Arc<DiagnosticReporter>,
    notify_sender: Arc<Notify>,
    project_status: ProjectStatusMap,
    pub js_resource: Box<dyn JSLanguageServer<TPerfLogger, TSchemaDocumentation>>,
}

impl<TPerfLogger: PerfLogger + 'static, TSchemaDocumentation: SchemaDocumentation>
    LSPState<TPerfLogger, TSchemaDocumentation>
{
    /// Private constructor
    fn new(
        config: Arc<Config>,
        perf_logger: Arc<TPerfLogger>,
        extra_data_provider: Box<dyn LSPExtraDataProvider>,
        schema_documentation_loader: Option<
            Box<dyn SchemaDocumentationLoader<TSchemaDocumentation>>,
        >,
        js_resource: Box<dyn JSLanguageServer<TPerfLogger, TSchemaDocumentation>>,
        sender: Sender<Message>,
    ) -> Self {
        let file_categorizer = FileCategorizer::from_config(&config);
        let root_dir = &config.root_dir.clone();
        let diagnostic_reporter =
            Arc::new(DiagnosticReporter::new(config.root_dir.clone(), sender));

        Self {
            compiler: None,
            config,
            diagnostic_reporter,
            extra_data_provider,
            file_categorizer,
            notify_sender: Arc::new(Notify::new()),
            perf_logger,
            project_status: Arc::new(DashMap::with_hasher(FnvBuildHasher::default())),
            root_dir_str: root_dir.to_string_lossy().to_string(),
            root_dir: root_dir.clone(),
            schemas: Arc::new(DashMap::with_hasher(FnvBuildHasher::default())),
            schema_documentation_loader,
            source_programs: Arc::new(DashMap::with_hasher(FnvBuildHasher::default())),
            synced_graphql_documents: Default::default(),
            js_resource,
        }
    }

    /// This method is responsible for creating schema/source_programs for LSP internal state
    /// - so the LSP can provide these to Hover/Completion/GoToDefinition requests.
    /// It also creates a watchman subscription that is responsible for keeping these resources up-to-date.
    pub(crate) fn create_state(
        config: Arc<Config>,
        perf_logger: Arc<TPerfLogger>,
        extra_data_provider: Box<dyn LSPExtraDataProvider>,
        schema_documentation_loader: Option<
            Box<dyn SchemaDocumentationLoader<TSchemaDocumentation>>,
        >,
        js_resource: Box<dyn JSLanguageServer<TPerfLogger, TSchemaDocumentation>>,
        extensions_config: &ExtensionConfig,
        sender: Sender<Message>,
    ) -> Self {
        debug!("Creating lsp_state...");
        let mut lsp_state = Self::new(
            config,
            perf_logger,
            extra_data_provider,
            schema_documentation_loader,
            js_resource,
            sender.clone(),
        );

        // Preload schema documentation - this will warm-up schema documentation cache in the LSP Extra Data providers
        lsp_state.preload_documentation();

        let config_clone = Arc::clone(&lsp_state.config);
        let perf_logger_clone = Arc::clone(&lsp_state.perf_logger);
        let schemas = Arc::clone(&lsp_state.schemas);
        let source_programs = Arc::clone(&lsp_state.source_programs);
        let diagnostic_reporter = Arc::clone(&lsp_state.diagnostic_reporter);
        let notify_sender = Arc::clone(&lsp_state.notify_sender);
        let project_status = Arc::clone(&lsp_state.project_status);

        task::spawn(async move {
            let resources = LSPStateResources::new(
                config_clone,
                perf_logger_clone,
                schemas,
                source_programs,
                sender,
                diagnostic_reporter,
                notify_sender,
                project_status,
            );
            resources.watch().await.unwrap();
        });

        lsp_state.compiler = if extensions_config.enable_compiler {
            debug!("extensions_config.enable_compiler = true");
            Some(Compiler::new(
                Arc::clone(&lsp_state.config),
                Arc::clone(&lsp_state.perf_logger),
            ))
        } else {
            None
        };

        debug!("Creating lsp_state created!");
        lsp_state
    }

    pub fn get_schemas(&self) -> Schemas {
        self.schemas.clone()
    }

    pub(crate) fn get_source_programs_ref(&self) -> &SourcePrograms {
        &self.source_programs
    }

    pub(crate) fn resolve_node(
        &self,
        text_document_position: &TextDocumentPositionParams,
    ) -> LSPRuntimeResult<NodeResolutionInfo> {
        get_node_resolution_info(
            text_document_position,
            &self.synced_graphql_documents,
            &self.file_categorizer,
            &self.root_dir,
        )
    }

    pub(crate) fn resolve_executable_definitions(
        &self,
        params: &TextDocumentPositionParams,
    ) -> LSPRuntimeResult<Vec<ExecutableDefinition>> {
        extract_executable_definitions_from_text(params, &self.synced_graphql_documents)
    }

    pub(crate) fn root_dir(&self) -> &PathBuf {
        &self.root_dir
    }

    pub(crate) fn root_dir_str(&self) -> &str {
        &self.root_dir_str
    }

    pub(crate) fn remove_synced_sources(&mut self, url: &Url) {
        self.synced_graphql_documents.remove(url);
        self.diagnostic_reporter
            .clear_quick_diagnostics_for_url(url);
    }

    pub(crate) fn extract_executable_document_from_text(
        &mut self,
        position: &TextDocumentPositionParams,
        index_offset: usize,
    ) -> LSPRuntimeResult<(ExecutableDocument, Span, StringKey)> {
        extract_executable_document_from_text(
            &position,
            &self.synced_graphql_documents,
            &self.file_categorizer,
            &self.root_dir,
            index_offset,
        )
    }

    pub(crate) fn process_synced_sources(
        &mut self,
        url: Url,
        sources: Vec<GraphQLSource>,
    ) -> LSPRuntimeResult<()> {
        let project_name = self.extract_project_name_from_url(&url)?;

        if let Entry::Vacant(e) = self.project_status.entry(project_name) {
            e.insert(ProjectStatus::Activated);
            self.notify_sender.notify_one();
        }

        self.validate_synced_sources(url.clone(), project_name, &sources);
        self.insert_synced_sources(url, sources);
        Ok(())
    }

    pub fn extract_project_name_from_url(&self, url: &Url) -> LSPRuntimeResult<StringKey> {
        extract_project_name_from_url(&self.file_categorizer, url, &self.root_dir)
    }

    fn insert_synced_sources(&mut self, url: Url, sources: Vec<GraphQLSource>) {
        self.start_compiler_once();
        self.synced_graphql_documents.insert(url, sources);
    }

    fn validate_synced_sources(
        &mut self,
        url: Url,
        project_name: StringKey,
        graphql_sources: &[GraphQLSource],
    ) {
        let mut diagnostics = vec![];
        for graphql_source in graphql_sources {
            let result = parse_executable_with_error_recovery(
                &graphql_source.text,
                SourceLocationKey::standalone(&url.to_string()),
            );

            diagnostics.extend(
                result
                    .errors
                    .into_iter()
                    .map(|diagnostic| convert_diagnostic(graphql_source, diagnostic)),
            );
            if let Some(schema) = self.schemas.get(&project_name) {
                if let Err(errors) = build_ir_with_extra_features(
                    &schema,
                    &result.item.definitions,
                    BuilderOptions {
                        allow_undefined_fragment_spreads: true,
                        fragment_variables_semantic: FragmentVariablesSemantic::PassedValue,
                        relay_mode: true,
                        default_anonymous_operation_name: None,
                    },
                ) {
                    diagnostics.extend(
                        errors
                            .into_iter()
                            .map(|diagnostic| convert_diagnostic(graphql_source, diagnostic)),
                    );
                }
            }
        }
        self.diagnostic_reporter
            .update_quick_diagnostics_for_url(url, diagnostics);
    }

    fn start_compiler_once(&mut self) {
        if let Some(compiler) = self.compiler.take() {
            tokio::spawn(async move { compiler.watch().await });
        }
    }

    fn preload_documentation(&self) {
        for project_config in self.config.enabled_projects() {
            self.get_schema_documentation(&project_config.name.to_string());
        }
    }

    pub fn get_project_config_ref(&self, project_name: StringKey) -> Option<&ProjectConfig> {
        self.config
            .enabled_projects()
            .find(|project_config| project_config.name == project_name)
    }

    pub fn get_config(&self) -> Arc<Config> {
        self.config.clone()
    }

    pub fn get_logger(&self) -> Arc<TPerfLogger> {
        self.perf_logger.clone()
    }

    pub fn get_schema_documentation(&self, schema_name: &str) -> impl SchemaDocumentation {
        let primary = self
            .schemas
            .get(&schema_name.intern())
            .map(|x| Arc::clone(x.value()));
        let secondary = self
            .schema_documentation_loader
            .as_ref()
            .map(|loader| loader.get_schema_documentation(schema_name));

        CombinedSchemaDocumentation::new(primary, secondary)
    }

    /// Given a Range return first available diagnostic message for it
    pub(crate) fn get_diagnostic_for_range(&self, url: &Url, range: Range) -> Option<Diagnostic> {
        self.diagnostic_reporter
            .get_diagnostics_for_range(url, range)
    }
}

fn convert_diagnostic(
    graphql_source: &GraphQLSource,
    diagnostic: CompilerDiagnostic,
) -> Diagnostic {
    Diagnostic {
        code: None,
        data: get_diagnostics_data(&diagnostic),
        message: diagnostic.message().to_string(),
        range: diagnostic.location().span().to_range(
            &graphql_source.text,
            graphql_source.line_index,
            graphql_source.column_index,
        ),
        related_information: None,
        severity: Some(DiagnosticSeverity::Error),
        source: None,
        tags: None,
        ..Default::default()
    }
}
<commit_msg>Make fields on LSPState private<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use crate::{
    diagnostic_reporter::{get_diagnostics_data, DiagnosticReporter},
    js_language_server::JSLanguageServer,
    lsp_runtime_error::LSPRuntimeResult,
    node_resolution_info::{get_node_resolution_info, NodeResolutionInfo},
    utils::extract_project_name_from_url,
    utils::{extract_executable_definitions_from_text, extract_executable_document_from_text},
};
use crate::{ExtensionConfig, LSPExtraDataProvider};
use common::{Diagnostic as CompilerDiagnostic, PerfLogger, SourceLocationKey, Span};
use crossbeam::channel::Sender;
use dashmap::{mapref::entry::Entry, DashMap};
use fnv::FnvBuildHasher;
use graphql_ir::{
    build_ir_with_extra_features, BuilderOptions, FragmentVariablesSemantic, Program,
};
use graphql_syntax::{
    parse_executable_with_error_recovery, ExecutableDefinition, ExecutableDocument, GraphQLSource,
};
use interner::{Intern, StringKey};
use log::debug;
use lsp_server::Message;
use lsp_types::{Diagnostic, DiagnosticSeverity, Range, TextDocumentPositionParams, Url};
use relay_compiler::{
    compiler::Compiler,
    config::{Config, ProjectConfig},
    FileCategorizer,
};
use schema::SDLSchema;
use schema_documentation::{
    CombinedSchemaDocumentation, SchemaDocumentation, SchemaDocumentationLoader,
};
use std::{collections::HashMap, path::PathBuf, sync::Arc};
use tokio::{sync::Notify, task};

use super::lsp_state_resources::LSPStateResources;

pub type Schemas = Arc<DashMap<StringKey, Arc<SDLSchema>, FnvBuildHasher>>;
pub type SourcePrograms = Arc<DashMap<StringKey, Program, FnvBuildHasher>>;
pub type ProjectStatusMap = Arc<DashMap<StringKey, ProjectStatus, FnvBuildHasher>>;

#[derive(Eq, PartialEq)]
pub enum ProjectStatus {
    Activated,
    Completed,
}

/// This structure contains all available resources that we may use in the Relay LSP message/notification
/// handlers. Such as schema, programs, extra_data_providers, etc...
pub struct LSPState<TPerfLogger: PerfLogger + 'static, TSchemaDocumentation: SchemaDocumentation> {
    config: Arc<Config>,
    compiler: Option<Compiler<TPerfLogger>>,
    root_dir: PathBuf,
    root_dir_str: String,
    pub extra_data_provider: Box<dyn LSPExtraDataProvider>,
    file_categorizer: FileCategorizer,
    schemas: Schemas,
    schema_documentation_loader: Option<Box<dyn SchemaDocumentationLoader<TSchemaDocumentation>>>,
    source_programs: SourcePrograms,
    synced_graphql_documents: HashMap<Url, Vec<GraphQLSource>>,
    perf_logger: Arc<TPerfLogger>,
    diagnostic_reporter: Arc<DiagnosticReporter>,
    notify_sender: Arc<Notify>,
    project_status: ProjectStatusMap,
    pub js_resource: Box<dyn JSLanguageServer<TPerfLogger, TSchemaDocumentation>>,
}

impl<TPerfLogger: PerfLogger + 'static, TSchemaDocumentation: SchemaDocumentation>
    LSPState<TPerfLogger, TSchemaDocumentation>
{
    /// Private constructor
    fn new(
        config: Arc<Config>,
        perf_logger: Arc<TPerfLogger>,
        extra_data_provider: Box<dyn LSPExtraDataProvider>,
        schema_documentation_loader: Option<
            Box<dyn SchemaDocumentationLoader<TSchemaDocumentation>>,
        >,
        js_resource: Box<dyn JSLanguageServer<TPerfLogger, TSchemaDocumentation>>,
        sender: Sender<Message>,
    ) -> Self {
        let file_categorizer = FileCategorizer::from_config(&config);
        let root_dir = &config.root_dir.clone();
        let diagnostic_reporter =
            Arc::new(DiagnosticReporter::new(config.root_dir.clone(), sender));

        Self {
            compiler: None,
            config,
            diagnostic_reporter,
            extra_data_provider,
            file_categorizer,
            notify_sender: Arc::new(Notify::new()),
            perf_logger,
            project_status: Arc::new(DashMap::with_hasher(FnvBuildHasher::default())),
            root_dir_str: root_dir.to_string_lossy().to_string(),
            root_dir: root_dir.clone(),
            schemas: Arc::new(DashMap::with_hasher(FnvBuildHasher::default())),
            schema_documentation_loader,
            source_programs: Arc::new(DashMap::with_hasher(FnvBuildHasher::default())),
            synced_graphql_documents: Default::default(),
            js_resource,
        }
    }

    /// This method is responsible for creating schema/source_programs for LSP internal state
    /// - so the LSP can provide these to Hover/Completion/GoToDefinition requests.
    /// It also creates a watchman subscription that is responsible for keeping these resources up-to-date.
    pub(crate) fn create_state(
        config: Arc<Config>,
        perf_logger: Arc<TPerfLogger>,
        extra_data_provider: Box<dyn LSPExtraDataProvider>,
        schema_documentation_loader: Option<
            Box<dyn SchemaDocumentationLoader<TSchemaDocumentation>>,
        >,
        js_resource: Box<dyn JSLanguageServer<TPerfLogger, TSchemaDocumentation>>,
        extensions_config: &ExtensionConfig,
        sender: Sender<Message>,
    ) -> Self {
        debug!("Creating lsp_state...");
        let mut lsp_state = Self::new(
            config,
            perf_logger,
            extra_data_provider,
            schema_documentation_loader,
            js_resource,
            sender.clone(),
        );

        // Preload schema documentation - this will warm-up schema documentation cache in the LSP Extra Data providers
        lsp_state.preload_documentation();

        let config_clone = Arc::clone(&lsp_state.config);
        let perf_logger_clone = Arc::clone(&lsp_state.perf_logger);
        let schemas = Arc::clone(&lsp_state.schemas);
        let source_programs = Arc::clone(&lsp_state.source_programs);
        let diagnostic_reporter = Arc::clone(&lsp_state.diagnostic_reporter);
        let notify_sender = Arc::clone(&lsp_state.notify_sender);
        let project_status = Arc::clone(&lsp_state.project_status);

        task::spawn(async move {
            let resources = LSPStateResources::new(
                config_clone,
                perf_logger_clone,
                schemas,
                source_programs,
                sender,
                diagnostic_reporter,
                notify_sender,
                project_status,
            );
            resources.watch().await.unwrap();
        });

        lsp_state.compiler = if extensions_config.enable_compiler {
            debug!("extensions_config.enable_compiler = true");
            Some(Compiler::new(
                Arc::clone(&lsp_state.config),
                Arc::clone(&lsp_state.perf_logger),
            ))
        } else {
            None
        };

        debug!("Creating lsp_state created!");
        lsp_state
    }

    pub fn get_schemas(&self) -> Schemas {
        self.schemas.clone()
    }

    pub(crate) fn get_source_programs_ref(&self) -> &SourcePrograms {
        &self.source_programs
    }

    pub(crate) fn resolve_node(
        &self,
        text_document_position: &TextDocumentPositionParams,
    ) -> LSPRuntimeResult<NodeResolutionInfo> {
        get_node_resolution_info(
            text_document_position,
            &self.synced_graphql_documents,
            &self.file_categorizer,
            &self.root_dir,
        )
    }

    pub(crate) fn resolve_executable_definitions(
        &self,
        params: &TextDocumentPositionParams,
    ) -> LSPRuntimeResult<Vec<ExecutableDefinition>> {
        extract_executable_definitions_from_text(params, &self.synced_graphql_documents)
    }

    pub(crate) fn root_dir(&self) -> &PathBuf {
        &self.root_dir
    }

    pub(crate) fn root_dir_str(&self) -> &str {
        &self.root_dir_str
    }

    pub(crate) fn remove_synced_sources(&mut self, url: &Url) {
        self.synced_graphql_documents.remove(url);
        self.diagnostic_reporter
            .clear_quick_diagnostics_for_url(url);
    }

    pub(crate) fn extract_executable_document_from_text(
        &mut self,
        position: &TextDocumentPositionParams,
        index_offset: usize,
    ) -> LSPRuntimeResult<(ExecutableDocument, Span, StringKey)> {
        extract_executable_document_from_text(
            &position,
            &self.synced_graphql_documents,
            &self.file_categorizer,
            &self.root_dir,
            index_offset,
        )
    }

    pub(crate) fn process_synced_sources(
        &mut self,
        url: Url,
        sources: Vec<GraphQLSource>,
    ) -> LSPRuntimeResult<()> {
        let project_name = self.extract_project_name_from_url(&url)?;

        if let Entry::Vacant(e) = self.project_status.entry(project_name) {
            e.insert(ProjectStatus::Activated);
            self.notify_sender.notify_one();
        }

        self.validate_synced_sources(url.clone(), project_name, &sources);
        self.insert_synced_sources(url, sources);
        Ok(())
    }

    pub fn extract_project_name_from_url(&self, url: &Url) -> LSPRuntimeResult<StringKey> {
        extract_project_name_from_url(&self.file_categorizer, url, &self.root_dir)
    }

    fn insert_synced_sources(&mut self, url: Url, sources: Vec<GraphQLSource>) {
        self.start_compiler_once();
        self.synced_graphql_documents.insert(url, sources);
    }

    fn validate_synced_sources(
        &mut self,
        url: Url,
        project_name: StringKey,
        graphql_sources: &[GraphQLSource],
    ) {
        let mut diagnostics = vec![];
        for graphql_source in graphql_sources {
            let result = parse_executable_with_error_recovery(
                &graphql_source.text,
                SourceLocationKey::standalone(&url.to_string()),
            );

            diagnostics.extend(
                result
                    .errors
                    .into_iter()
                    .map(|diagnostic| convert_diagnostic(graphql_source, diagnostic)),
            );
            if let Some(schema) = self.schemas.get(&project_name) {
                if let Err(errors) = build_ir_with_extra_features(
                    &schema,
                    &result.item.definitions,
                    BuilderOptions {
                        allow_undefined_fragment_spreads: true,
                        fragment_variables_semantic: FragmentVariablesSemantic::PassedValue,
                        relay_mode: true,
                        default_anonymous_operation_name: None,
                    },
                ) {
                    diagnostics.extend(
                        errors
                            .into_iter()
                            .map(|diagnostic| convert_diagnostic(graphql_source, diagnostic)),
                    );
                }
            }
        }
        self.diagnostic_reporter
            .update_quick_diagnostics_for_url(url, diagnostics);
    }

    fn start_compiler_once(&mut self) {
        if let Some(compiler) = self.compiler.take() {
            tokio::spawn(async move { compiler.watch().await });
        }
    }

    fn preload_documentation(&self) {
        for project_config in self.config.enabled_projects() {
            self.get_schema_documentation(&project_config.name.to_string());
        }
    }

    pub fn get_project_config_ref(&self, project_name: StringKey) -> Option<&ProjectConfig> {
        self.config
            .enabled_projects()
            .find(|project_config| project_config.name == project_name)
    }

    pub fn get_config(&self) -> Arc<Config> {
        self.config.clone()
    }

    pub fn get_logger(&self) -> Arc<TPerfLogger> {
        self.perf_logger.clone()
    }

    pub fn get_schema_documentation(&self, schema_name: &str) -> impl SchemaDocumentation {
        let primary = self
            .schemas
            .get(&schema_name.intern())
            .map(|x| Arc::clone(x.value()));
        let secondary = self
            .schema_documentation_loader
            .as_ref()
            .map(|loader| loader.get_schema_documentation(schema_name));

        CombinedSchemaDocumentation::new(primary, secondary)
    }

    /// Given a Range return first available diagnostic message for it
    pub(crate) fn get_diagnostic_for_range(&self, url: &Url, range: Range) -> Option<Diagnostic> {
        self.diagnostic_reporter
            .get_diagnostics_for_range(url, range)
    }
}

fn convert_diagnostic(
    graphql_source: &GraphQLSource,
    diagnostic: CompilerDiagnostic,
) -> Diagnostic {
    Diagnostic {
        code: None,
        data: get_diagnostics_data(&diagnostic),
        message: diagnostic.message().to_string(),
        range: diagnostic.location().span().to_range(
            &graphql_source.text,
            graphql_source.line_index,
            graphql_source.column_index,
        ),
        related_information: None,
        severity: Some(DiagnosticSeverity::Error),
        source: None,
        tags: None,
        ..Default::default()
    }
}
<|endoftext|>