<commit_before>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Alex Lyon <arcterus@mail.com>
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.

// spell-checker:ignore (ToDO) bitor ulong

extern crate clap;
extern crate remove_dir_all;
extern crate walkdir;

#[macro_use]
extern crate uucore;

use clap::{App, Arg};
use remove_dir_all::remove_dir_all;
use std::collections::VecDeque;
use std::fs;
use std::io::{stderr, stdin, BufRead, Write};
use std::ops::BitOr;
use std::path::Path;
use walkdir::{DirEntry, WalkDir};

#[derive(Eq, PartialEq, Clone, Copy)]
enum InteractiveMode {
    None,
    Once,
    Always,
}

struct Options {
    force: bool,
    interactive: InteractiveMode,
    #[allow(dead_code)]
    one_fs: bool,
    preserve_root: bool,
    recursive: bool,
    dir: bool,
    verbose: bool,
}

static ABOUT: &str = "Remove (unlink) the FILE(s)";
static VERSION: &str = env!("CARGO_PKG_VERSION");
static OPT_DIR: &str = "dir";
static OPT_INTERACTIVE: &str = "interactive";
static OPT_FORCE: &str = "force";
static OPT_NO_PRESERVE_ROOT: &str = "no-preserve-root";
static OPT_ONE_FILE_SYSTEM: &str = "one-file-system";
static OPT_PRESERVE_ROOT: &str = "preserve-root";
static OPT_PROMPT: &str = "prompt";
static OPT_PROMPT_MORE: &str = "prompt-more";
static OPT_RECURSIVE: &str = "recursive";
static OPT_VERBOSE: &str = "verbose";

static ARG_FILES: &str = "files";

fn get_usage() -> String {
    format!("{0} [OPTION]... FILE...", executable!())
}

fn get_long_usage() -> String {
    String::from(
        "By default, rm does not remove directories.  Use the --recursive (-r)
        option to remove each listed directory, too, along with all of its contents

        To remove a file whose name starts with a '-', for example '-foo',
        use one of these commands:
        rm -- -foo

        rm ./-foo

        Note that if you use rm to remove a file, it might be possible to recover
        some of its contents, given sufficient expertise and/or time.  For greater
        assurance that the contents are truly unrecoverable, consider using shred.",
    )
}

pub fn uumain(args: impl uucore::Args) -> i32 {
    let usage = get_usage();
    let long_usage = get_long_usage();

    let matches = App::new(executable!())
        .version(VERSION)
        .about(ABOUT)
        .usage(&usage[..])
        .after_help(&long_usage[..])
    // TODO: make getopts support -R in addition to -r

        .arg(
            Arg::with_name(OPT_FORCE)
            .short("f")
            .long(OPT_FORCE)
            .help("ignore nonexistent files and arguments, never prompt")
        )
        .arg(
            Arg::with_name(OPT_PROMPT)
            .short("i")
            .long("prompt before every removal")
        )
        .arg(
            Arg::with_name(OPT_PROMPT_MORE)
            .short("I")
            .help("prompt once before removing more than three files, or when removing recursively. Less intrusive than -i, while still giving some protection against most mistakes")
        )
        .arg(
            Arg::with_name(OPT_INTERACTIVE)
            .long(OPT_INTERACTIVE)
            .help("prompt according to WHEN: never, once (-I), or always (-i). Without WHEN, prompts always")
            .value_name("WHEN")
            .takes_value(true)
        )
        .arg(
            Arg::with_name(OPT_ONE_FILE_SYSTEM)
            .long(OPT_ONE_FILE_SYSTEM)
            .help("when removing a hierarchy recursively, skip any directory that is on a file system different from that of the corresponding command line argument (NOT IMPLEMENTED)")
        )
        .arg(
            Arg::with_name(OPT_NO_PRESERVE_ROOT)
            .long(OPT_NO_PRESERVE_ROOT)
            .help("do not treat '/' specially")
        )
        .arg(
            Arg::with_name(OPT_PRESERVE_ROOT)
            .long(OPT_PRESERVE_ROOT)
            .help("do not remove '/' (default)")
        )
        .arg(
            Arg::with_name(OPT_RECURSIVE).short("r")
            .long(OPT_RECURSIVE)
            .help("remove directories and their contents recursively")
        )
        .arg(
            Arg::with_name(OPT_DIR)
            .short("d")
            .long(OPT_DIR)
            .help("remove empty directories")
        )
        .arg(
            Arg::with_name(OPT_VERBOSE)
            .short("v")
            .long(OPT_VERBOSE)
            .help("explain what is being done")
        )
        .arg(
            Arg::with_name(ARG_FILES)
            .multiple(true)
            .takes_value(true)
            .min_values(1)
        )
        .get_matches_from(args);

    let files: Vec<String> = matches
        .values_of(ARG_FILES)
        .map(|v| v.map(ToString::to_string).collect())
        .unwrap_or_default();

    let force = matches.is_present(OPT_FORCE);

    if files.is_empty() && !force {
        // Still check by hand and not use clap
        // Because "rm -f" is a thing
        show_error!("missing an argument");
        show_error!("for help, try '{0} --help'", executable!());
        return 1;
    } else {
        let options = Options {
            force,
            interactive: {
                if matches.is_present(OPT_PROMPT) {
                    InteractiveMode::Always
                } else if matches.is_present(OPT_PROMPT_MORE) {
                    InteractiveMode::Once
                } else if matches.is_present(OPT_INTERACTIVE) {
                    match &matches.value_of(OPT_INTERACTIVE).unwrap()[..] {
                        "none" => InteractiveMode::None,
                        "once" => InteractiveMode::Once,
                        "always" => InteractiveMode::Always,
                        val => crash!(1, "Invalid argument to interactive ({})", val),
                    }
                } else {
                    InteractiveMode::None
                }
            },
            one_fs: matches.is_present(OPT_ONE_FILE_SYSTEM),
            preserve_root: !matches.is_present(OPT_NO_PRESERVE_ROOT),
            recursive: matches.is_present(OPT_RECURSIVE),
            dir: matches.is_present(OPT_DIR),
            verbose: matches.is_present(OPT_VERBOSE),
        };
        if options.interactive == InteractiveMode::Once && (options.recursive || files.len() > 3) {
            let msg = if options.recursive {
                "Remove all arguments recursively? "
            } else {
                "Remove all arguments? "
            };
            if !prompt(msg) {
                return 0;
            }
        }

        if remove(files, options) {
            return 1;
        }
    }

    0
}

// TODO: implement one-file-system (this may get partially implemented in walkdir)
fn remove(files: Vec<String>, options: Options) -> bool {
    let mut had_err = false;

    for filename in &files {
        let file = Path::new(filename);
        had_err = match file.symlink_metadata() {
            Ok(metadata) => {
                if metadata.is_dir() {
                    handle_dir(file, &options)
                } else if is_symlink_dir(&metadata) {
                    remove_dir(file, &options)
                } else {
                    remove_file(file, &options)
                }
            }
            Err(_e) => {
                // TODO: actually print out the specific error
                // TODO: When the error is not about missing files
                // (e.g., permission), even rm -f should fail with
                // outputting the error, but there's no easy eay.
                if !options.force {
                    show_error!("no such file or directory '{}'", filename);
                    true
                } else {
                    false
                }
            }
        }
        .bitor(had_err);
    }

    had_err
}

fn handle_dir(path: &Path, options: &Options) -> bool {
    let mut had_err = false;

    let is_root = path.has_root() && path.parent().is_none();
    if options.recursive && (!is_root || !options.preserve_root) {
        if options.interactive != InteractiveMode::Always {
            // we need the extra crate because apparently fs::remove_dir_all() does not function
            // correctly on Windows
            if let Err(e) = remove_dir_all(path) {
                had_err = true;
                show_error!("could not remove '{}': {}", path.display(), e);
            }
        } else {
            let mut dirs: VecDeque<DirEntry> = VecDeque::new();

            for entry in WalkDir::new(path) {
                match entry {
                    Ok(entry) => {
                        let file_type = entry.file_type();
                        if file_type.is_dir() {
                            dirs.push_back(entry);
                        } else {
                            had_err = remove_file(entry.path(), options).bitor(had_err);
                        }
                    }
                    Err(e) => {
                        had_err = true;
                        show_error!("recursing in '{}': {}", path.display(), e);
                    }
                }
            }

            for dir in dirs.iter().rev() {
                had_err = remove_dir(dir.path(), options).bitor(had_err);
            }
        }
    } else if options.dir && (!is_root || !options.preserve_root) {
        had_err = remove_dir(path, options).bitor(had_err);
    } else if options.recursive {
        show_error!("could not remove directory '{}'", path.display());
        had_err = true;
    } else {
        show_error!(
            "could not remove directory '{}' (did you mean to pass '-r'?)",
            path.display()
        );
        had_err = true;
    }

    had_err
}

fn remove_dir(path: &Path, options: &Options) -> bool {
    let response = if options.interactive == InteractiveMode::Always {
        prompt_file(path, true)
    } else {
        true
    };
    if response {
        match fs::remove_dir(path) {
            Ok(_) => {
                if options.verbose {
                    println!("removed '{}'", path.display());
                }
            }
            Err(e) => {
                show_error!("removing '{}': {}", path.display(), e);
                return true;
            }
        }
    }

    false
}

fn remove_file(path: &Path, options: &Options) -> bool {
    let response = if options.interactive == InteractiveMode::Always {
        prompt_file(path, false)
    } else {
        true
    };
    if response {
        match fs::remove_file(path) {
            Ok(_) => {
                if options.verbose {
                    println!("removed '{}'", path.display());
                }
            }
            Err(e) => {
                show_error!("removing '{}': {}", path.display(), e);
                return true;
            }
        }
    }

    false
}

fn prompt_file(path: &Path, is_dir: bool) -> bool {
    if is_dir {
        prompt(&(format!("rm: remove directory '{}'? ", path.display())))
    } else {
        prompt(&(format!("rm: remove file '{}'? ", path.display())))
    }
}

fn prompt(msg: &str) -> bool {
    let _ = stderr().write_all(msg.as_bytes());
    let _ = stderr().flush();

    let mut buf = Vec::new();
    let stdin = stdin();
    let mut stdin = stdin.lock();

    #[allow(clippy::match_like_matches_macro)]
    // `matches!(...)` macro not stabilized until rust v1.42
    match stdin.read_until(b'\n', &mut buf) {
        Ok(x) if x > 0 => match buf[0] {
            b'y' | b'Y' => true,
            _ => false,
        },
        _ => false,
    }
}

#[cfg(not(windows))]
fn is_symlink_dir(_metadata: &fs::Metadata) -> bool {
    false
}

#[cfg(windows)]
use std::os::windows::prelude::MetadataExt;

#[cfg(windows)]
fn is_symlink_dir(metadata: &fs::Metadata) -> bool {
    use std::os::raw::c_ulong;
    pub type DWORD = c_ulong;
    pub const FILE_ATTRIBUTE_DIRECTORY: DWORD = 0x10;

    metadata.file_type().is_symlink()
        && ((metadata.file_attributes() & FILE_ATTRIBUTE_DIRECTORY) != 0)
}
<commit_msg>rm: allow "-f" to be specified multiple times (#1667)<commit_after>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Alex Lyon <arcterus@mail.com>
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.

// spell-checker:ignore (ToDO) bitor ulong

extern crate clap;
extern crate remove_dir_all;
extern crate walkdir;

#[macro_use]
extern crate uucore;

use clap::{App, Arg};
use remove_dir_all::remove_dir_all;
use std::collections::VecDeque;
use std::fs;
use std::io::{stderr, stdin, BufRead, Write};
use std::ops::BitOr;
use std::path::Path;
use walkdir::{DirEntry, WalkDir};

#[derive(Eq, PartialEq, Clone, Copy)]
enum InteractiveMode {
    None,
    Once,
    Always,
}

struct Options {
    force: bool,
    interactive: InteractiveMode,
    #[allow(dead_code)]
    one_fs: bool,
    preserve_root: bool,
    recursive: bool,
    dir: bool,
    verbose: bool,
}

static ABOUT: &str = "Remove (unlink) the FILE(s)";
static VERSION: &str = env!("CARGO_PKG_VERSION");
static OPT_DIR: &str = "dir";
static OPT_INTERACTIVE: &str = "interactive";
static OPT_FORCE: &str = "force";
static OPT_NO_PRESERVE_ROOT: &str = "no-preserve-root";
static OPT_ONE_FILE_SYSTEM: &str = "one-file-system";
static OPT_PRESERVE_ROOT: &str = "preserve-root";
static OPT_PROMPT: &str = "prompt";
static OPT_PROMPT_MORE: &str = "prompt-more";
static OPT_RECURSIVE: &str = "recursive";
static OPT_VERBOSE: &str = "verbose";

static ARG_FILES: &str = "files";

fn get_usage() -> String {
    format!("{0} [OPTION]... FILE...", executable!())
}

fn get_long_usage() -> String {
    String::from(
        "By default, rm does not remove directories.  Use the --recursive (-r)
        option to remove each listed directory, too, along with all of its contents

        To remove a file whose name starts with a '-', for example '-foo',
        use one of these commands:
        rm -- -foo

        rm ./-foo

        Note that if you use rm to remove a file, it might be possible to recover
        some of its contents, given sufficient expertise and/or time.  For greater
        assurance that the contents are truly unrecoverable, consider using shred.",
    )
}

pub fn uumain(args: impl uucore::Args) -> i32 {
    let usage = get_usage();
    let long_usage = get_long_usage();

    let matches = App::new(executable!())
        .version(VERSION)
        .about(ABOUT)
        .usage(&usage[..])
        .after_help(&long_usage[..])
    // TODO: make getopts support -R in addition to -r

        .arg(
            Arg::with_name(OPT_FORCE)
            .short("f")
            .long(OPT_FORCE)
            .multiple(true)
            .help("ignore nonexistent files and arguments, never prompt")
        )
        .arg(
            Arg::with_name(OPT_PROMPT)
            .short("i")
            .long("prompt before every removal")
        )
        .arg(
            Arg::with_name(OPT_PROMPT_MORE)
            .short("I")
            .help("prompt once before removing more than three files, or when removing recursively. Less intrusive than -i, while still giving some protection against most mistakes")
        )
        .arg(
            Arg::with_name(OPT_INTERACTIVE)
            .long(OPT_INTERACTIVE)
            .help("prompt according to WHEN: never, once (-I), or always (-i). Without WHEN, prompts always")
            .value_name("WHEN")
            .takes_value(true)
        )
        .arg(
            Arg::with_name(OPT_ONE_FILE_SYSTEM)
            .long(OPT_ONE_FILE_SYSTEM)
            .help("when removing a hierarchy recursively, skip any directory that is on a file system different from that of the corresponding command line argument (NOT IMPLEMENTED)")
        )
        .arg(
            Arg::with_name(OPT_NO_PRESERVE_ROOT)
            .long(OPT_NO_PRESERVE_ROOT)
            .help("do not treat '/' specially")
        )
        .arg(
            Arg::with_name(OPT_PRESERVE_ROOT)
            .long(OPT_PRESERVE_ROOT)
            .help("do not remove '/' (default)")
        )
        .arg(
            Arg::with_name(OPT_RECURSIVE).short("r")
            .long(OPT_RECURSIVE)
            .help("remove directories and their contents recursively")
        )
        .arg(
            Arg::with_name(OPT_DIR)
            .short("d")
            .long(OPT_DIR)
            .help("remove empty directories")
        )
        .arg(
            Arg::with_name(OPT_VERBOSE)
            .short("v")
            .long(OPT_VERBOSE)
            .help("explain what is being done")
        )
        .arg(
            Arg::with_name(ARG_FILES)
            .multiple(true)
            .takes_value(true)
            .min_values(1)
        )
        .get_matches_from(args);

    let files: Vec<String> = matches
        .values_of(ARG_FILES)
        .map(|v| v.map(ToString::to_string).collect())
        .unwrap_or_default();

    let force = matches.is_present(OPT_FORCE);

    if files.is_empty() && !force {
        // Still check by hand and not use clap
        // Because "rm -f" is a thing
        show_error!("missing an argument");
        show_error!("for help, try '{0} --help'", executable!());
        return 1;
    } else {
        let options = Options {
            force,
            interactive: {
                if matches.is_present(OPT_PROMPT) {
                    InteractiveMode::Always
                } else if matches.is_present(OPT_PROMPT_MORE) {
                    InteractiveMode::Once
                } else if matches.is_present(OPT_INTERACTIVE) {
                    match &matches.value_of(OPT_INTERACTIVE).unwrap()[..] {
                        "none" => InteractiveMode::None,
                        "once" => InteractiveMode::Once,
                        "always" => InteractiveMode::Always,
                        val => crash!(1, "Invalid argument to interactive ({})", val),
                    }
                } else {
                    InteractiveMode::None
                }
            },
            one_fs: matches.is_present(OPT_ONE_FILE_SYSTEM),
            preserve_root: !matches.is_present(OPT_NO_PRESERVE_ROOT),
            recursive: matches.is_present(OPT_RECURSIVE),
            dir: matches.is_present(OPT_DIR),
            verbose: matches.is_present(OPT_VERBOSE),
        };
        if options.interactive == InteractiveMode::Once && (options.recursive || files.len() > 3) {
            let msg = if options.recursive {
                "Remove all arguments recursively? "
            } else {
                "Remove all arguments? "
            };
            if !prompt(msg) {
                return 0;
            }
        }

        if remove(files, options) {
            return 1;
        }
    }

    0
}

// TODO: implement one-file-system (this may get partially implemented in walkdir)
fn remove(files: Vec<String>, options: Options) -> bool {
    let mut had_err = false;

    for filename in &files {
        let file = Path::new(filename);
        had_err = match file.symlink_metadata() {
            Ok(metadata) => {
                if metadata.is_dir() {
                    handle_dir(file, &options)
                } else if is_symlink_dir(&metadata) {
                    remove_dir(file, &options)
                } else {
                    remove_file(file, &options)
                }
            }
            Err(_e) => {
                // TODO: actually print out the specific error
                // TODO: When the error is not about missing files
                // (e.g., permission), even rm -f should fail with
                // outputting the error, but there's no easy eay.
                if !options.force {
                    show_error!("no such file or directory '{}'", filename);
                    true
                } else {
                    false
                }
            }
        }
        .bitor(had_err);
    }

    had_err
}

fn handle_dir(path: &Path, options: &Options) -> bool {
    let mut had_err = false;

    let is_root = path.has_root() && path.parent().is_none();
    if options.recursive && (!is_root || !options.preserve_root) {
        if options.interactive != InteractiveMode::Always {
            // we need the extra crate because apparently fs::remove_dir_all() does not function
            // correctly on Windows
            if let Err(e) = remove_dir_all(path) {
                had_err = true;
                show_error!("could not remove '{}': {}", path.display(), e);
            }
        } else {
            let mut dirs: VecDeque<DirEntry> = VecDeque::new();

            for entry in WalkDir::new(path) {
                match entry {
                    Ok(entry) => {
                        let file_type = entry.file_type();
                        if file_type.is_dir() {
                            dirs.push_back(entry);
                        } else {
                            had_err = remove_file(entry.path(), options).bitor(had_err);
                        }
                    }
                    Err(e) => {
                        had_err = true;
                        show_error!("recursing in '{}': {}", path.display(), e);
                    }
                }
            }

            for dir in dirs.iter().rev() {
                had_err = remove_dir(dir.path(), options).bitor(had_err);
            }
        }
    } else if options.dir && (!is_root || !options.preserve_root) {
        had_err = remove_dir(path, options).bitor(had_err);
    } else if options.recursive {
        show_error!("could not remove directory '{}'", path.display());
        had_err = true;
    } else {
        show_error!(
            "could not remove directory '{}' (did you mean to pass '-r'?)",
            path.display()
        );
        had_err = true;
    }

    had_err
}

fn remove_dir(path: &Path, options: &Options) -> bool {
    let response = if options.interactive == InteractiveMode::Always {
        prompt_file(path, true)
    } else {
        true
    };
    if response {
        match fs::remove_dir(path) {
            Ok(_) => {
                if options.verbose {
                    println!("removed '{}'", path.display());
                }
            }
            Err(e) => {
                show_error!("removing '{}': {}", path.display(), e);
                return true;
            }
        }
    }

    false
}

fn remove_file(path: &Path, options: &Options) -> bool {
    let response = if options.interactive == InteractiveMode::Always {
        prompt_file(path, false)
    } else {
        true
    };
    if response {
        match fs::remove_file(path) {
            Ok(_) => {
                if options.verbose {
                    println!("removed '{}'", path.display());
                }
            }
            Err(e) => {
                show_error!("removing '{}': {}", path.display(), e);
                return true;
            }
        }
    }

    false
}

fn prompt_file(path: &Path, is_dir: bool) -> bool {
    if is_dir {
        prompt(&(format!("rm: remove directory '{}'? ", path.display())))
    } else {
        prompt(&(format!("rm: remove file '{}'? ", path.display())))
    }
}

fn prompt(msg: &str) -> bool {
    let _ = stderr().write_all(msg.as_bytes());
    let _ = stderr().flush();

    let mut buf = Vec::new();
    let stdin = stdin();
    let mut stdin = stdin.lock();

    #[allow(clippy::match_like_matches_macro)]
    // `matches!(...)` macro not stabilized until rust v1.42
    match stdin.read_until(b'\n', &mut buf) {
        Ok(x) if x > 0 => match buf[0] {
            b'y' | b'Y' => true,
            _ => false,
        },
        _ => false,
    }
}

#[cfg(not(windows))]
fn is_symlink_dir(_metadata: &fs::Metadata) -> bool {
    false
}

#[cfg(windows)]
use std::os::windows::prelude::MetadataExt;

#[cfg(windows)]
fn is_symlink_dir(metadata: &fs::Metadata) -> bool {
    use std::os::raw::c_ulong;
    pub type DWORD = c_ulong;
    pub const FILE_ATTRIBUTE_DIRECTORY: DWORD = 0x10;

    metadata.file_type().is_symlink()
        && ((metadata.file_attributes() & FILE_ATTRIBUTE_DIRECTORY) != 0)
}
<|endoftext|>


<commit_before>// Copyright (c) 2018 Fabian Schuiki

//! Packages

#![allow(dead_code)]

use std::cell::RefCell;

use common::NodeId;
use common::name::Name;
use common::source::{Span, Spanned, INVALID_SPAN};

use arenas::{Alloc, AllocInto};
use syntax::ast;

make_arenas!(
    pub struct Arenas<'t> {
        package:   Package2<'t>,
        type_decl: TypeDecl2,
    }
);

/// A placeholder for an HIR node.
pub struct Slot<'t, T>(RefCell<SlotState<'t, T>>)
where
    T: FromAst<'t> + 't;

#[derive(Copy, Clone)]
enum SlotState<'t, T>
where
    T: FromAst<'t> + 't,
{
    Fresh(&'t AnyScope, T::Input, T::Arena),
    ReadyOk(&'t T),
    ReadyErr,
}

impl<'t, T> Slot<'t, T>
where
    T: FromAst<'t>,
    T::Arena: AllocInto<'t, T> + Clone,
{
    /// Create a new slot.
    pub fn new(scope: &'t AnyScope, ast: T::Input, arena: T::Arena) -> Slot<'t, T> {
        Slot(RefCell::new(SlotState::Fresh(scope, ast, arena)))
    }

    /// Poll the slot, creating the HIR node from the AST the first time.
    pub fn poll(&self) -> Result<&'t T, ()> {
        match *self.0.borrow() {
            SlotState::ReadyOk(x) => return Ok(x),
            SlotState::ReadyErr => return Err(()),
            _ => (),
        }
        let (scope, ast, arena) = match self.0.replace(SlotState::ReadyErr) {
            SlotState::Fresh(scope, ast, arena) => (scope, ast, arena),
            _ => unreachable!(),
        };
        let node = T::from_ast(scope, ast, arena.clone()).map(|x| arena.alloc(x) as &T);
        self.0.replace(match node {
            Ok(x) => SlotState::ReadyOk(x),
            Err(()) => SlotState::ReadyErr,
        });
        node
    }
}

impl<'t, T> Node for Slot<'t, T>
where
    T: FromAst<'t> + Node,
    T::Arena: AllocInto<'t, T> + Clone,
{
    fn span(&self) -> Span {
        self.poll().map(Node::span).unwrap_or(INVALID_SPAN)
    }
}

pub struct Package2<'t> {
    id: NodeId,
    span: Span,
    name: Spanned<Name>,
    scope: &'t AnyScope,
    decls: Vec<Box<Node + 't>>,
}

impl<'t> FromAst<'t> for Package2<'t> {
    type Input = &'t ast::PkgDecl;
    type Arena = Context<'t>;

    fn alloc_slot(
        scope: &'t AnyScope,
        ast: Self::Input,
        arena: Self::Arena,
    ) -> Result<Slot<'t, Self>, ()> {
        // TODO: register the package name in the scope
        Ok(Slot::new(scope, ast, arena))
    }

    fn from_ast(scope: &'t AnyScope, ast: Self::Input, _arena: Self::Arena) -> Result<Self, ()> {
        // TODO: create a new scope for the package
        let decls = ast.decls
            .iter()
            .flat_map(|decl| -> Option<Box<Node>> {
                match *decl {
                    // ast::DeclItem::PkgDecl(ref decl) => {
                    //     Some(Box::new(Package2::alloc_slot(scope, decl, arena).ok()?))
                    // }
                    // ast::DeclItem::TypeDecl(ref decl) => {
                    //     Some(Box::new(TypeDecl2::alloc_slot(scope, decl, arena).ok()?))
                    // }
                    _ => None,
                }
            })
            .collect::<Vec<_>>();
        Ok(Package2 {
            id: NodeId::alloc(),
            span: ast.span,
            name: ast.name,
            scope: scope,
            decls: decls,
        })
    }
}

impl<'t> Node for Package2<'t> {
    fn span(&self) -> Span {
        self.span
    }
}

pub struct TypeDecl2 {
    id: NodeId,
    span: Span,
    name: Spanned<Name>,
}

impl<'t> FromAst<'t> for TypeDecl2 {
    type Input = &'t ast::TypeDecl;
    type Arena = &'t Alloc<Self>;

    fn alloc_slot(
        scope: &'t AnyScope,
        ast: Self::Input,
        arena: Self::Arena,
    ) -> Result<Slot<'t, Self>, ()> {
        // TODO: register the type name in the scope
        Ok(Slot::new(scope, ast, arena))
    }

    fn from_ast(_scope: &'t AnyScope, ast: Self::Input, _arena: Self::Arena) -> Result<Self, ()> {
        Ok(TypeDecl2 {
            id: NodeId::alloc(),
            span: ast.span,
            name: ast.name,
        })
    }
}

impl Node for TypeDecl2 {
    fn span(&self) -> Span {
        self.span
    }
}

pub trait AnyScope {}

pub trait Node {
    /// The source file location of this node.
    fn span(&self) -> Span;
}

/// Construct something from an AST node.
pub trait FromAst<'t>: Sized {
    type Input;
    type Arena;

    fn alloc_slot(
        scope: &'t AnyScope,
        ast: Self::Input,
        arena: Self::Arena,
    ) -> Result<Slot<'t, Self>, ()>;

    fn from_ast(scope: &'t AnyScope, ast: Self::Input, arena: Self::Arena) -> Result<Self, ()>;
}

#[derive(Copy, Clone)]
pub struct Context<'t> {
    arenas: &'t Arenas<'t>,
}

impl<'t, T> AllocInto<'t, T> for Context<'t>
where
    Arenas<'t>: Alloc<T>,
{
    fn alloc(&self, value: T) -> &'t mut T {
        self.arenas.alloc(value)
    }
}
<commit_msg>vhdl: create pkg slots using new context<commit_after>// Copyright (c) 2018 Fabian Schuiki

//! Packages

#![allow(dead_code)]

use std::cell::RefCell;

use common::NodeId;
use common::name::Name;
use common::source::{Span, Spanned, INVALID_SPAN};

use arenas::{Alloc, AllocInto};
use syntax::ast;

make_arenas!(
    pub struct Arenas<'t> {
        package:   Package2<'t>,
        type_decl: TypeDecl2,
    }
);

/// A placeholder for an HIR node.
pub struct Slot<'t, T>(RefCell<SlotState<'t, T>>)
where
    T: FromAst<'t> + 't;

#[derive(Copy, Clone)]
enum SlotState<'t, T>
where
    T: FromAst<'t> + 't,
{
    Fresh(&'t AnyScope, T::Input, T::Arena),
    ReadyOk(&'t T),
    ReadyErr,
}

impl<'t, T> Slot<'t, T>
where
    T: FromAst<'t>,
    T::Arena: AllocInto<'t, T> + Clone,
{
    /// Create a new slot.
    pub fn new(scope: &'t AnyScope, ast: T::Input, arena: T::Arena) -> Slot<'t, T> {
        Slot(RefCell::new(SlotState::Fresh(scope, ast, arena)))
    }

    /// Poll the slot, creating the HIR node from the AST the first time.
    pub fn poll(&self) -> Result<&'t T, ()> {
        match *self.0.borrow() {
            SlotState::ReadyOk(x) => return Ok(x),
            SlotState::ReadyErr => return Err(()),
            _ => (),
        }
        let (scope, ast, arena) = match self.0.replace(SlotState::ReadyErr) {
            SlotState::Fresh(scope, ast, arena) => (scope, ast, arena),
            _ => unreachable!(),
        };
        let node = T::from_ast(scope, ast, arena.clone()).map(|x| arena.alloc(x) as &T);
        self.0.replace(match node {
            Ok(x) => SlotState::ReadyOk(x),
            Err(()) => SlotState::ReadyErr,
        });
        node
    }
}

impl<'t, T> Node for Slot<'t, T>
where
    T: FromAst<'t> + Node,
    T::Arena: AllocInto<'t, T> + Clone,
{
    fn span(&self) -> Span {
        self.poll().map(Node::span).unwrap_or(INVALID_SPAN)
    }
}

pub struct Package2<'t> {
    id: NodeId,
    span: Span,
    name: Spanned<Name>,
    scope: &'t AnyScope,
    decls: Vec<Box<Node + 't>>,
}

impl<'t> FromAst<'t> for Package2<'t> {
    type Input = &'t ast::PkgDecl;
    type Arena = Context<'t>;

    fn alloc_slot(
        scope: &'t AnyScope,
        ast: Self::Input,
        arena: Self::Arena,
    ) -> Result<Slot<'t, Self>, ()> {
        // TODO: register the package name in the scope
        Ok(Slot::new(scope, ast, arena))
    }

    fn from_ast(scope: &'t AnyScope, ast: Self::Input, arena: Self::Arena) -> Result<Self, ()> {
        // TODO: create a new scope for the package
        let decls = ast.decls
            .iter()
            .flat_map(|decl| -> Option<Box<Node>> {
                match *decl {
                    ast::DeclItem::PkgDecl(ref decl) => {
                        Some(Box::new(Package2::alloc_slot(scope, decl, arena).ok()?))
                    }
                    ast::DeclItem::TypeDecl(ref decl) => {
                        Some(Box::new(TypeDecl2::alloc_slot(scope, decl, arena).ok()?))
                    }
                    _ => None,
                }
            })
            .collect::<Vec<_>>();
        Ok(Package2 {
            id: NodeId::alloc(),
            span: ast.span,
            name: ast.name,
            scope: scope,
            decls: decls,
        })
    }
}

impl<'t> Node for Package2<'t> {
    fn span(&self) -> Span {
        self.span
    }
}

pub struct TypeDecl2 {
    id: NodeId,
    span: Span,
    name: Spanned<Name>,
}

impl<'t> FromAst<'t> for TypeDecl2 {
    type Input = &'t ast::TypeDecl;
    type Arena = Context<'t>;

    fn alloc_slot(
        scope: &'t AnyScope,
        ast: Self::Input,
        arena: Self::Arena,
    ) -> Result<Slot<'t, Self>, ()> {
        // TODO: register the type name in the scope
        Ok(Slot::new(scope, ast, arena))
    }

    fn from_ast(_scope: &'t AnyScope, ast: Self::Input, _arena: Self::Arena) -> Result<Self, ()> {
        Ok(TypeDecl2 {
            id: NodeId::alloc(),
            span: ast.span,
            name: ast.name,
        })
    }
}

impl Node for TypeDecl2 {
    fn span(&self) -> Span {
        self.span
    }
}

pub trait AnyScope {}

pub trait Node {
    /// The source file location of this node.
    fn span(&self) -> Span;
}

/// Construct something from an AST node.
pub trait FromAst<'t>: Sized {
    type Input;
    type Arena;

    fn alloc_slot(
        scope: &'t AnyScope,
        ast: Self::Input,
        arena: Self::Arena,
    ) -> Result<Slot<'t, Self>, ()>;

    fn from_ast(scope: &'t AnyScope, ast: Self::Input, arena: Self::Arena) -> Result<Self, ()>;
}

#[derive(Copy, Clone)]
pub struct Context<'t> {
    arenas: &'t Arenas<'t>,
}

impl<'t, T> AllocInto<'t, T> for Context<'t>
where
    Arenas<'t>: Alloc<T>,
{
    fn alloc(&self, value: T) -> &'t mut T {
        self.arenas.alloc(value)
    }
}
<|endoftext|>


<commit_before>extern crate bincode;
extern crate proto;
extern crate mio;

use std::io::{Read, ErrorKind};
use std::time::Instant;
use std::collections::HashMap;

use mio::net::{TcpListener, TcpStream};
use mio::{Poll, Token, Ready, PollOpt, Events};

const LISTENER: Token = Token(0);

struct ClientSock {
    stream: TcpStream,
    data_buf: Vec<u8>,
}

impl ClientSock {
    fn new(stream: TcpStream) -> Self {
        Self {
            stream,
            data_buf: Vec::with_capacity(1024),
        }
    }

    fn read_socket(&mut self) {
        let _ = self.stream.read_to_end(&mut self.data_buf);
    }
}

fn main() {
    let mut clients = HashMap::new();
    let mut user_list = vec![
        proto::User {
            name: "Lisoph".to_owned(),
            status: proto::UserStatus::Avail,
            in_project: Some("Archon".to_owned()),
        },
        proto::User {
            name: "Irockus".to_owned(),
            status: proto::UserStatus::Away,
            in_project: None,
        },
    ];

    let addr = "0.0.0.0:4450".parse().unwrap();
    let listener = TcpListener::bind(&addr).expect("TCP listen");
    let poll = Poll::new().expect("Poll");
    poll.register(&listener, LISTENER, Ready::readable(), PollOpt::edge()).expect("Listener register");

    let mut timer = Instant::now();
    let mut user_count = 0usize;
    let mut cur_client_id = 1usize;

    let mut events = Events::with_capacity(1024);
    loop {
        poll.poll(&mut events, None).unwrap();
        for e in events.iter() {
            match e.token() {
                LISTENER => {
                    let (client_stream, client_addr) = listener.accept().expect("Client accept");
                    println!("New client: {:?}", client_addr);
                    let client = ClientSock::new(client_stream);
                    clients.insert(cur_client_id, client);
                    poll.register(&clients[&cur_client_id].stream, Token(cur_client_id), Ready::readable(), PollOpt::edge()).expect("Client register");
                    cur_client_id += 1;
                },
                Token(client_id) => {
                    let mut client = &mut clients.get_mut(&client_id).unwrap();
                    client.read_socket();

                    let cmd: bincode::Result<proto::Command> = bincode::deserialize_from(client.data_buf.as_slice());
                    if let Ok(cmd) = cmd {
                        let resp = build_response(cmd, &mut user_list);
                        if bincode::serialize_into(&mut client.stream, &resp).is_err() {
                            println!("Failed to write response!");
                        }
                        client.data_buf.clear();
                    }
                }
            }
        }
    }

    /*
    loop {
        if let Ok((client, _)) = listener.accept() {
            let client_addr = client
                .peer_addr()
                .or_else(|_| client.local_addr())
                .expect("New client IP");
            println!("New connection: {}", client_addr);
            clients.push(client);
        }

        if timer.elapsed().as_secs() >= 15 {
            user_list.push(proto::User {
                name: format!("Extra User #{:02}", user_count + 1),
                status: proto::UserStatus::Avail,
                in_project: None,
            });
            user_count += 1;
            timer = Instant::now();

            for c in clients.iter_mut() {
                if bincode::serialize_into(c, &proto::Response::UserList(user_list.clone()))
                    .is_err()
                {
                    println!("Failed to write response!");
                }
            }
        }

        clients.retain(|client| {
            let client: &mut TcpStream = unsafe { &mut *(client as *const _ as *mut _) };
            let mut buf = [0u8];
            let is_disconnected = client.peek(&mut buf).ok() == Some(0);
            if is_disconnected {
                println!("Client disconnected");
                return false;
            }

            let cmd: bincode::Result<proto::Command> =
                bincode::deserialize_from(client as &mut std::io::Read);
            match cmd {
                Ok(cmd) => {
                    let resp = build_response(cmd, &mut user_list);
                    if bincode::serialize_into(client, &resp).is_err() {
                        println!("Failed to write response!");
                    }
                    true
                }
                Err(b) => {
                    if let bincode::ErrorKind::Io(e) = *b {
                        match e.kind() {
                            ErrorKind::WouldBlock => true,
                            ErrorKind::UnexpectedEof => true,
                            ErrorKind::ConnectionAborted => {
                                println!("Client disconnected");
                                false
                            }
                            _ => {
                                println!("Unhandled error: {:?}", e);
                                false
                            }
                        }
                    } else {
                        println!("{:?}", *b);
                        true
                    }
                }
            }
        });

        std::thread::sleep(std::time::Duration::from_millis(50));
    }
    */
}

fn build_response(cmd: proto::Command, user_list: &mut Vec<proto::User>) -> proto::Response {
    use proto::Command::*;
    match cmd {
        ListUsers => proto::Response::UserList(user_list.clone()),
        Login { username, password, } => {
            let password_hex = {
                use std::fmt::Write;
                let mut buf = String::with_capacity(password.len() * 2);
                for b in password.iter() {
                    let _ = write!(&mut buf, "{:X}", b);
                }
                buf
            };
            println!("Login with username '{}' and password '{}'", username, password_hex);
            if password == [0xc0u8, 0x06, 0x7d, 0x4a, 0xf4, 0xe8, 0x7f, 0x00, 0xdb, 0xac, 0x63, 0xb6, 0x15, 0x68, 0x28, 0x23, 0x70, 0x59, 0x17, 0x2d, 0x1b, 0xbe, 0xac, 0x67, 0x42, 0x73, 0x45, 0xd6, 0xa9, 0xfd, 0xa4, 0x84] {
                user_list.push(proto::User {
                    name: username,
                    status: proto::UserStatus::Avail,
                    in_project: None,
                });
                proto::Response::LoginOk
            } else {
                proto::Response::LoginInvalid
            }
        },
    }
}
<commit_msg>Proper socket data buffering<commit_after>extern crate bincode;
extern crate proto;
extern crate mio;

use std::io::{Read, ErrorKind};
use std::time::Instant;
use std::collections::HashMap;

use mio::net::{TcpListener, TcpStream};
use mio::{Poll, Token, Ready, PollOpt, Events};

const LISTENER: Token = Token(0);

struct ClientSock {
    stream: TcpStream,
    data_buf: Vec<u8>,
}

impl ClientSock {
    fn new(stream: TcpStream) -> Self {
        Self {
            stream,
            data_buf: Vec::with_capacity(1024),
        }
    }

    fn read_socket(&mut self) {
        let _ = self.stream.read_to_end(&mut self.data_buf);
    }

    fn try_deserialize(&mut self) -> Option<proto::Command> {
        let (cmd, bytes_read) = {
            let mut bytes = self.data_buf.as_slice();
            let cmd = bincode::deserialize_from(&mut bytes);
            let bytes_read = bytes.as_ptr() as usize - self.data_buf.as_slice().as_ptr() as usize;
            (cmd, bytes_read)
        };
        println!("try_deserialize: {} bytes", bytes_read);
        match cmd {
            Ok(cmd) => {
                self.data_buf.drain(0..bytes_read);
                Some(cmd)
            }
            Err(e) => {
                match *e {
                    bincode::ErrorKind::Io(..) => {} // Not enough data received yet
                    _ => { self.data_buf.drain(0..bytes_read); },
                };
                None
            }
        }
    }
}

fn main() {
    let mut clients = HashMap::new();
    let mut user_list = vec![
        proto::User {
            name: "Lisoph".to_owned(),
            status: proto::UserStatus::Avail,
            in_project: Some("Archon".to_owned()),
        },
        proto::User {
            name: "Irockus".to_owned(),
            status: proto::UserStatus::Away,
            in_project: None,
        },
    ];

    let addr = "0.0.0.0:4450".parse().unwrap();
    let listener = TcpListener::bind(&addr).expect("TCP listen");
    let poll = Poll::new().expect("Poll");
    poll.register(&listener, LISTENER, Ready::readable(), PollOpt::edge()).expect("Listener register");

    let mut timer = Instant::now();
    let mut user_count = 0usize;
    let mut cur_client_id = 1usize;

    let mut events = Events::with_capacity(1024);
    loop {
        poll.poll(&mut events, None).unwrap();
        for e in events.iter() {
            match e.token() {
                LISTENER => {
                    let (client_stream, client_addr) = listener.accept().expect("Client accept");
                    println!("New client: {:?}", client_addr);
                    let client = ClientSock::new(client_stream);
                    clients.insert(cur_client_id, client);
                    poll.register(&clients[&cur_client_id].stream, Token(cur_client_id), Ready::readable(), PollOpt::edge()).expect("Client register");
                    cur_client_id += 1;
                },
                Token(client_id) => {
                    let mut client = &mut clients.get_mut(&client_id).unwrap();
                    client.read_socket();
                    if let Some(cmd) = client.try_deserialize() {
                        let resp = build_response(cmd, &mut user_list);
                        if bincode::serialize_into(&mut client.stream, &resp).is_err() {
                            println!("Failed to write response!");
                        }
                    }
                }
            }
        }
    }

    /*
    loop {
        if let Ok((client, _)) = listener.accept() {
            let client_addr = client
                .peer_addr()
                .or_else(|_| client.local_addr())
                .expect("New client IP");
            println!("New connection: {}", client_addr);
            clients.push(client);
        }

        if timer.elapsed().as_secs() >= 15 {
            user_list.push(proto::User {
                name: format!("Extra User #{:02}", user_count + 1),
                status: proto::UserStatus::Avail,
                in_project: None,
            });
            user_count += 1;
            timer = Instant::now();

            for c in clients.iter_mut() {
                if bincode::serialize_into(c, &proto::Response::UserList(user_list.clone()))
                    .is_err()
                {
                    println!("Failed to write response!");
                }
            }
        }

        clients.retain(|client| {
            let client: &mut TcpStream = unsafe { &mut *(client as *const _ as *mut _) };
            let mut buf = [0u8];
            let is_disconnected = client.peek(&mut buf).ok() == Some(0);
            if is_disconnected {
                println!("Client disconnected");
                return false;
            }

            let cmd: bincode::Result<proto::Command> =
                bincode::deserialize_from(client as &mut std::io::Read);
            match cmd {
                Ok(cmd) => {
                    let resp = build_response(cmd, &mut user_list);
                    if bincode::serialize_into(client, &resp).is_err() {
                        println!("Failed to write response!");
                    }
                    true
                }
                Err(b) => {
                    if let bincode::ErrorKind::Io(e) = *b {
                        match e.kind() {
                            ErrorKind::WouldBlock => true,
                            ErrorKind::UnexpectedEof => true,
                            ErrorKind::ConnectionAborted => {
                                println!("Client disconnected");
                                false
                            }
                            _ => {
                                println!("Unhandled error: {:?}", e);
                                false
                            }
                        }
                    } else {
                        println!("{:?}", *b);
                        true
                    }
                }
            }
        });

        std::thread::sleep(std::time::Duration::from_millis(50));
    }
    */
}

fn build_response(cmd: proto::Command, user_list: &mut Vec<proto::User>) -> proto::Response {
    use proto::Command::*;
    match cmd {
        ListUsers => proto::Response::UserList(user_list.clone()),
        Login { username, password, } => {
            let password_hex = {
                use std::fmt::Write;
                let mut buf = String::with_capacity(password.len() * 2);
                for b in password.iter() {
                    let _ = write!(&mut buf, "{:X}", b);
                }
                buf
            };
            println!("Login with username '{}' and password '{}'", username, password_hex);
            if password == [0xc0u8, 0x06, 0x7d, 0x4a, 0xf4, 0xe8, 0x7f, 0x00, 0xdb, 0xac, 0x63, 0xb6, 0x15, 0x68, 0x28, 0x23, 0x70, 0x59, 0x17, 0x2d, 0x1b, 0xbe, 0xac, 0x67, 0x42, 0x73, 0x45, 0xd6, 0xa9, 0xfd, 0xa4, 0x84] {
                user_list.push(proto::User {
                    name: username,
                    status: proto::UserStatus::Avail,
                    in_project: None,
                });
                proto::Response::LoginOk
            } else {
                proto::Response::LoginInvalid
            }
        },
    }
}
<|endoftext|>


<commit_before>use std::io::{self, BufRead, BufReader, BufWriter, Write};

use serde_json::Value;

use crate::protocol::{
    read_message, write_error_response, write_notification, write_success_response,
    write_success_result, ErrorCodes, Message, NotificationMessage, RequestMessage, ResponseError,
};

use crate::{Error, Result};

#[derive(PartialEq)]
enum State {
    Initialized,
    ShuttingDown,
}

struct ServerContext {
    state: State,
    // True when the previous text document has errors.
    has_error: bool,
    // Set when `exit` notification is received.
    exit_code: Option<i32>,
}

impl ServerContext {
    fn new() -> ServerContext {
        ServerContext {
            state: State::Initialized,
            has_error: false,
            exit_code: None,
        }
    }
}

fn create_server_capabilities() -> lsp_types::ServerCapabilities {
    let options = lsp_types::TextDocumentSyncOptions {
        open_close: Some(true),
        change: Some(lsp_types::TextDocumentSyncKind::Full),
        will_save: None,
        will_save_wait_until: None,
        save: None,
    };

    let text_document_sync = lsp_types::TextDocumentSyncCapability::Options(options);

    lsp_types::ServerCapabilities {
        text_document_sync: Some(text_document_sync),
        hover_provider: None,
        completion_provider: None,
        signature_help_provider: None,
        definition_provider: None,
        type_definition_provider: None,
        implementation_provider: None,
        references_provider: None,
        document_highlight_provider: None,
        document_symbol_provider: None,
        workspace_symbol_provider: None,
        code_action_provider: None,
        code_lens_provider: None,
        document_formatting_provider: None,
        document_range_formatting_provider: None,
        document_on_type_formatting_provider: None,
        rename_provider: None,
        color_provider: None,
        folding_range_provider: None,
        execute_command_provider: None,
        workspace: None,
    }
}

// Requests

fn handle_request(
    writer: &mut impl Write,
    ctx: &mut ServerContext,
    msg: RequestMessage,
) -> Result<()> {
    let id = msg.id;
    let method = msg.method.as_str();

    let res = match method {
        "initialize" => initialize_request(),
        "shutdown" => shutdown_request(ctx),
        _ => unimplemented!(),
    };
    match res {
        Ok(res) => write_success_response(writer, id, res)?,
        Err(error) => write_error_response(writer, id, error)?,
    }
    Ok(())
}

type MessageResult<T> = std::result::Result<T, ResponseError>;

fn initialize_request() -> MessageResult<Value> {
    // The server has been initialized already.
    let error_message = "Unexpected initialize message".to_owned();
    Err(ResponseError::new(
        ErrorCodes::ServerNotInitialized,
        error_message,
    ))
}

fn shutdown_request(ctx: &mut ServerContext) -> MessageResult<Value> {
    ctx.state = State::ShuttingDown;
    Ok(Value::Null)
}

// Notifications

fn get_params<P: serde::de::DeserializeOwned>(params: Value) -> Result<P> {
    serde_json::from_value::<P>(params).map_err(|err| Error::ProtocolError(err.to_string()))
}

fn do_nothing() -> Result<()> {
    Ok(())
}

fn handle_notification(
    writer: &mut impl Write,
    ctx: &mut ServerContext,
    msg: NotificationMessage,
) -> Result<()> {
    let method = msg.method.as_str();
    eprintln!("Got notification: {}", method);

    use lsp_types::notification::*;
    match msg.method.as_str() {
        Exit::METHOD => exit_notification(ctx),
        DidOpenTextDocument::METHOD => {
            get_params(msg.params).and_then(|params| did_open_text_document(writer, ctx, params))
        }
        DidChangeTextDocument::METHOD => {
            get_params(msg.params).and_then(|params| did_change_text_document(writer, ctx, params))
        }
        // Accept following notifications but do nothing.
        DidChangeConfiguration::METHOD => do_nothing(),
        WillSaveTextDocument::METHOD => do_nothing(),
        DidSaveTextDocument::METHOD => do_nothing(),
        // Intentionally crash for unsupported notifications.
        _ => {
            panic!(format!("Notification `{}` isn't supported yet", msg.method.as_str()));
        }
    }
}

fn publish_diagnotics(
    writer: &mut impl Write,
    params: lsp_types::PublishDiagnosticsParams,
) -> Result<()> {
    // TODO: Don't use unwrap().
    let params = serde_json::to_value(&params).unwrap();
    write_notification(writer, "textDocument/publishDiagnostics", params)
}

fn _dbg_print_ast(ast: &mojom_parser::MojomFile) {
    for stmt in &ast.stmts {
        match stmt {
            mojom_parser::Statement::Interface(ref intr) => {
                let (line, col) = intr.name.start_pos().line_col();
                eprintln!("{}:{}: name: {}", line, col, intr.name.as_str());
            }
            _ => (),
        };
    }
}

fn _check_syntax(
    writer: &mut impl Write,
    ctx: &mut ServerContext,
    uri: lsp_types::Url,
    input: &str,
) -> Result<()> {
    match mojom_parser::parse(input) {
        Ok(ast) => {
            _dbg_print_ast(&ast);
            let params = lsp_types::PublishDiagnosticsParams {
                uri: uri,
                diagnostics: vec![],
            };
            publish_diagnotics(writer, params)?;
            ctx.has_error = false;
        }
        Err(err) => {
            // Convert error location.
            let (start, end) = match err.line_col {
                mojom_parser::LineColLocation::Pos((line, col)) => {
                    let start = lsp_types::Position {
                        line: line as u64 - 1,
                        character: col as u64 - 1,
                    };
                    // ???
                    let end = lsp_types::Position {
                        line: line as u64 - 1,
                        character: col as u64 - 1,
                    };
                    (start, end)
                }
                mojom_parser::LineColLocation::Span(start, end) => {
                    // `start` and `end` are tuples like (line, col).
                    let start = lsp_types::Position {
                        line: start.0 as u64 - 1,
                        character: start.1 as u64 - 1,
                    };
                    let end = lsp_types::Position {
                        line: end.0 as u64 - 1,
                        character: end.1 as u64 - 1,
                    };
                    (start, end)
                }
            };
            let range = lsp_types::Range {
                start: start,
                end: end,
            };
            let diagostic = lsp_types::Diagnostic {
                range: range,
                severity: Some(lsp_types::DiagnosticSeverity::Error),
                code: Some(lsp_types::NumberOrString::Number(1)),
                source: Some("mojomlsp".to_owned()),
                message: "Syntax error".to_owned(),
                related_information: None,
            };
            let params = lsp_types::PublishDiagnosticsParams {
                uri: uri,
                diagnostics: vec![diagostic],
            };
            publish_diagnotics(writer, params)?;
            ctx.has_error = true;
        }
    }
    Ok(())
}

fn exit_notification(ctx: &mut ServerContext) -> Result<()> {
    // https://microsoft.github.io/language-server-protocol/specification#exit
    if ctx.state == State::ShuttingDown {
        ctx.exit_code = Some(0);
    } else {
        ctx.exit_code = Some(1);
    }
    Ok(())
}

fn did_open_text_document(
    writer: &mut impl Write,
    ctx: &mut ServerContext,
    params: lsp_types::DidOpenTextDocumentParams,
) -> Result<()> {
    let uri = params.text_document.uri.clone();
    _check_syntax(writer, ctx, uri, &params.text_document.text)
}

fn did_change_text_document(
    writer: &mut impl Write,
    ctx: &mut ServerContext,
    params: lsp_types::DidChangeTextDocumentParams,
) -> Result<()> {
    let uri = params.text_document.uri.clone();
    let content = params
        .content_changes
        .iter()
        .map(|i| i.text.to_owned())
        .collect::<Vec<_>>();
    let res = content.join("");
    _check_syntax(writer, ctx, uri, &res)
}

// Initialization

fn initialize(
    reader: &mut impl BufRead,
    writer: &mut impl Write,
) -> Result<lsp_types::InitializeParams> {
    let message = read_message(reader)?;

    let (id, params) = match message {
        Message::Request(req) => req.cast::<lsp_types::request::Initialize>()?,
        _ => {
            // TODO: Gracefully handle `exit` and `shutdown` messages.
            let error_message = format!("Expected initialize message but got {:?}", message);
            return Err(Error::ProtocolError(error_message));
        }
    };

    let capabilities = create_server_capabilities();
    let res = lsp_types::InitializeResult {
        capabilities: capabilities,
    };
    write_success_result(writer, id, res)?;

    let message = read_message(reader)?;
    match message {
        Message::Notofication(notif) => notif.cast::<lsp_types::notification::Initialized>()?,
        _ => {
            let error_message = format!("Expected initialized message but got {:?}", message);
            return Err(Error::ProtocolError(error_message));
        }
    };

    Ok(params)
}

// Returns exit code.
pub fn start() -> Result<i32> {
    let mut reader = BufReader::new(io::stdin());
    let mut writer = BufWriter::new(io::stdout());

    let _params = initialize(&mut reader, &mut writer)?;

    let mut ctx = ServerContext::new();

    loop {
        eprintln!("Reading message...");
        let message = read_message(&mut reader)?;
        match message {
            Message::Request(request) => handle_request(&mut writer, &mut ctx, request)?,
            Message::Notofication(notification) => {
                handle_notification(&mut writer, &mut ctx, notification)?
            }
            _ => unimplemented!(),
        };

        if let Some(exit_code) = ctx.exit_code {
            return Ok(exit_code);
        }
    }
}
<commit_msg>server: Improve syntax error message<commit_after>use std::io::{self, BufRead, BufReader, BufWriter, Write};

use serde_json::Value;

use crate::protocol::{
    read_message, write_error_response, write_notification, write_success_response,
    write_success_result, ErrorCodes, Message, NotificationMessage, RequestMessage, ResponseError,
};

use crate::{Error, Result};

#[derive(PartialEq)]
enum State {
    Initialized,
    ShuttingDown,
}

struct ServerContext {
    state: State,
    // True when the previous text document has errors.
    has_error: bool,
    // Set when `exit` notification is received.
    exit_code: Option<i32>,
}

impl ServerContext {
    fn new() -> ServerContext {
        ServerContext {
            state: State::Initialized,
            has_error: false,
            exit_code: None,
        }
    }
}

fn create_server_capabilities() -> lsp_types::ServerCapabilities {
    let options = lsp_types::TextDocumentSyncOptions {
        open_close: Some(true),
        change: Some(lsp_types::TextDocumentSyncKind::Full),
        will_save: None,
        will_save_wait_until: None,
        save: None,
    };

    let text_document_sync = lsp_types::TextDocumentSyncCapability::Options(options);

    lsp_types::ServerCapabilities {
        text_document_sync: Some(text_document_sync),
        hover_provider: None,
        completion_provider: None,
        signature_help_provider: None,
        definition_provider: None,
        type_definition_provider: None,
        implementation_provider: None,
        references_provider: None,
        document_highlight_provider: None,
        document_symbol_provider: None,
        workspace_symbol_provider: None,
        code_action_provider: None,
        code_lens_provider: None,
        document_formatting_provider: None,
        document_range_formatting_provider: None,
        document_on_type_formatting_provider: None,
        rename_provider: None,
        color_provider: None,
        folding_range_provider: None,
        execute_command_provider: None,
        workspace: None,
    }
}

// Requests

fn handle_request(
    writer: &mut impl Write,
    ctx: &mut ServerContext,
    msg: RequestMessage,
) -> Result<()> {
    let id = msg.id;
    let method = msg.method.as_str();

    let res = match method {
        "initialize" => initialize_request(),
        "shutdown" => shutdown_request(ctx),
        _ => unimplemented!(),
    };
    match res {
        Ok(res) => write_success_response(writer, id, res)?,
        Err(error) => write_error_response(writer, id, error)?,
    }
    Ok(())
}

type MessageResult<T> = std::result::Result<T, ResponseError>;

fn initialize_request() -> MessageResult<Value> {
    // The server has been initialized already.
    let error_message = "Unexpected initialize message".to_owned();
    Err(ResponseError::new(
        ErrorCodes::ServerNotInitialized,
        error_message,
    ))
}

fn shutdown_request(ctx: &mut ServerContext) -> MessageResult<Value> {
    ctx.state = State::ShuttingDown;
    Ok(Value::Null)
}

// Notifications

fn get_params<P: serde::de::DeserializeOwned>(params: Value) -> Result<P> {
    serde_json::from_value::<P>(params).map_err(|err| Error::ProtocolError(err.to_string()))
}

fn do_nothing() -> Result<()> {
    Ok(())
}

fn handle_notification(
    writer: &mut impl Write,
    ctx: &mut ServerContext,
    msg: NotificationMessage,
) -> Result<()> {
    let method = msg.method.as_str();
    eprintln!("Got notification: {}", method);

    use lsp_types::notification::*;
    match msg.method.as_str() {
        Exit::METHOD => exit_notification(ctx),
        DidOpenTextDocument::METHOD => {
            get_params(msg.params).and_then(|params| did_open_text_document(writer, ctx, params))
        }
        DidChangeTextDocument::METHOD => {
            get_params(msg.params).and_then(|params| did_change_text_document(writer, ctx, params))
        }
        // Accept following notifications but do nothing.
        DidChangeConfiguration::METHOD => do_nothing(),
        WillSaveTextDocument::METHOD => do_nothing(),
        DidSaveTextDocument::METHOD => do_nothing(),
        // Intentionally crash for unsupported notifications.
        _ => {
            panic!(format!(
                "Notification `{}` isn't supported yet",
                msg.method.as_str()
            ));
        }
    }
}

fn publish_diagnotics(
    writer: &mut impl Write,
    params: lsp_types::PublishDiagnosticsParams,
) -> Result<()> {
    // TODO: Don't use unwrap().
    let params = serde_json::to_value(&params).unwrap();
    write_notification(writer, "textDocument/publishDiagnostics", params)
}

fn _dbg_print_ast(ast: &mojom_parser::MojomFile) {
    for stmt in &ast.stmts {
        match stmt {
            mojom_parser::Statement::Interface(ref intr) => {
                let (line, col) = intr.name.start_pos().line_col();
                eprintln!("{}:{}: name: {}", line, col, intr.name.as_str());
            }
            _ => (),
        };
    }
}

fn convert_error_position(line_col: &mojom_parser::LineColLocation) -> lsp_types::Range {
    let (start, end) = match line_col {
        mojom_parser::LineColLocation::Pos((line, col)) => {
            let start = lsp_types::Position {
                line: *line as u64 - 1,
                character: *col as u64 - 1,
            };
            // ???
            let end = lsp_types::Position {
                line: *line as u64 - 1,
                character: *col as u64 - 1,
            };
            (start, end)
        }
        mojom_parser::LineColLocation::Span(start, end) => {
            // `start` and `end` are tuples like (line, col).
            let start = lsp_types::Position {
                line: start.0 as u64 - 1,
                character: start.1 as u64 - 1,
            };
            let end = lsp_types::Position {
                line: end.0 as u64 - 1,
                character: end.1 as u64 - 1,
            };
            (start, end)
        }
    };
    lsp_types::Range {
        start: start,
        end: end,
    }
}

fn _check_syntax(
    writer: &mut impl Write,
    ctx: &mut ServerContext,
    uri: lsp_types::Url,
    input: &str,
) -> Result<()> {
    match mojom_parser::parse(input) {
        Ok(ast) => {
            _dbg_print_ast(&ast);
            let params = lsp_types::PublishDiagnosticsParams {
                uri: uri,
                diagnostics: vec![],
            };
            publish_diagnotics(writer, params)?;
            ctx.has_error = false;
        }
        Err(err) => {
            let range = convert_error_position(&err.line_col);
            let diagnostic = lsp_types::Diagnostic {
                range: range,
                severity: Some(lsp_types::DiagnosticSeverity::Error),
                code: Some(lsp_types::NumberOrString::String("mojom".to_owned())),
                source: Some("mojom-lsp".to_owned()),
                message: err.to_string(),
                related_information: None,
            };
            let params = lsp_types::PublishDiagnosticsParams {
                uri: uri,
                diagnostics: vec![diagnostic],
            };
            publish_diagnotics(writer, params)?;
            ctx.has_error = true;
        }
    }
    Ok(())
}

fn exit_notification(ctx: &mut ServerContext) -> Result<()> {
    // https://microsoft.github.io/language-server-protocol/specification#exit
    if ctx.state == State::ShuttingDown {
        ctx.exit_code = Some(0);
    } else {
        ctx.exit_code = Some(1);
    }
    Ok(())
}

fn did_open_text_document(
    writer: &mut impl Write,
    ctx: &mut ServerContext,
    params: lsp_types::DidOpenTextDocumentParams,
) -> Result<()> {
    let uri = params.text_document.uri.clone();
    _check_syntax(writer, ctx, uri, &params.text_document.text)
}

fn did_change_text_document(
    writer: &mut impl Write,
    ctx: &mut ServerContext,
    params: lsp_types::DidChangeTextDocumentParams,
) -> Result<()> {
    let uri = params.text_document.uri.clone();
    let content = params
        .content_changes
        .iter()
        .map(|i| i.text.to_owned())
        .collect::<Vec<_>>();
    let res = content.join("");
    _check_syntax(writer, ctx, uri, &res)
}

// Initialization

fn initialize(
    reader: &mut impl BufRead,
    writer: &mut impl Write,
) -> Result<lsp_types::InitializeParams> {
    let message = read_message(reader)?;

    let (id, params) = match message {
        Message::Request(req) => req.cast::<lsp_types::request::Initialize>()?,
        _ => {
            // TODO: Gracefully handle `exit` and `shutdown` messages.
            let error_message = format!("Expected initialize message but got {:?}", message);
            return Err(Error::ProtocolError(error_message));
        }
    };

    let capabilities = create_server_capabilities();
    let res = lsp_types::InitializeResult {
        capabilities: capabilities,
    };
    write_success_result(writer, id, res)?;

    let message = read_message(reader)?;
    match message {
        Message::Notofication(notif) => notif.cast::<lsp_types::notification::Initialized>()?,
        _ => {
            let error_message = format!("Expected initialized message but got {:?}", message);
            return Err(Error::ProtocolError(error_message));
        }
    };

    Ok(params)
}

// Returns exit code.
pub fn start() -> Result<i32> {
    let mut reader = BufReader::new(io::stdin());
    let mut writer = BufWriter::new(io::stdout());

    let _params = initialize(&mut reader, &mut writer)?;

    let mut ctx = ServerContext::new();

    loop {
        eprintln!("Reading message...");
        let message = read_message(&mut reader)?;
        match message {
            Message::Request(request) => handle_request(&mut writer, &mut ctx, request)?,
            Message::Notofication(notification) => {
                handle_notification(&mut writer, &mut ctx, notification)?
            }
            _ => unimplemented!(),
        };

        if let Some(exit_code) = ctx.exit_code {
            return Ok(exit_code);
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

pub mod decoders;
pub mod utils;

#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub struct Resolution {
    pub width: u32,
    pub height: u32,
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum DecodedFormat {
    NV12,
    I420,
}

/// Copies `src` into `dst` as NV12, removing any extra padding.
pub fn nv12_copy(
    src: &[u8],
    mut dst: &mut [u8],
    width: u32,
    height: u32,
    strides: [u32; 3],
    offsets: [u32; 3],
) {
    let width = width.try_into().unwrap();
    let height = height.try_into().unwrap();
    let data = src;

    let mut src = &data[offsets[0] as usize..];

    // Copy luma
    for _ in 0..height {
        dst[..width].copy_from_slice(&src[..width]);
        dst = &mut dst[width..];
        src = &src[strides[0] as usize..];
    }

    // Advance to the offset of the chroma plane
    let mut src = &data[offsets[1] as usize..];

    // Copy chroma
    for _ in 0..height / 2 {
        dst[..width].copy_from_slice(&src[..width]);
        dst = &mut dst[width..];
        src = &src[strides[1_usize] as usize..];
    }
}

/// Copies `src` into `dst` as I420, removing any extra padding.
pub fn i420_copy(
    src: &[u8],
    mut dst: &mut [u8],
    width: u32,
    height: u32,
    strides: [u32; 3],
    offsets: [u32; 3],
) {
    let width = width.try_into().unwrap();
    let height = height.try_into().unwrap();
    let data = src;

    let mut src = &data[offsets[0] as usize..];

    // Copy luma
    for _ in 0..height {
        dst[..width].copy_from_slice(&src[..width]);
        dst = &mut dst[width..];
        src = &src[strides[0] as usize..];
    }

    // Advance to the offset of the U plane
    let mut src = &data[offsets[1] as usize..];

    // Copy U
    for _ in 0..height / 2 {
        dst[..width].copy_from_slice(&src[..width]);
        dst = &mut dst[width..];
        src = &src[strides[1_usize] as usize..];
    }

    // Advance to the offset of the V plane
    let mut src = &data[offsets[2] as usize..];

    // Copy V
    for _ in 0..height / 2 {
        dst[..width].copy_from_slice(&src[..width]);
        dst = &mut dst[width..];
        src = &src[strides[2_usize] as usize..];
    }
}
<commit_msg>media: cros-codecs: remove unneeded type suffixes<commit_after>// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

pub mod decoders;
pub mod utils;

#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub struct Resolution {
    pub width: u32,
    pub height: u32,
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum DecodedFormat {
    NV12,
    I420,
}

/// Copies `src` into `dst` as NV12, removing any extra padding.
pub fn nv12_copy(
    src: &[u8],
    mut dst: &mut [u8],
    width: u32,
    height: u32,
    strides: [u32; 3],
    offsets: [u32; 3],
) {
    let width = width.try_into().unwrap();
    let height = height.try_into().unwrap();
    let data = src;

    let mut src = &data[offsets[0] as usize..];

    // Copy luma
    for _ in 0..height {
        dst[..width].copy_from_slice(&src[..width]);
        dst = &mut dst[width..];
        src = &src[strides[0] as usize..];
    }

    // Advance to the offset of the chroma plane
    let mut src = &data[offsets[1] as usize..];

    // Copy chroma
    for _ in 0..height / 2 {
        dst[..width].copy_from_slice(&src[..width]);
        dst = &mut dst[width..];
        src = &src[strides[1] as usize..];
    }
}

/// Copies `src` into `dst` as I420, removing any extra padding.
pub fn i420_copy(
    src: &[u8],
    mut dst: &mut [u8],
    width: u32,
    height: u32,
    strides: [u32; 3],
    offsets: [u32; 3],
) {
    let width = width.try_into().unwrap();
    let height = height.try_into().unwrap();
    let data = src;

    let mut src = &data[offsets[0] as usize..];

    // Copy luma
    for _ in 0..height {
        dst[..width].copy_from_slice(&src[..width]);
        dst = &mut dst[width..];
        src = &src[strides[0] as usize..];
    }

    // Advance to the offset of the U plane
    let mut src = &data[offsets[1] as usize..];

    // Copy U
    for _ in 0..height / 2 {
        dst[..width].copy_from_slice(&src[..width]);
        dst = &mut dst[width..];
        src = &src[strides[1] as usize..];
    }

    // Advance to the offset of the V plane
    let mut src = &data[offsets[2] as usize..];

    // Copy V
    for _ in 0..height / 2 {
        dst[..width].copy_from_slice(&src[..width]);
        dst = &mut dst[width..];
        src = &src[strides[2] as usize..];
    }
}
<|endoftext|>


<commit_before>//! Implements a command for uploading proguard mapping files.
use std::fs;
use std::io::Write;
use std::path::PathBuf;

use prelude::*;
use utils::{ArgExt, TempFile, copy_with_progress, make_byte_progress_bar,
            get_sha1_checksum};
use config::Config;
use api::{Api, AssociateDsyms};

use clap::{App, Arg, ArgMatches};
use uuid::Uuid;
use zip;
use proguard::MappingView;
use console::style;

#[derive(Debug)]
struct MappingRef {
    pub path: PathBuf,
    pub size: u64,
    pub uuid: Uuid,
}

pub fn make_app<'a, 'b: 'a>(app: App<'a, 'b>) -> App<'a, 'b> {
    app.about("uploads proguard mapping files to a project")
        .org_project_args()
        .arg(Arg::with_name("paths")
            .value_name("PATH")
            .help("The path to the mapping files")
            .multiple(true)
            .number_of_values(1)
            .index(1))
        .arg(Arg::with_name("version")
             .long("version")
             .value_name("VERSION")
             .requires("app_id")
             .help("Optionally associate the mapping files with a humand \
                    readable version.  This helps you understand which \
                    proguard files go with which version of your app."))
        .arg(Arg::with_name("version_code")
             .long("version-code")
             .value_name("VERSION_CODE")
             .requires("app_id")
             .requires("version")
             .help("Optionally associate the mapping files with a version \
                    code.  This helps you understand which proguard files \
                    go with which version of your app."))
        .arg(Arg::with_name("app_id")
             .long("app-id")
             .value_name("APP_ID")
             .requires("version")
             .help("Optionally associate the mapping files with an application \
                    ID.  If you have multiple apps in one sentry project you can \
                    then easlier tell them apart."))
        .arg(Arg::with_name("no_reprocessing")
             .long("no-reprocessing")
             .help("Does not trigger reprocessing after upload"))
}

pub fn execute<'a>(matches: &ArgMatches<'a>, config: &Config) -> Result<()> {
    let (org, project) = config.get_org_and_project(matches)?;
    let api = Api::new(config);

    let paths: Vec<_> = match matches.values_of("paths") {
        Some(paths) => paths.collect(),
        None => { return Ok(()); }
    };
    let mut mappings = vec![];
    let mut all_checksums = vec![];

    for path in &paths {
        let md = fs::metadata(path)?;
        let mapping = MappingView::from_path(path)?;
        if !mapping.has_line_info() {
            println_stderr!("warning: proguard mapping '{}' was ignored because it \
                             does not contain any line information.", path);
        } else {
            let mut f = fs::File::open(path)?;
            all_checksums.push(get_sha1_checksum(&mut f)?);
            mappings.push(MappingRef {
                path: PathBuf::from(path),
                size: md.len(),
                uuid: mapping.uuid(),
            });
        }
    }

    if mappings.is_empty() {
        fail!("found no mapping files to upload");
    }

    println!("{} compressing mappings", style("[1/2]").dim());
    let tf = TempFile::new()?;

    // add a scope here so we will flush before uploading
    {
        let mut zip = zip::ZipWriter::new(tf.open());
        for mapping in &mappings {
            let pb = make_byte_progress_bar(mapping.size);
            zip.start_file(format!("proguard/{}.txt", mapping.uuid),
                           zip::write::FileOptions::default())?;
            copy_with_progress(&pb, &mut fs::File::open(&mapping.path)?, &mut zip)?;
            pb.finish_and_clear();
        }
    }

    println!("{} uploading mappings", style("[2/2]").dim());
    api.upload_dsyms(&org, &project, tf.path())?;

    println!("Uploaded a total of {} mapping files",
             style(mappings.len()).yellow());

    // if values are given associate
    if let Some(app_id) = matches.value_of("app_id") {
        api.associate_dsyms(&org, &project, &AssociateDsyms {
            // android?
            platform: "android".to_string(),
            checksums: all_checksums,
            name: app_id.to_string(),
            app_id: app_id.to_string(),
            version: matches.value_of("version").unwrap().to_string(),
            build: matches.value_of("version_code").map(|x| x.to_string()),
        })?;
    }

    // If wanted trigger reprocessing
    if !matches.is_present("no_reprocessing") {
        if !api.trigger_reprocessing(&org, &project)? {
            println!("Server does not support reprocessing. Not triggering.");
        }
    } else {
        println!("Skipped reprocessing.");
    }

    Ok(())
}
<commit_msg>Added comment why we do not check for missing files<commit_after>//! Implements a command for uploading proguard mapping files.
use std::fs;
use std::io::Write;
use std::path::PathBuf;

use prelude::*;
use utils::{ArgExt, TempFile, copy_with_progress, make_byte_progress_bar,
            get_sha1_checksum};
use config::Config;
use api::{Api, AssociateDsyms};

use clap::{App, Arg, ArgMatches};
use uuid::Uuid;
use zip;
use proguard::MappingView;
use console::style;

#[derive(Debug)]
struct MappingRef {
    pub path: PathBuf,
    pub size: u64,
    pub uuid: Uuid,
}

pub fn make_app<'a, 'b: 'a>(app: App<'a, 'b>) -> App<'a, 'b> {
    app.about("uploads proguard mapping files to a project")
        .org_project_args()
        .arg(Arg::with_name("paths")
            .value_name("PATH")
            .help("The path to the mapping files")
            .multiple(true)
            .number_of_values(1)
            .index(1))
        .arg(Arg::with_name("version")
             .long("version")
             .value_name("VERSION")
             .requires("app_id")
             .help("Optionally associate the mapping files with a humand \
                    readable version.  This helps you understand which \
                    proguard files go with which version of your app."))
        .arg(Arg::with_name("version_code")
             .long("version-code")
             .value_name("VERSION_CODE")
             .requires("app_id")
             .requires("version")
             .help("Optionally associate the mapping files with a version \
                    code.  This helps you understand which proguard files \
                    go with which version of your app."))
        .arg(Arg::with_name("app_id")
             .long("app-id")
             .value_name("APP_ID")
             .requires("version")
             .help("Optionally associate the mapping files with an application \
                    ID.  If you have multiple apps in one sentry project you can \
                    then easlier tell them apart."))
        .arg(Arg::with_name("no_reprocessing")
             .long("no-reprocessing")
             .help("Does not trigger reprocessing after upload"))
}

pub fn execute<'a>(matches: &ArgMatches<'a>, config: &Config) -> Result<()> {
    let (org, project) = config.get_org_and_project(matches)?;
    let api = Api::new(config);

    let paths: Vec<_> = match matches.values_of("paths") {
        Some(paths) => paths.collect(),
        None => { return Ok(()); }
    };
    let mut mappings = vec![];
    let mut all_checksums = vec![];

    // since the mappings are quite small we don't bother doing a second http
    // request to figure out if any of the checksums are missing.  We just ship
    // them all up.
    for path in &paths {
        let md = fs::metadata(path)?;
        let mapping = MappingView::from_path(path)?;
        if !mapping.has_line_info() {
            println_stderr!("warning: proguard mapping '{}' was ignored because it \
                             does not contain any line information.", path);
        } else {
            let mut f = fs::File::open(path)?;
            all_checksums.push(get_sha1_checksum(&mut f)?);
            mappings.push(MappingRef {
                path: PathBuf::from(path),
                size: md.len(),
                uuid: mapping.uuid(),
            });
        }
    }

    if mappings.is_empty() {
        fail!("found no mapping files to upload");
    }

    println!("{} compressing mappings", style("[1/2]").dim());
    let tf = TempFile::new()?;

    // add a scope here so we will flush before uploading
    {
        let mut zip = zip::ZipWriter::new(tf.open());
        for mapping in &mappings {
            let pb = make_byte_progress_bar(mapping.size);
            zip.start_file(format!("proguard/{}.txt", mapping.uuid),
                           zip::write::FileOptions::default())?;
            copy_with_progress(&pb, &mut fs::File::open(&mapping.path)?, &mut zip)?;
            pb.finish_and_clear();
        }
    }

    println!("{} uploading mappings", style("[2/2]").dim());
    api.upload_dsyms(&org, &project, tf.path())?;

    println!("Uploaded a total of {} mapping files",
             style(mappings.len()).yellow());

    // if values are given associate
    if let Some(app_id) = matches.value_of("app_id") {
        api.associate_dsyms(&org, &project, &AssociateDsyms {
            // android?
            platform: "android".to_string(),
            checksums: all_checksums,
            name: app_id.to_string(),
            app_id: app_id.to_string(),
            version: matches.value_of("version").unwrap().to_string(),
            build: matches.value_of("version_code").map(|x| x.to_string()),
        })?;
    }

    // If wanted trigger reprocessing
    if !matches.is_present("no_reprocessing") {
        if !api.trigger_reprocessing(&org, &project)? {
            println!("Server does not support reprocessing. Not triggering.");
        }
    } else {
        println!("Skipped reprocessing.");
    }

    Ok(())
}
<|endoftext|>


<commit_before>// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// 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.

// ignore-android: FIXME(#10381)
// min-lldb-version: 310

// This test case checks if function arguments already have the correct value
// when breaking at the beginning of a function. Functions with the
// #[no_stack_check] attribute have the same prologue as regular C functions
// compiled with GCC or Clang and therefore are better handled by GDB. As a
// consequence, and as opposed to regular Rust functions, we can set the
// breakpoints via the function name (and don't have to fall back on using line
// numbers). For LLDB this shouldn't make a difference because it can handle
// both cases.

// compile-flags:-g

// === GDB TESTS ===================================================================================

// gdb-command:rbreak immediate_args
// gdb-command:rbreak binding
// gdb-command:rbreak assignment
// gdb-command:rbreak function_call
// gdb-command:rbreak identifier
// gdb-command:rbreak return_expr
// gdb-command:rbreak arithmetic_expr
// gdb-command:rbreak if_expr
// gdb-command:rbreak while_expr
// gdb-command:rbreak loop_expr
// gdb-command:run

// IMMEDIATE ARGS
// gdb-command:print a
// gdb-check:$1 = 1
// gdb-command:print b
// gdb-check:$2 = true
// gdb-command:print c
// gdb-check:$3 = 2.5
// gdb-command:continue

// NON IMMEDIATE ARGS
// gdb-command:print a
// gdb-check:$4 = {a = 3, b = 4, c = 5, d = 6, e = 7, f = 8, g = 9, h = 10}
// gdb-command:print b
// gdb-check:$5 = {a = 11, b = 12, c = 13, d = 14, e = 15, f = 16, g = 17, h = 18}
// gdb-command:continue

// BINDING
// gdb-command:print a
// gdb-check:$6 = 19
// gdb-command:print b
// gdb-check:$7 = 20
// gdb-command:print c
// gdb-check:$8 = 21.5
// gdb-command:continue

// ASSIGNMENT
// gdb-command:print a
// gdb-check:$9 = 22
// gdb-command:print b
// gdb-check:$10 = 23
// gdb-command:print c
// gdb-check:$11 = 24.5
// gdb-command:continue

// FUNCTION CALL
// gdb-command:print x
// gdb-check:$12 = 25
// gdb-command:print y
// gdb-check:$13 = 26
// gdb-command:print z
// gdb-check:$14 = 27.5
// gdb-command:continue

// EXPR
// gdb-command:print x
// gdb-check:$15 = 28
// gdb-command:print y
// gdb-check:$16 = 29
// gdb-command:print z
// gdb-check:$17 = 30.5
// gdb-command:continue

// RETURN EXPR
// gdb-command:print x
// gdb-check:$18 = 31
// gdb-command:print y
// gdb-check:$19 = 32
// gdb-command:print z
// gdb-check:$20 = 33.5
// gdb-command:continue

// ARITHMETIC EXPR
// gdb-command:print x
// gdb-check:$21 = 34
// gdb-command:print y
// gdb-check:$22 = 35
// gdb-command:print z
// gdb-check:$23 = 36.5
// gdb-command:continue

// IF EXPR
// gdb-command:print x
// gdb-check:$24 = 37
// gdb-command:print y
// gdb-check:$25 = 38
// gdb-command:print z
// gdb-check:$26 = 39.5
// gdb-command:continue

// WHILE EXPR
// gdb-command:print x
// gdb-check:$27 = 40
// gdb-command:print y
// gdb-check:$28 = 41
// gdb-command:print z
// gdb-check:$29 = 42
// gdb-command:continue

// LOOP EXPR
// gdb-command:print x
// gdb-check:$30 = 43
// gdb-command:print y
// gdb-check:$31 = 44
// gdb-command:print z
// gdb-check:$32 = 45
// gdb-command:continue

// === LLDB TESTS ==================================================================================

// lldb-command:breakpoint set --name immediate_args
// lldb-command:breakpoint set --name non_immediate_args
// lldb-command:breakpoint set --name binding
// lldb-command:breakpoint set --name assignment
// lldb-command:breakpoint set --name function_call
// lldb-command:breakpoint set --name identifier
// lldb-command:breakpoint set --name return_expr
// lldb-command:breakpoint set --name arithmetic_expr
// lldb-command:breakpoint set --name if_expr
// lldb-command:breakpoint set --name while_expr
// lldb-command:breakpoint set --name loop_expr
// lldb-command:run

// IMMEDIATE ARGS
// lldb-command:print a
// lldb-check:[...]$0 = 1
// lldb-command:print b
// lldb-check:[...]$1 = true
// lldb-command:print c
// lldb-check:[...]$2 = 2.5
// lldb-command:continue

// NON IMMEDIATE ARGS
// lldb-command:print a
// lldb-check:[...]$3 = BigStruct { a: 3, b: 4, c: 5, d: 6, e: 7, f: 8, g: 9, h: 10 }
// lldb-command:print b
// lldb-check:[...]$4 = BigStruct { a: 11, b: 12, c: 13, d: 14, e: 15, f: 16, g: 17, h: 18 }
// lldb-command:continue

// BINDING
// lldb-command:print a
// lldb-check:[...]$5 = 19
// lldb-command:print b
// lldb-check:[...]$6 = 20
// lldb-command:print c
// lldb-check:[...]$7 = 21.5
// lldb-command:continue

// ASSIGNMENT
// lldb-command:print a
// lldb-check:[...]$8 = 22
// lldb-command:print b
// lldb-check:[...]$9 = 23
// lldb-command:print c
// lldb-check:[...]$10 = 24.5
// lldb-command:continue

// FUNCTION CALL
// lldb-command:print x
// lldb-check:[...]$11 = 25
// lldb-command:print y
// lldb-check:[...]$12 = 26
// lldb-command:print z
// lldb-check:[...]$13 = 27.5
// lldb-command:continue

// EXPR
// lldb-command:print x
// lldb-check:[...]$14 = 28
// lldb-command:print y
// lldb-check:[...]$15 = 29
// lldb-command:print z
// lldb-check:[...]$16 = 30.5
// lldb-command:continue

// RETURN EXPR
// lldb-command:print x
// lldb-check:[...]$17 = 31
// lldb-command:print y
// lldb-check:[...]$18 = 32
// lldb-command:print z
// lldb-check:[...]$19 = 33.5
// lldb-command:continue

// ARITHMETIC EXPR
// lldb-command:print x
// lldb-check:[...]$20 = 34
// lldb-command:print y
// lldb-check:[...]$21 = 35
// lldb-command:print z
// lldb-check:[...]$22 = 36.5
// lldb-command:continue

// IF EXPR
// lldb-command:print x
// lldb-check:[...]$23 = 37
// lldb-command:print y
// lldb-check:[...]$24 = 38
// lldb-command:print z
// lldb-check:[...]$25 = 39.5
// lldb-command:continue

// WHILE EXPR
// lldb-command:print x
// lldb-check:[...]$26 = 40
// lldb-command:print y
// lldb-check:[...]$27 = 41
// lldb-command:print z
// lldb-check:[...]$28 = 42
// lldb-command:continue

// LOOP EXPR
// lldb-command:print x
// lldb-check:[...]$29 = 43
// lldb-command:print y
// lldb-check:[...]$30 = 44
// lldb-command:print z
// lldb-check:[...]$31 = 45
// lldb-command:continue

#![allow(dead_code, unused_assignments, unused_variables)]
#![feature(omit_gdb_pretty_printer_section)]
#![omit_gdb_pretty_printer_section]

#[no_stack_check]
fn immediate_args(a: isize, b: bool, c: f64) {
    println!("");
}

struct BigStruct {
    a: u64,
    b: u64,
    c: u64,
    d: u64,
    e: u64,
    f: u64,
    g: u64,
    h: u64
}

#[no_stack_check]
fn non_immediate_args(a: BigStruct, b: BigStruct) {
    println!("");
}

#[no_stack_check]
fn binding(a: i64, b: u64, c: f64) {
    let x = 0;
    println!("");
}

#[no_stack_check]
fn assignment(mut a: u64, b: u64, c: f64) {
    a = b;
    println!("");
}

#[no_stack_check]
fn function_call(x: u64, y: u64, z: f64) {
    println!("Hi!")
}

#[no_stack_check]
fn identifier(x: u64, y: u64, z: f64) -> u64 {
    x
}

#[no_stack_check]
fn return_expr(x: u64, y: u64, z: f64) -> u64 {
    return x;
}

#[no_stack_check]
fn arithmetic_expr(x: u64, y: u64, z: f64) -> u64 {
    x + y
}

#[no_stack_check]
fn if_expr(x: u64, y: u64, z: f64) -> u64 {
    if x + y < 1000 {
        x
    } else {
        y
    }
}

#[no_stack_check]
fn while_expr(mut x: u64, y: u64, z: u64) -> u64 {
    while x + y < 1000 {
        x += z
    }
    return x;
}

#[no_stack_check]
fn loop_expr(mut x: u64, y: u64, z: u64) -> u64 {
    loop {
        x += z;

        if x + y > 1000 {
            return x;
        }
    }
}

fn main() {
    immediate_args(1, true, 2.5);

    non_immediate_args(
        BigStruct {
            a: 3,
            b: 4,
            c: 5,
            d: 6,
            e: 7,
            f: 8,
            g: 9,
            h: 10
        },
        BigStruct {
            a: 11,
            b: 12,
            c: 13,
            d: 14,
            e: 15,
            f: 16,
            g: 17,
            h: 18
        }
    );

    binding(19, 20, 21.5);
    assignment(22, 23, 24.5);
    function_call(25, 26, 27.5);
    identifier(28, 29, 30.5);
    return_expr(31, 32, 33.5);
    arithmetic_expr(34, 35, 36.5);
    if_expr(37, 38, 39.5);
    while_expr(40, 41, 42);
    loop_expr(43, 44, 45);
}
<commit_msg>Rollup merge of #35028 - abhijeetbhagat:patch-6, r=alexcrichton<commit_after><|endoftext|>