<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.

use stb_image::bindgen::*;

use std::libc;
use std::libc::{c_void, c_int};
use std::ptr::{is_null, to_mut_unsafe_ptr};
use std::str::as_c_str;
use std::task;
use std::vec::raw::from_buf_raw;

pub struct Image<T> {
    width   : uint,
    height  : uint,
    depth   : uint,
    data    : ~[T],
}

pub fn new_image<T>(width: uint, height: uint, depth: uint, data: ~[T]) -> Image<T> {
    Image::<T> {
        width   : width,
        height  : height,
        depth   : depth,
        data    : data,
    }
}

enum LoadResult {
    Error,
    ImageU8(Image<u8>),
    ImageF32(Image<f32>),
}

pub fn load(path: ~str) -> LoadResult {
    let force_depth = 0;
    load_with_depth(path, force_depth, false)
}

priv fn load_internal<T>(buf : *T, w : c_int, h : c_int, d : c_int) -> Image<T> {
    unsafe {
        // FIXME: Shouldn't copy; instead we should use a sendable resource. They
        // aren't particularly safe yet though.
        let data = from_buf_raw(buf, (w * h * d) as uint);
        libc::free(buf as *c_void);
        Image::<T>{
            width   : w as uint,
            height  : h as uint,
            depth   : d as uint,
            data    : data}
    }
}

pub fn load_with_depth(path: ~str, force_depth: uint, convert_hdr:bool) -> LoadResult {
    unsafe {
        do task::unkillable {
            let mut width   = 0 as c_int;
            let mut height  = 0 as c_int;
            let mut depth   = 0 as c_int;
            as_c_str(path, |bytes| {
                if !convert_hdr && stbi_is_hdr(bytes)!=0   {
                    let buffer = stbi_loadf(bytes,
                                            to_mut_unsafe_ptr(&mut width),
                                            to_mut_unsafe_ptr(&mut height),
                                            to_mut_unsafe_ptr(&mut depth),
                                            force_depth as c_int);
                    if is_null(buffer) {
                        Error
                    } else {
                        ImageF32( load_internal(buffer,width,height,depth) )
                    }
                } else {
                    let buffer = stbi_load(bytes,
                                           to_mut_unsafe_ptr(&mut width),
                                           to_mut_unsafe_ptr(&mut height),
                                           to_mut_unsafe_ptr(&mut depth),
                                           force_depth as c_int);
                    if is_null(buffer) {
                        Error
                    } else {
                        ImageU8( load_internal(buffer,width,height,depth) )
                    }
                }
            })
        }
    }
}

pub fn load_from_memory(buffer: &[u8]) -> LoadResult {
    let force_depth = 0;
    load_from_memory_with_depth(buffer, force_depth, false)
}

pub fn load_from_memory_with_depth(buffer: &[u8], force_depth: uint, convert_hdr:bool) -> LoadResult {
    unsafe {
        do task::unkillable {
            let mut width = 0 as c_int;
            let mut height = 0 as c_int;
            let mut depth = 0 as c_int;
            do buffer.as_imm_buf |bytes, len| {
                if !convert_hdr && stbi_is_hdr_from_memory(bytes, len as c_int) != 0 {
                    let buffer = stbi_loadf_from_memory(bytes,
                                                        len as c_int,
                                                        to_mut_unsafe_ptr(&mut width),
                                                        to_mut_unsafe_ptr(&mut height),
                                                        to_mut_unsafe_ptr(&mut depth),
                                                        force_depth as c_int);
                    if is_null(buffer) {
                        Error
                    } else {
                        let actual_depth = if force_depth != 0 { force_depth as c_int } else { depth };
                        ImageF32( load_internal(buffer,width,height,actual_depth) )
                    }
                } else {
                    let buffer = stbi_load_from_memory(bytes,
                                                       len as c_int,
                                                       to_mut_unsafe_ptr(&mut width),
                                                       to_mut_unsafe_ptr(&mut height),
                                                       to_mut_unsafe_ptr(&mut depth),
                                                       force_depth as c_int);
                    if is_null(buffer) {
                        Error
                    } else {
                        let actual_depth = if force_depth != 0 { force_depth as c_int } else { depth };
                        ImageU8( load_internal(buffer,width,height,actual_depth) )
                    }
                }
            }
        }
    }
}
<commit_msg>as_c_str is now a method.<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.

use stb_image::bindgen::*;

use std::libc;
use std::libc::{c_void, c_int};
use std::ptr::{is_null, to_mut_unsafe_ptr};
use std::task;
use std::vec::raw::from_buf_raw;

pub struct Image<T> {
    width   : uint,
    height  : uint,
    depth   : uint,
    data    : ~[T],
}

pub fn new_image<T>(width: uint, height: uint, depth: uint, data: ~[T]) -> Image<T> {
    Image::<T> {
        width   : width,
        height  : height,
        depth   : depth,
        data    : data,
    }
}

enum LoadResult {
    Error,
    ImageU8(Image<u8>),
    ImageF32(Image<f32>),
}

pub fn load(path: ~str) -> LoadResult {
    let force_depth = 0;
    load_with_depth(path, force_depth, false)
}

priv fn load_internal<T>(buf : *T, w : c_int, h : c_int, d : c_int) -> Image<T> {
    unsafe {
        // FIXME: Shouldn't copy; instead we should use a sendable resource. They
        // aren't particularly safe yet though.
        let data = from_buf_raw(buf, (w * h * d) as uint);
        libc::free(buf as *c_void);
        Image::<T>{
            width   : w as uint,
            height  : h as uint,
            depth   : d as uint,
            data    : data}
    }
}

pub fn load_with_depth(path: ~str, force_depth: uint, convert_hdr:bool) -> LoadResult {
    unsafe {
        do task::unkillable {
            let mut width   = 0 as c_int;
            let mut height  = 0 as c_int;
            let mut depth   = 0 as c_int;
            do path.as_c_str |bytes|
            {
                if !convert_hdr && stbi_is_hdr(bytes)!=0   {
                    let buffer = stbi_loadf(bytes,
                                            to_mut_unsafe_ptr(&mut width),
                                            to_mut_unsafe_ptr(&mut height),
                                            to_mut_unsafe_ptr(&mut depth),
                                            force_depth as c_int);
                    if is_null(buffer) {
                        Error
                    } else {
                        ImageF32( load_internal(buffer,width,height,depth) )
                    }
                } else {
                    let buffer = stbi_load(bytes,
                                           to_mut_unsafe_ptr(&mut width),
                                           to_mut_unsafe_ptr(&mut height),
                                           to_mut_unsafe_ptr(&mut depth),
                                           force_depth as c_int);
                    if is_null(buffer) {
                        Error
                    } else {
                        ImageU8( load_internal(buffer,width,height,depth) )
                    }
                }
            }
        }
    }
}

pub fn load_from_memory(buffer: &[u8]) -> LoadResult {
    let force_depth = 0;
    load_from_memory_with_depth(buffer, force_depth, false)
}

pub fn load_from_memory_with_depth(buffer: &[u8], force_depth: uint, convert_hdr:bool) -> LoadResult {
    unsafe {
        do task::unkillable {
            let mut width = 0 as c_int;
            let mut height = 0 as c_int;
            let mut depth = 0 as c_int;
            do buffer.as_imm_buf |bytes, len| {
                if !convert_hdr && stbi_is_hdr_from_memory(bytes, len as c_int) != 0 {
                    let buffer = stbi_loadf_from_memory(bytes,
                                                        len as c_int,
                                                        to_mut_unsafe_ptr(&mut width),
                                                        to_mut_unsafe_ptr(&mut height),
                                                        to_mut_unsafe_ptr(&mut depth),
                                                        force_depth as c_int);
                    if is_null(buffer) {
                        Error
                    } else {
                        let actual_depth = if force_depth != 0 { force_depth as c_int } else { depth };
                        ImageF32( load_internal(buffer,width,height,actual_depth) )
                    }
                } else {
                    let buffer = stbi_load_from_memory(bytes,
                                                       len as c_int,
                                                       to_mut_unsafe_ptr(&mut width),
                                                       to_mut_unsafe_ptr(&mut height),
                                                       to_mut_unsafe_ptr(&mut depth),
                                                       force_depth as c_int);
                    if is_null(buffer) {
                        Error
                    } else {
                        let actual_depth = if force_depth != 0 { force_depth as c_int } else { depth };
                        ImageU8( load_internal(buffer,width,height,actual_depth) )
                    }
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::thread::sleep;
use std::time::Duration;

use anyhow::Result;
use chrono::Local;
use crossterm::style::{Attribute, Color};

use pueue::network::protocol::GenericStream;
use pueue::task::{Task, TaskResult, TaskStatus};

use crate::commands::get_state;
use crate::output_helper::style_text;

/// Wait until tasks are done.
/// Tasks can be specified by:
/// - Default queue (no parameter given)
/// - Group
/// - A list of task ids
/// - All tasks (`all == true`)
///
/// By default, this will log status changes on tasks.
/// Pass `quiet == true` to supress any logging.
pub async fn wait(
    stream: &mut GenericStream,
    task_ids: &Vec<usize>,
    group: &str,
    all: bool,
    quiet: bool,
) -> Result<()> {
    let mut first_run = true;
    // Create a list of tracked tasks.
    // This way we can track any status changes and if any new tasks are added.
    let mut watched_tasks: HashMap<usize, TaskStatus> = HashMap::new();

    loop {
        let state = get_state(stream).await?;

        let tasks: Vec<Task> = if !task_ids.is_empty() {
            // Get all tasks of a specific group
            state
                .tasks
                .iter()
                .filter(|(id, _)| task_ids.contains(id))
                .map(|(_, task)| task.clone())
                .collect()
        } else if all {
            // Get all tasks
            state.tasks.iter().map(|(_, task)| task.clone()).collect()
        } else {
            // Get all tasks of a specific group
            state
                .tasks
                .iter()
                .filter(|(_, task)| task.group.eq(group))
                .map(|(_, task)| task.clone())
                .collect()
        };

        // Get current time for log output
        let current_time = Local::now().format("%H:%M:%S").to_string();

        // Iterate over all matching tasks
        for task in tasks.iter() {
            // Check if we already know this task or if it is new.
            let previous_status = match watched_tasks.get(&task.id) {
                None => {
                    // Add any unknown tasks to our watchlist
                    // Don't log anything if this is the first run
                    if !quiet && !first_run {
                        let color = get_color_for_status(&task.status);
                        println!(
                            "{} - New task {} with status {}",
                            current_time,
                            style_text(task.id, None, Some(Attribute::Bold)),
                            style_text(&task.status, Some(color), None),
                        );
                    }
                    watched_tasks.insert(task.id, task.status.clone());

                    continue;
                }
                Some(previous_status) => {
                    if previous_status == &task.status {
                        continue;
                    }
                    previous_status.clone()
                }
            };

            // Update the (previous) task status and log any changes
            watched_tasks.insert(task.id, task.status.clone());
            if !quiet {
                log_status_change(&current_time, previous_status, &task);
            }
        }

        // We can stop waiting, if every task is on `Done`
        // Always check the actual task list instead of the watched_tasks list.
        // Otherwise we get locked if tasks get removed.
        let all_finished = tasks.iter().all(|task| task.status == TaskStatus::Done);

        if all_finished {
            break;
        }

        // Sleep for a few seconds. We don't want to hurt the CPU.
        sleep(Duration::from_millis(2000));
        first_run = false;
    }

    Ok(())
}

fn log_status_change(current_time: &str, previous_status: TaskStatus, task: &Task) {
    // Finishing tasks get some special handling
    if task.status == TaskStatus::Done {
        let text = match task.result {
            Some(TaskResult::Success) => {
                format!(
                    "Task {} succeeded with {}",
                    style_text(task.id, None, Some(Attribute::Bold)),
                    style_text("0", Some(Color::Green), None)
                )
            }
            Some(TaskResult::DependencyFailed) => {
                format!(
                    "Task {} failed due to {}",
                    style_text(task.id, None, Some(Attribute::Bold)),
                    style_text("failed dependencies", Some(Color::Red), None)
                )
            }

            Some(TaskResult::FailedToSpawn(_)) => {
                format!(
                    "Task {} {}",
                    style_text(task.id, None, Some(Attribute::Bold)),
                    style_text("failed to spawn", Some(Color::Red), None)
                )
            }
            Some(TaskResult::Failed(exit_code)) => {
                format!(
                    "Task {} failed with {}",
                    style_text(task.id, None, Some(Attribute::Bold)),
                    style_text(exit_code, Some(Color::Red), Some(Attribute::Bold))
                )
            }
            Some(TaskResult::Errored) => {
                format!(
                    "Task {} experienced an {}.",
                    style_text(task.id, None, Some(Attribute::Bold)),
                    style_text("IO error", Some(Color::Red), Some(Attribute::Bold))
                )
            }
            Some(TaskResult::Killed) => {
                format!(
                    "Task {} has been {}",
                    style_text(task.id, None, Some(Attribute::Bold)),
                    style_text("killed", Some(Color::Red), None)
                )
            }
            None => panic!("Got a 'Done' task without a task result. Please report this bug."),
        };
        println!("{} - {}", current_time, text);

        return;
    }
    let new_status_color = get_color_for_status(&task.status);
    let previous_status_color = get_color_for_status(&previous_status);

    println!(
        "{} - Task {} changed from {} to {}",
        current_time,
        style_text(task.id, None, Some(Attribute::Bold)),
        style_text(previous_status, Some(previous_status_color), None),
        style_text(&task.status, Some(new_status_color), None),
    );
}

fn get_color_for_status(task_status: &TaskStatus) -> Color {
    match task_status {
        TaskStatus::Running | TaskStatus::Done => Color::Green,
        TaskStatus::Paused | TaskStatus::Locked => Color::White,
        _ => Color::Yellow,
    }
}
<commit_msg>Fix clippy issue<commit_after>use std::collections::HashMap;
use std::thread::sleep;
use std::time::Duration;

use anyhow::Result;
use chrono::Local;
use crossterm::style::{Attribute, Color};

use pueue::network::protocol::GenericStream;
use pueue::task::{Task, TaskResult, TaskStatus};

use crate::commands::get_state;
use crate::output_helper::style_text;

/// Wait until tasks are done.
/// Tasks can be specified by:
/// - Default queue (no parameter given)
/// - Group
/// - A list of task ids
/// - All tasks (`all == true`)
///
/// By default, this will log status changes on tasks.
/// Pass `quiet == true` to supress any logging.
pub async fn wait(
    stream: &mut GenericStream,
    task_ids: &[usize],
    group: &str,
    all: bool,
    quiet: bool,
) -> Result<()> {
    let mut first_run = true;
    // Create a list of tracked tasks.
    // This way we can track any status changes and if any new tasks are added.
    let mut watched_tasks: HashMap<usize, TaskStatus> = HashMap::new();

    loop {
        let state = get_state(stream).await?;

        let tasks: Vec<Task> = if !task_ids.is_empty() {
            // Get all tasks of a specific group
            state
                .tasks
                .iter()
                .filter(|(id, _)| task_ids.contains(id))
                .map(|(_, task)| task.clone())
                .collect()
        } else if all {
            // Get all tasks
            state.tasks.iter().map(|(_, task)| task.clone()).collect()
        } else {
            // Get all tasks of a specific group
            state
                .tasks
                .iter()
                .filter(|(_, task)| task.group.eq(group))
                .map(|(_, task)| task.clone())
                .collect()
        };

        // Get current time for log output
        let current_time = Local::now().format("%H:%M:%S").to_string();

        // Iterate over all matching tasks
        for task in tasks.iter() {
            // Check if we already know this task or if it is new.
            let previous_status = match watched_tasks.get(&task.id) {
                None => {
                    // Add any unknown tasks to our watchlist
                    // Don't log anything if this is the first run
                    if !quiet && !first_run {
                        let color = get_color_for_status(&task.status);
                        println!(
                            "{} - New task {} with status {}",
                            current_time,
                            style_text(task.id, None, Some(Attribute::Bold)),
                            style_text(&task.status, Some(color), None),
                        );
                    }
                    watched_tasks.insert(task.id, task.status.clone());

                    continue;
                }
                Some(previous_status) => {
                    if previous_status == &task.status {
                        continue;
                    }
                    previous_status.clone()
                }
            };

            // Update the (previous) task status and log any changes
            watched_tasks.insert(task.id, task.status.clone());
            if !quiet {
                log_status_change(&current_time, previous_status, &task);
            }
        }

        // We can stop waiting, if every task is on `Done`
        // Always check the actual task list instead of the watched_tasks list.
        // Otherwise we get locked if tasks get removed.
        let all_finished = tasks.iter().all(|task| task.status == TaskStatus::Done);

        if all_finished {
            break;
        }

        // Sleep for a few seconds. We don't want to hurt the CPU.
        sleep(Duration::from_millis(2000));
        first_run = false;
    }

    Ok(())
}

fn log_status_change(current_time: &str, previous_status: TaskStatus, task: &Task) {
    // Finishing tasks get some special handling
    if task.status == TaskStatus::Done {
        let text = match task.result {
            Some(TaskResult::Success) => {
                format!(
                    "Task {} succeeded with {}",
                    style_text(task.id, None, Some(Attribute::Bold)),
                    style_text("0", Some(Color::Green), None)
                )
            }
            Some(TaskResult::DependencyFailed) => {
                format!(
                    "Task {} failed due to {}",
                    style_text(task.id, None, Some(Attribute::Bold)),
                    style_text("failed dependencies", Some(Color::Red), None)
                )
            }

            Some(TaskResult::FailedToSpawn(_)) => {
                format!(
                    "Task {} {}",
                    style_text(task.id, None, Some(Attribute::Bold)),
                    style_text("failed to spawn", Some(Color::Red), None)
                )
            }
            Some(TaskResult::Failed(exit_code)) => {
                format!(
                    "Task {} failed with {}",
                    style_text(task.id, None, Some(Attribute::Bold)),
                    style_text(exit_code, Some(Color::Red), Some(Attribute::Bold))
                )
            }
            Some(TaskResult::Errored) => {
                format!(
                    "Task {} experienced an {}.",
                    style_text(task.id, None, Some(Attribute::Bold)),
                    style_text("IO error", Some(Color::Red), Some(Attribute::Bold))
                )
            }
            Some(TaskResult::Killed) => {
                format!(
                    "Task {} has been {}",
                    style_text(task.id, None, Some(Attribute::Bold)),
                    style_text("killed", Some(Color::Red), None)
                )
            }
            None => panic!("Got a 'Done' task without a task result. Please report this bug."),
        };
        println!("{} - {}", current_time, text);

        return;
    }
    let new_status_color = get_color_for_status(&task.status);
    let previous_status_color = get_color_for_status(&previous_status);

    println!(
        "{} - Task {} changed from {} to {}",
        current_time,
        style_text(task.id, None, Some(Attribute::Bold)),
        style_text(previous_status, Some(previous_status_color), None),
        style_text(&task.status, Some(new_status_color), None),
    );
}

fn get_color_for_status(task_status: &TaskStatus) -> Color {
    match task_status {
        TaskStatus::Running | TaskStatus::Done => Color::Green,
        TaskStatus::Paused | TaskStatus::Locked => Color::White,
        _ => Color::Yellow,
    }
}
<|endoftext|>


<commit_before>use rustc::lint::*;
use syntax::ast;
use syntax::codemap::{Span, BytePos};
use utils::span_lint;

/// **What it does:** Checks for the presence of `_`, `::` or camel-case words
/// outside ticks in documentation.
///
/// **Why is this bad?** *Rustdoc* supports markdown formatting, `_`, `::` and
/// camel-case probably indicates some code which should be included between
/// ticks. `_` can also be used for empasis in markdown, this lint tries to
/// consider that.
///
/// **Known problems:** Lots of bad docs won’t be fixed, what the lint checks
/// for is limited, and there are still false positives.
///
/// **Examples:**
/// ```rust
/// /// Do something with the foo_bar parameter. See also that::other::module::foo.
/// // ^ `foo_bar` and `that::other::module::foo` should be ticked.
/// fn doit(foo_bar) { .. }
/// ```
declare_lint! {
    pub DOC_MARKDOWN,
    Warn,
    "presence of `_`, `::` or camel-case outside backticks in documentation"
}

#[derive(Clone)]
pub struct Doc {
    valid_idents: Vec<String>,
}

impl Doc {
    pub fn new(valid_idents: Vec<String>) -> Self {
        Doc { valid_idents: valid_idents }
    }
}

impl LintPass for Doc {
    fn get_lints(&self) -> LintArray {
        lint_array![DOC_MARKDOWN]
    }
}

impl EarlyLintPass for Doc {
    fn check_crate(&mut self, cx: &EarlyContext, krate: &ast::Crate) {
        check_attrs(cx, &self.valid_idents, &krate.attrs);
    }

    fn check_item(&mut self, cx: &EarlyContext, item: &ast::Item) {
        check_attrs(cx, &self.valid_idents, &item.attrs);
    }
}

/// Cleanup documentation decoration (`///` and such).
///
/// We can't use `syntax::attr::AttributeMethods::with_desugared_doc` or
/// `syntax::parse::lexer::comments::strip_doc_comment_decoration` because we need to keep track of
/// the span but this function is inspired from the later.
#[allow(cast_possible_truncation)]
pub fn strip_doc_comment_decoration((comment, span): (String, Span)) -> Vec<(String, Span)> {
    // one-line comments lose their prefix
    const ONELINERS: &'static [&'static str] = &["///!", "///", "//!", "//"];
    for prefix in ONELINERS {
        if comment.starts_with(*prefix) {
            return vec![(comment[prefix.len()..].to_owned(),
                         Span { lo: span.lo + BytePos(prefix.len() as u32), ..span })];
        }
    }

    if comment.starts_with("/*") {
        return comment[3..comment.len() - 2]
            .lines()
            .map(|line| {
                let offset = line.as_ptr() as usize - comment.as_ptr() as usize;
                debug_assert_eq!(offset as u32 as usize, offset);

                (line.to_owned(), Span { lo: span.lo + BytePos(offset as u32), ..span })
            })
            .collect();
    }

    panic!("not a doc-comment: {}", comment);
}

pub fn check_attrs<'a>(cx: &EarlyContext, valid_idents: &[String], attrs: &'a [ast::Attribute]) {
    let mut docs = vec![];

    for attr in attrs {
        if attr.is_sugared_doc {
            if let Some(ref doc) = attr.value_str() {
                    let doc = (*doc.as_str()).to_owned();
                    docs.extend_from_slice(&strip_doc_comment_decoration((doc, attr.span)));
            }
        }
    }

    if !docs.is_empty() {
        let _ = check_doc(cx, valid_idents, &docs);
    }
}

#[allow(while_let_loop)] // #362
fn check_doc(cx: &EarlyContext, valid_idents: &[String], docs: &[(String, Span)]) -> Result<(), ()> {
    // In markdown, `_` can be used to emphasize something, or, is a raw `_` depending on context.
    // There really is no markdown specification that would disambiguate this properly. This is
    // what GitHub and Rustdoc do:
    //
    // foo_bar test_quz    → foo_bar test_quz
    // foo_bar_baz         → foo_bar_baz (note that the “official” spec says this should be emphasized)
    // _foo bar_ test_quz_ → foo bar test_quz_
    // \_foo bar\_         → _foo bar_
    // (_baz_)             → (baz)
    // foo _ bar _ baz     → foo _ bar _ baz

    /// Character that can appear in a path
    fn is_path_char(c: char) -> bool {
        match c {
            t if t.is_alphanumeric() => true,
            ':' | '_' => true,
            _ => false,
        }
    }

    #[derive(Clone, Debug)]
    /// This type is used to iterate through the documentation characters, keeping the span at the
    /// same time.
    struct Parser<'a> {
        /// First byte of the current potential match
        current_word_begin: usize,
        /// List of lines and their associated span
        docs: &'a [(String, Span)],
        /// Index of the current line we are parsing
        line: usize,
        /// Whether we are in a link
        link: bool,
        /// Whether we are at the beginning of a line
        new_line: bool,
        /// Whether we were to the end of a line last time `next` was called
        reset: bool,
        /// The position of the current character within the current line
        pos: usize,
    }

    impl<'a> Parser<'a> {
        fn advance_begin(&mut self) {
            self.current_word_begin = self.pos;
        }

        fn line(&self) -> (&'a str, Span) {
            let (ref doc, span) = self.docs[self.line];
            (doc, span)
        }

        fn peek(&self) -> Option<char> {
            self.line().0[self.pos..].chars().next()
        }

        #[allow(while_let_on_iterator)] // borrowck complains about for
        fn jump_to(&mut self, n: char) -> Result<bool, ()> {
            while let Some((new_line, c)) = self.next() {
                if c == n {
                    self.advance_begin();
                    return Ok(new_line);
                }
            }

            Err(())
        }

        fn next_line(&mut self) {
            self.pos = 0;
            self.current_word_begin = 0;
            self.line += 1;
            self.new_line = true;
        }

        fn put_back(&mut self, c: char) {
            self.pos -= c.len_utf8();
        }

        #[allow(cast_possible_truncation)]
        fn word(&self) -> (&'a str, Span) {
            let begin = self.current_word_begin;
            let end = self.pos;

            debug_assert_eq!(end as u32 as usize, end);
            debug_assert_eq!(begin as u32 as usize, begin);

            let (doc, mut span) = self.line();
            span.hi = span.lo + BytePos(end as u32);
            span.lo = span.lo + BytePos(begin as u32);

            (&doc[begin..end], span)
        }
    }

    impl<'a> Iterator for Parser<'a> {
        type Item = (bool, char);

        fn next(&mut self) -> Option<(bool, char)> {
            while self.line < self.docs.len() {
                if self.reset {
                    self.line += 1;
                    self.reset = false;
                    self.pos = 0;
                    self.current_word_begin = 0;
                }

                let mut chars = self.line().0[self.pos..].chars();
                let c = chars.next();

                if let Some(c) = c {
                    self.pos += c.len_utf8();
                    let new_line = self.new_line;
                    self.new_line = c == '\n' || (self.new_line && c.is_whitespace());
                    return Some((new_line, c));
                } else if self.line == self.docs.len() - 1 {
                    return None;
                } else {
                    self.new_line = true;
                    self.reset = true;
                    self.pos += 1;
                    return Some((true, '\n'));
                }
            }

            None
        }
    }

    let mut parser = Parser {
        current_word_begin: 0,
        docs: docs,
        line: 0,
        link: false,
        new_line: true,
        reset: false,
        pos: 0,
    };

    /// Check for fanced code block.
    macro_rules! check_block {
        ($parser:expr, $c:tt, $new_line:expr) => {{
            check_block!($parser, $c, $c, $new_line)
        }};

        ($parser:expr, $c:pat, $c_expr:expr, $new_line:expr) => {{
            fn check_block(parser: &mut Parser, new_line: bool) -> Result<bool, ()> {
                if new_line {
                    let mut lookup_parser = parser.clone();
                    if let (Some((false, $c)), Some((false, $c))) = (lookup_parser.next(), lookup_parser.next()) {
                        *parser = lookup_parser;
                        // 3 or more ` or ~ open a code block to be closed with the same number of ` or ~
                        let mut open_count = 3;
                        while let Some((false, $c)) = parser.next() {
                            open_count += 1;
                        }

                        loop {
                            loop {
                                if try!(parser.jump_to($c_expr)) {
                                    break;
                                }
                            }

                            lookup_parser = parser.clone();
                            let a = lookup_parser.next();
                            let b = lookup_parser.next();
                            if let (Some((false, $c)), Some((false, $c))) = (a, b) {
                                let mut close_count = 3;
                                while let Some((false, $c)) = lookup_parser.next() {
                                    close_count += 1;
                                }

                                if close_count == open_count {
                                    *parser = lookup_parser;
                                    return Ok(true);
                                }
                            }
                        }
                    }
                }

                Ok(false)
            }

            check_block(&mut $parser, $new_line)
        }};
    }

    loop {
        match parser.next() {
            Some((new_line, c)) => {
                match c {
                    '#' if new_line => {
                        // don’t warn on titles
                        parser.next_line();
                    },
                    '`' => {
                        if try!(check_block!(parser, '`', new_line)) {
                            continue;
                        }

                        // not a code block, just inline code
                        try!(parser.jump_to('`'));
                    },
                    '~' => {
                        if try!(check_block!(parser, '~', new_line)) {
                            continue;
                        }

                        // ~ does not introduce inline code, but two of them introduce
                        // strikethrough. Too bad for the consistency but we don't care about
                        // strikethrough.
                    },
                    '[' => {
                        // Check for a reference definition `[foo]:` at the beginning of a line
                        let mut link = true;

                        if new_line {
                            let mut lookup_parser = parser.clone();
                            if lookup_parser.any(|(_, c)| c == ']') {
                                if let Some((_, ':')) = lookup_parser.next() {
                                    lookup_parser.next_line();
                                    parser = lookup_parser;
                                    link = false;
                                }
                            }
                        }

                        parser.advance_begin();
                        parser.link = link;
                    },
                    ']' if parser.link => {
                        parser.link = false;

                        match parser.peek() {
                            Some('(') => {
                                try!(parser.jump_to(')'));
                            },
                            Some('[') => {
                                try!(parser.jump_to(']'));
                            },
                            Some(_) => continue,
                            None => return Err(()),
                        }
                    },
                    c if !is_path_char(c) => {
                        parser.advance_begin();
                    },
                    _ => {
                        if let Some((_, c)) = parser.find(|&(_, c)| !is_path_char(c)) {
                            parser.put_back(c);
                        }

                        let (word, span) = parser.word();
                        check_word(cx, valid_idents, word, span);
                        parser.advance_begin();
                    },
                }

            },
            None => break,
        }
    }

    Ok(())
}

fn check_word(cx: &EarlyContext, valid_idents: &[String], word: &str, span: Span) {
    /// Checks if a string a camel-case, ie. contains at least two uppercase letter (`Clippy` is
    /// ok) and one lower-case letter (`NASA` is ok). Plural are also excluded (`IDs` is ok).
    fn is_camel_case(s: &str) -> bool {
        if s.starts_with(|c: char| c.is_digit(10)) {
            return false;
        }

        let s = if s.ends_with('s') {
            &s[..s.len() - 1]
        } else {
            s
        };

        s.chars().all(char::is_alphanumeric) && s.chars().filter(|&c| c.is_uppercase()).take(2).count() > 1 &&
        s.chars().filter(|&c| c.is_lowercase()).take(1).count() > 0
    }

    fn has_underscore(s: &str) -> bool {
        s != "_" && !s.contains("\\_") && s.contains('_')
    }

    // Trim punctuation as in `some comment (see foo::bar).`
    //                                                   ^^
    // Or even as in `_foo bar_` which is emphasized.
    let word = word.trim_matches(|c: char| !c.is_alphanumeric());

    if valid_idents.iter().any(|i| i == word) {
        return;
    }

    if has_underscore(word) || word.contains("::") || is_camel_case(word) {
        span_lint(cx,
                  DOC_MARKDOWN,
                  span,
                  &format!("you should put `{}` between ticks in the documentation", word));
    }
}
<commit_msg>Run rustfmt<commit_after>use rustc::lint::*;
use syntax::ast;
use syntax::codemap::{Span, BytePos};
use utils::span_lint;

/// **What it does:** Checks for the presence of `_`, `::` or camel-case words
/// outside ticks in documentation.
///
/// **Why is this bad?** *Rustdoc* supports markdown formatting, `_`, `::` and
/// camel-case probably indicates some code which should be included between
/// ticks. `_` can also be used for empasis in markdown, this lint tries to
/// consider that.
///
/// **Known problems:** Lots of bad docs won’t be fixed, what the lint checks
/// for is limited, and there are still false positives.
///
/// **Examples:**
/// ```rust
/// /// Do something with the foo_bar parameter. See also that::other::module::foo.
/// // ^ `foo_bar` and `that::other::module::foo` should be ticked.
/// fn doit(foo_bar) { .. }
/// ```
declare_lint! {
    pub DOC_MARKDOWN,
    Warn,
    "presence of `_`, `::` or camel-case outside backticks in documentation"
}

#[derive(Clone)]
pub struct Doc {
    valid_idents: Vec<String>,
}

impl Doc {
    pub fn new(valid_idents: Vec<String>) -> Self {
        Doc { valid_idents: valid_idents }
    }
}

impl LintPass for Doc {
    fn get_lints(&self) -> LintArray {
        lint_array![DOC_MARKDOWN]
    }
}

impl EarlyLintPass for Doc {
    fn check_crate(&mut self, cx: &EarlyContext, krate: &ast::Crate) {
        check_attrs(cx, &self.valid_idents, &krate.attrs);
    }

    fn check_item(&mut self, cx: &EarlyContext, item: &ast::Item) {
        check_attrs(cx, &self.valid_idents, &item.attrs);
    }
}

/// Cleanup documentation decoration (`///` and such).
///
/// We can't use `syntax::attr::AttributeMethods::with_desugared_doc` or
/// `syntax::parse::lexer::comments::strip_doc_comment_decoration` because we need to keep track of
/// the span but this function is inspired from the later.
#[allow(cast_possible_truncation)]
pub fn strip_doc_comment_decoration((comment, span): (String, Span)) -> Vec<(String, Span)> {
    // one-line comments lose their prefix
    const ONELINERS: &'static [&'static str] = &["///!", "///", "//!", "//"];
    for prefix in ONELINERS {
        if comment.starts_with(*prefix) {
            return vec![(comment[prefix.len()..].to_owned(),
                         Span { lo: span.lo + BytePos(prefix.len() as u32), ..span })];
        }
    }

    if comment.starts_with("/*") {
        return comment[3..comment.len() - 2]
            .lines()
            .map(|line| {
                let offset = line.as_ptr() as usize - comment.as_ptr() as usize;
                debug_assert_eq!(offset as u32 as usize, offset);

                (line.to_owned(), Span { lo: span.lo + BytePos(offset as u32), ..span })
            })
            .collect();
    }

    panic!("not a doc-comment: {}", comment);
}

pub fn check_attrs<'a>(cx: &EarlyContext, valid_idents: &[String], attrs: &'a [ast::Attribute]) {
    let mut docs = vec![];

    for attr in attrs {
        if attr.is_sugared_doc {
            if let Some(ref doc) = attr.value_str() {
                let doc = (*doc.as_str()).to_owned();
                docs.extend_from_slice(&strip_doc_comment_decoration((doc, attr.span)));
            }
        }
    }

    if !docs.is_empty() {
        let _ = check_doc(cx, valid_idents, &docs);
    }
}

#[allow(while_let_loop)] // #362
fn check_doc(cx: &EarlyContext, valid_idents: &[String], docs: &[(String, Span)]) -> Result<(), ()> {
    // In markdown, `_` can be used to emphasize something, or, is a raw `_` depending on context.
    // There really is no markdown specification that would disambiguate this properly. This is
    // what GitHub and Rustdoc do:
    //
    // foo_bar test_quz    → foo_bar test_quz
    // foo_bar_baz         → foo_bar_baz (note that the “official” spec says this should be emphasized)
    // _foo bar_ test_quz_ → foo bar test_quz_
    // \_foo bar\_         → _foo bar_
    // (_baz_)             → (baz)
    // foo _ bar _ baz     → foo _ bar _ baz

    /// Character that can appear in a path
    fn is_path_char(c: char) -> bool {
        match c {
            t if t.is_alphanumeric() => true,
            ':' | '_' => true,
            _ => false,
        }
    }

    #[derive(Clone, Debug)]
    /// This type is used to iterate through the documentation characters, keeping the span at the
    /// same time.
    struct Parser<'a> {
        /// First byte of the current potential match
        current_word_begin: usize,
        /// List of lines and their associated span
        docs: &'a [(String, Span)],
        /// Index of the current line we are parsing
        line: usize,
        /// Whether we are in a link
        link: bool,
        /// Whether we are at the beginning of a line
        new_line: bool,
        /// Whether we were to the end of a line last time `next` was called
        reset: bool,
        /// The position of the current character within the current line
        pos: usize,
    }

    impl<'a> Parser<'a> {
        fn advance_begin(&mut self) {
            self.current_word_begin = self.pos;
        }

        fn line(&self) -> (&'a str, Span) {
            let (ref doc, span) = self.docs[self.line];
            (doc, span)
        }

        fn peek(&self) -> Option<char> {
            self.line().0[self.pos..].chars().next()
        }

        #[allow(while_let_on_iterator)] // borrowck complains about for
        fn jump_to(&mut self, n: char) -> Result<bool, ()> {
            while let Some((new_line, c)) = self.next() {
                if c == n {
                    self.advance_begin();
                    return Ok(new_line);
                }
            }

            Err(())
        }

        fn next_line(&mut self) {
            self.pos = 0;
            self.current_word_begin = 0;
            self.line += 1;
            self.new_line = true;
        }

        fn put_back(&mut self, c: char) {
            self.pos -= c.len_utf8();
        }

        #[allow(cast_possible_truncation)]
        fn word(&self) -> (&'a str, Span) {
            let begin = self.current_word_begin;
            let end = self.pos;

            debug_assert_eq!(end as u32 as usize, end);
            debug_assert_eq!(begin as u32 as usize, begin);

            let (doc, mut span) = self.line();
            span.hi = span.lo + BytePos(end as u32);
            span.lo = span.lo + BytePos(begin as u32);

            (&doc[begin..end], span)
        }
    }

    impl<'a> Iterator for Parser<'a> {
        type Item = (bool, char);

        fn next(&mut self) -> Option<(bool, char)> {
            while self.line < self.docs.len() {
                if self.reset {
                    self.line += 1;
                    self.reset = false;
                    self.pos = 0;
                    self.current_word_begin = 0;
                }

                let mut chars = self.line().0[self.pos..].chars();
                let c = chars.next();

                if let Some(c) = c {
                    self.pos += c.len_utf8();
                    let new_line = self.new_line;
                    self.new_line = c == '\n' || (self.new_line && c.is_whitespace());
                    return Some((new_line, c));
                } else if self.line == self.docs.len() - 1 {
                    return None;
                } else {
                    self.new_line = true;
                    self.reset = true;
                    self.pos += 1;
                    return Some((true, '\n'));
                }
            }

            None
        }
    }

    let mut parser = Parser {
        current_word_begin: 0,
        docs: docs,
        line: 0,
        link: false,
        new_line: true,
        reset: false,
        pos: 0,
    };

    /// Check for fanced code block.
    macro_rules! check_block {
        ($parser:expr, $c:tt, $new_line:expr) => {{
            check_block!($parser, $c, $c, $new_line)
        }};

        ($parser:expr, $c:pat, $c_expr:expr, $new_line:expr) => {{
            fn check_block(parser: &mut Parser, new_line: bool) -> Result<bool, ()> {
                if new_line {
                    let mut lookup_parser = parser.clone();
                    if let (Some((false, $c)), Some((false, $c))) = (lookup_parser.next(), lookup_parser.next()) {
                        *parser = lookup_parser;
                        // 3 or more ` or ~ open a code block to be closed with the same number of ` or ~
                        let mut open_count = 3;
                        while let Some((false, $c)) = parser.next() {
                            open_count += 1;
                        }

                        loop {
                            loop {
                                if try!(parser.jump_to($c_expr)) {
                                    break;
                                }
                            }

                            lookup_parser = parser.clone();
                            let a = lookup_parser.next();
                            let b = lookup_parser.next();
                            if let (Some((false, $c)), Some((false, $c))) = (a, b) {
                                let mut close_count = 3;
                                while let Some((false, $c)) = lookup_parser.next() {
                                    close_count += 1;
                                }

                                if close_count == open_count {
                                    *parser = lookup_parser;
                                    return Ok(true);
                                }
                            }
                        }
                    }
                }

                Ok(false)
            }

            check_block(&mut $parser, $new_line)
        }};
    }

    loop {
        match parser.next() {
            Some((new_line, c)) => {
                match c {
                    '#' if new_line => {
                        // don’t warn on titles
                        parser.next_line();
                    },
                    '`' => {
                        if try!(check_block!(parser, '`', new_line)) {
                            continue;
                        }

                        // not a code block, just inline code
                        try!(parser.jump_to('`'));
                    },
                    '~' => {
                        if try!(check_block!(parser, '~', new_line)) {
                            continue;
                        }

                        // ~ does not introduce inline code, but two of them introduce
                        // strikethrough. Too bad for the consistency but we don't care about
                        // strikethrough.
                    },
                    '[' => {
                        // Check for a reference definition `[foo]:` at the beginning of a line
                        let mut link = true;

                        if new_line {
                            let mut lookup_parser = parser.clone();
                            if lookup_parser.any(|(_, c)| c == ']') {
                                if let Some((_, ':')) = lookup_parser.next() {
                                    lookup_parser.next_line();
                                    parser = lookup_parser;
                                    link = false;
                                }
                            }
                        }

                        parser.advance_begin();
                        parser.link = link;
                    },
                    ']' if parser.link => {
                        parser.link = false;

                        match parser.peek() {
                            Some('(') => {
                                try!(parser.jump_to(')'));
                            },
                            Some('[') => {
                                try!(parser.jump_to(']'));
                            },
                            Some(_) => continue,
                            None => return Err(()),
                        }
                    },
                    c if !is_path_char(c) => {
                        parser.advance_begin();
                    },
                    _ => {
                        if let Some((_, c)) = parser.find(|&(_, c)| !is_path_char(c)) {
                            parser.put_back(c);
                        }

                        let (word, span) = parser.word();
                        check_word(cx, valid_idents, word, span);
                        parser.advance_begin();
                    },
                }

            },
            None => break,
        }
    }

    Ok(())
}

fn check_word(cx: &EarlyContext, valid_idents: &[String], word: &str, span: Span) {
    /// Checks if a string a camel-case, ie. contains at least two uppercase letter (`Clippy` is
    /// ok) and one lower-case letter (`NASA` is ok). Plural are also excluded (`IDs` is ok).
    fn is_camel_case(s: &str) -> bool {
        if s.starts_with(|c: char| c.is_digit(10)) {
            return false;
        }

        let s = if s.ends_with('s') {
            &s[..s.len() - 1]
        } else {
            s
        };

        s.chars().all(char::is_alphanumeric) && s.chars().filter(|&c| c.is_uppercase()).take(2).count() > 1 &&
        s.chars().filter(|&c| c.is_lowercase()).take(1).count() > 0
    }

    fn has_underscore(s: &str) -> bool {
        s != "_" && !s.contains("\\_") && s.contains('_')
    }

    // Trim punctuation as in `some comment (see foo::bar).`
    //                                                   ^^
    // Or even as in `_foo bar_` which is emphasized.
    let word = word.trim_matches(|c: char| !c.is_alphanumeric());

    if valid_idents.iter().any(|i| i == word) {
        return;
    }

    if has_underscore(word) || word.contains("::") || is_camel_case(word) {
        span_lint(cx,
                  DOC_MARKDOWN,
                  span,
                  &format!("you should put `{}` between ticks in the documentation", word));
    }
}
<|endoftext|>


<commit_before>#![allow(non_snake_case)]

/// Extracted from [CMSIS 5]'s CMSIS/Core/Include/cmsis_gcc.h
///
/// [CMSIS 5]: https://github.com/ARM-software/CMSIS_5

/* Core function access */

/// Enable IRQ Interrupts
///
/// Enables IRQ interrupts by clearing the I-bit in the CPSR. Can only be
/// executed in Privileged modes.
#[inline]
#[target_feature(enable = "mclass")]
#[cfg_attr(test, assert_instr(cpsie))]
pub unsafe fn __enable_irq() {
    asm!("cpsie i" : : : "memory" : "volatile");
}

/// Disable IRQ Interrupts
///
/// Disables IRQ interrupts by setting the I-bit in the CPSR. Can only be
/// executed in Privileged modes.
#[inline]
#[target_feature(enable = "mclass")]
#[cfg_attr(test, assert_instr(cpsid))]
pub unsafe fn __disable_irq() {
    asm!("cpsid i" : : : "memory" : "volatile");
}

/// Get Control Register
///
/// Returns the content of the Control Register.
#[inline]
#[target_feature(enable = "mclass")]
#[cfg_attr(test, assert_instr(mrs))]
pub unsafe fn __get_CONTROL() -> u32 {
    let result: u32;
    asm!("mrs $0, CONTROL" : "=r"(result) : : : "volatile");
    result
}

/// Set Control Register
///
/// Writes the given value to the Control Register.
#[inline]
#[target_feature(enable = "mclass")]
#[cfg_attr(test, assert_instr(msr))]
pub unsafe fn __set_CONTROL(control: u32) {
    asm!("msr CONTROL, $0" : : "r"(control) : "memory" : "volatile");
}

/// Get IPSR Register
///
/// Returns the content of the IPSR Register.
#[inline]
#[target_feature(enable = "mclass")]
#[cfg_attr(test, assert_instr(mrs))]
pub unsafe fn __get_IPSR() -> u32 {
    let result: u32;
    asm!("mrs $0, IPSR" : "=r"(result) : : : "volatile");
    result
}

/// Get APSR Register
///
/// Returns the content of the APSR Register.
#[inline]
#[target_feature(enable = "mclass")]
#[cfg_attr(test, assert_instr(mrs))]
pub unsafe fn __get_APSR() -> u32 {
    let result: u32;
    asm!("mrs $0, APSR" : "=r"(result) : : : "volatile");
    result
}

/// Get xPSR Register
///
/// Returns the content of the xPSR Register.
#[inline]
#[target_feature(enable = "mclass")]
#[cfg_attr(test, assert_instr(mrs))]
pub unsafe fn __get_xPSR() -> u32 {
    let result: u32;
    asm!("mrs $0, XPSR" : "=r"(result) : : : "volatile");
    result
}

/// Get Process Stack Pointer
///
/// Returns the current value of the Process Stack Pointer (PSP).
#[inline]
#[target_feature(enable = "mclass")]
#[cfg_attr(test, assert_instr(mrs))]
pub unsafe fn __get_PSP() -> u32 {
    let result: u32;
    asm!("mrs $0, PSP" : "=r"(result) : : : "volatile");
    result
}

/// Set Process Stack Pointer
///
/// Assigns the given value to the Process Stack Pointer (PSP).
#[inline]
#[target_feature(enable = "mclass")]
#[cfg_attr(test, assert_instr(msr))]
pub unsafe fn __set_PSP(top_of_proc_stack: u32) {
    asm!("msr PSP, $0" : : "r"(top_of_proc_stack) : : "volatile");
}

/// Get Main Stack Pointer
///
/// Returns the current value of the Main Stack Pointer (MSP).
#[inline]
#[target_feature(enable = "mclass")]
#[cfg_attr(test, assert_instr(mrs))]
pub unsafe fn __get_MSP() -> u32 {
    let result: u32;
    asm!("mrs $0, MSP" : "=r"(result) : : : "volatile");
    result
}

/// Set Main Stack Pointer
///
/// Assigns the given value to the Main Stack Pointer (MSP).
#[inline]
#[target_feature(enable = "mclass")]
#[cfg_attr(test, assert_instr(msr))]
pub unsafe fn __set_MSP(top_of_main_stack: u32) {
    asm!("msr MSP, $0" : : "r"(top_of_main_stack) : : "volatile");
}

/// Get Priority Mask
///
/// Returns the current state of the priority mask bit from the Priority Mask
/// Register.
#[inline]
#[target_feature(enable = "mclass")]
#[cfg_attr(test, assert_instr(mrs))]
pub unsafe fn __get_PRIMASK() -> u32 {
    let result: u32;
    asm!("mrs $0, PRIMASK" : "=r"(result) : : "memory" : "volatile");
    result
}

/// Set Priority Mask
///
/// Assigns the given value to the Priority Mask Register.
#[inline]
#[target_feature(enable = "mclass")]
#[cfg_attr(test, assert_instr(msr))]
pub unsafe fn __set_PRIMASK(pri_mask: u32) {
    asm!("msr PRIMASK, $0" : : "r"(pri_mask) : : "volatile");
}

#[cfg(target_feature = "v7")]
mod v7 {
    /// Enable FIQ
    ///
    /// Enables FIQ interrupts by clearing the F-bit in the CPSR. Can only be
    /// executed in Privileged modes.
    #[inline]
    #[target_feature(enable = "mclass")]
    #[cfg_attr(test, assert_instr(cpsie))]
    pub unsafe fn __enable_fault_irq() {
        asm!("cpsie f" : : : "memory" : "volatile");
    }

    /// Disable FIQ
    ///
    /// Disables FIQ interrupts by setting the F-bit in the CPSR. Can only be
    /// executed in Privileged modes.
    #[inline]
    #[target_feature(enable = "mclass")]
    #[cfg_attr(test, assert_instr(cpsid))]
    pub unsafe fn __disable_fault_irq() {
        asm!("cpsid f" : : : "memory" : "volatile");
    }

    /// Get Base Priority
    ///
    /// Returns the current value of the Base Priority register.
    #[inline]
    #[target_feature(enable = "mclass")]
    #[cfg_attr(test, assert_instr(mrs))]
    pub unsafe fn __get_BASEPRI() -> u32 {
        let result: u32;
        asm!("mrs $0, BASEPRI" : "=r"(result) : : : "volatile");
        result
    }

    /// Set Base Priority
    ///
    /// Assigns the given value to the Base Priority register.
    #[inline]
    #[target_feature(enable = "mclass")]
    #[cfg_attr(test, assert_instr(msr))]
    pub unsafe fn __set_BASEPRI(base_pri: u32) {
        asm!("msr BASEPRI, $0" : : "r"(base_pri) : "memory" : "volatile");
    }

    /// Set Base Priority with condition
    ///
    /// Assigns the given value to the Base Priority register only if BASEPRI
    /// masking is disabled, or the new value increases the BASEPRI
    /// priority level.
    #[inline]
    #[target_feature(enable = "mclass")]
    #[cfg_attr(test, assert_instr(mrs))]
    pub unsafe fn __set_BASEPRI_MAX(base_pri: u32) {
        asm!("msr BASEPRI_MAX, $0" : : "r"(base_pri) : "memory" : "volatile");
    }

    /// Get Fault Mask
    ///
    /// Returns the current value of the Fault Mask register.
    #[inline]
    #[target_feature(enable = "mclass")]
    #[cfg_attr(test, assert_instr(mrs))]
    pub unsafe fn __get_FAULTMASK() -> u32 {
        let result: u32;
        asm!("mrs $0, FAULTMASK" : "=r"(result) : : : "volatile");
        result
    }

    /// Set Fault Mask
    ///
    /// Assigns the given value to the Fault Mask register.
    #[inline]
    #[target_feature(enable = "mclass")]
    #[cfg_attr(test, assert_instr(msr))]
    pub unsafe fn __set_FAULTMASK(fault_mask: u32) {
        asm!("msr FAULTMASK, $0" : : "r"(fault_mask) : "memory" : "volatile");
    }
}

#[cfg(target_feature = "v7")]
pub use self::v7::*;

/* Core instruction access */

/// No Operation
///
/// No Operation does nothing. This instruction can be used for code alignment
/// purposes.
#[inline]
#[target_feature(enable = "mclass")]
#[cfg_attr(test, assert_instr(nop))]
pub unsafe fn __NOP() {
    asm!("nop" : : : : "volatile");
}

/// Wait For Interrupt
///
/// Wait For Interrupt is a hint instruction that suspends execution until one
/// of a number of events occurs.
#[inline]
#[target_feature(enable = "mclass")]
#[cfg_attr(test, assert_instr(wfi))]
pub unsafe fn __WFI() {
    asm!("wfi" : : : : "volatile");
}

/// Wait For Event
///
/// Wait For Event is a hint instruction that permits the processor to enter a
/// low-power state until one of a number of events occurs.
#[inline]
#[target_feature(enable = "mclass")]
#[cfg_attr(test, assert_instr(wfe))]
pub unsafe fn __WFE() {
    asm!("wfe" : : : : "volatile");
}

/// Send Event
///
/// Send Event is a hint instruction. It causes an event to be signaled to the
/// CPU.
#[inline]
#[target_feature(enable = "mclass")]
#[cfg_attr(test, assert_instr(sev))]
pub unsafe fn __SEV() {
    asm!("sev" : : : : "volatile");
}

/// Instruction Synchronization Barrier
///
/// Instruction Synchronization Barrier flushes the pipeline in the processor,
/// so that all instructions following the ISB are fetched from cache or
/// memory, after the instruction has been completed.
#[inline]
#[target_feature(enable = "mclass")]
#[cfg_attr(test, assert_instr(isb))]
pub unsafe fn __ISB() {
    asm!("isb 0xF" : : : "memory" : "volatile");
}

/// Data Synchronization Barrier
///
/// Acts as a special kind of Data Memory Barrier. It completes when all
/// explicit memory accesses before this instruction complete.
#[inline]
#[target_feature(enable = "mclass")]
#[cfg_attr(test, assert_instr(dsb))]
pub unsafe fn __DSB() {
    asm!("dsb 0xF" : : : "memory" : "volatile");
}

/// Data Memory Barrier
///
/// Ensures the apparent order of the explicit memory operations before and
/// after the instruction, without ensuring their completion.
#[inline]
#[target_feature(enable = "mclass")]
#[cfg_attr(test, assert_instr(dmb))]
pub unsafe fn __DMB() {
    asm!("dmb 0xF" : : : "memory" : "volatile");
}
<commit_msg>add links to standard, API and implementation to the module level docs<commit_after>//! CMSIS: Cortex Microcontroller Software Interface Standard
//!
//! The version 5 of the standard can be found at:
//!
//! http://arm-software.github.io/CMSIS_5/Core/html/index.html
//!
//! The API reference of the standard can be found at:
//!
//! - Core function access -- http://arm-software.github.io/CMSIS_5/Core/html/group__Core__Register__gr.html
//! - Intrinsic functions for CPU instructions -- http://arm-software.github.io/CMSIS_5/Core/html/group__intrinsic__CPU__gr.html
//!
//! The reference C implementation used as the base of this Rust port can be found at
//!
//! https://github.com/ARM-software/CMSIS_5/blob/5.3.0/CMSIS/Core/Include/cmsis_gcc.h

#![allow(non_snake_case)]

/* Core function access */

/// Enable IRQ Interrupts
///
/// Enables IRQ interrupts by clearing the I-bit in the CPSR. Can only be
/// executed in Privileged modes.
#[inline]
#[target_feature(enable = "mclass")]
#[cfg_attr(test, assert_instr(cpsie))]
pub unsafe fn __enable_irq() {
    asm!("cpsie i" : : : "memory" : "volatile");
}

/// Disable IRQ Interrupts
///
/// Disables IRQ interrupts by setting the I-bit in the CPSR. Can only be
/// executed in Privileged modes.
#[inline]
#[target_feature(enable = "mclass")]
#[cfg_attr(test, assert_instr(cpsid))]
pub unsafe fn __disable_irq() {
    asm!("cpsid i" : : : "memory" : "volatile");
}

/// Get Control Register
///
/// Returns the content of the Control Register.
#[inline]
#[target_feature(enable = "mclass")]
#[cfg_attr(test, assert_instr(mrs))]
pub unsafe fn __get_CONTROL() -> u32 {
    let result: u32;
    asm!("mrs $0, CONTROL" : "=r"(result) : : : "volatile");
    result
}

/// Set Control Register
///
/// Writes the given value to the Control Register.
#[inline]
#[target_feature(enable = "mclass")]
#[cfg_attr(test, assert_instr(msr))]
pub unsafe fn __set_CONTROL(control: u32) {
    asm!("msr CONTROL, $0" : : "r"(control) : "memory" : "volatile");
}

/// Get IPSR Register
///
/// Returns the content of the IPSR Register.
#[inline]
#[target_feature(enable = "mclass")]
#[cfg_attr(test, assert_instr(mrs))]
pub unsafe fn __get_IPSR() -> u32 {
    let result: u32;
    asm!("mrs $0, IPSR" : "=r"(result) : : : "volatile");
    result
}

/// Get APSR Register
///
/// Returns the content of the APSR Register.
#[inline]
#[target_feature(enable = "mclass")]
#[cfg_attr(test, assert_instr(mrs))]
pub unsafe fn __get_APSR() -> u32 {
    let result: u32;
    asm!("mrs $0, APSR" : "=r"(result) : : : "volatile");
    result
}

/// Get xPSR Register
///
/// Returns the content of the xPSR Register.
#[inline]
#[target_feature(enable = "mclass")]
#[cfg_attr(test, assert_instr(mrs))]
pub unsafe fn __get_xPSR() -> u32 {
    let result: u32;
    asm!("mrs $0, XPSR" : "=r"(result) : : : "volatile");
    result
}

/// Get Process Stack Pointer
///
/// Returns the current value of the Process Stack Pointer (PSP).
#[inline]
#[target_feature(enable = "mclass")]
#[cfg_attr(test, assert_instr(mrs))]
pub unsafe fn __get_PSP() -> u32 {
    let result: u32;
    asm!("mrs $0, PSP" : "=r"(result) : : : "volatile");
    result
}

/// Set Process Stack Pointer
///
/// Assigns the given value to the Process Stack Pointer (PSP).
#[inline]
#[target_feature(enable = "mclass")]
#[cfg_attr(test, assert_instr(msr))]
pub unsafe fn __set_PSP(top_of_proc_stack: u32) {
    asm!("msr PSP, $0" : : "r"(top_of_proc_stack) : : "volatile");
}

/// Get Main Stack Pointer
///
/// Returns the current value of the Main Stack Pointer (MSP).
#[inline]
#[target_feature(enable = "mclass")]
#[cfg_attr(test, assert_instr(mrs))]
pub unsafe fn __get_MSP() -> u32 {
    let result: u32;
    asm!("mrs $0, MSP" : "=r"(result) : : : "volatile");
    result
}

/// Set Main Stack Pointer
///
/// Assigns the given value to the Main Stack Pointer (MSP).
#[inline]
#[target_feature(enable = "mclass")]
#[cfg_attr(test, assert_instr(msr))]
pub unsafe fn __set_MSP(top_of_main_stack: u32) {
    asm!("msr MSP, $0" : : "r"(top_of_main_stack) : : "volatile");
}

/// Get Priority Mask
///
/// Returns the current state of the priority mask bit from the Priority Mask
/// Register.
#[inline]
#[target_feature(enable = "mclass")]
#[cfg_attr(test, assert_instr(mrs))]
pub unsafe fn __get_PRIMASK() -> u32 {
    let result: u32;
    asm!("mrs $0, PRIMASK" : "=r"(result) : : "memory" : "volatile");
    result
}

/// Set Priority Mask
///
/// Assigns the given value to the Priority Mask Register.
#[inline]
#[target_feature(enable = "mclass")]
#[cfg_attr(test, assert_instr(msr))]
pub unsafe fn __set_PRIMASK(pri_mask: u32) {
    asm!("msr PRIMASK, $0" : : "r"(pri_mask) : : "volatile");
}

#[cfg(target_feature = "v7")]
mod v7 {
    /// Enable FIQ
    ///
    /// Enables FIQ interrupts by clearing the F-bit in the CPSR. Can only be
    /// executed in Privileged modes.
    #[inline]
    #[target_feature(enable = "mclass")]
    #[cfg_attr(test, assert_instr(cpsie))]
    pub unsafe fn __enable_fault_irq() {
        asm!("cpsie f" : : : "memory" : "volatile");
    }

    /// Disable FIQ
    ///
    /// Disables FIQ interrupts by setting the F-bit in the CPSR. Can only be
    /// executed in Privileged modes.
    #[inline]
    #[target_feature(enable = "mclass")]
    #[cfg_attr(test, assert_instr(cpsid))]
    pub unsafe fn __disable_fault_irq() {
        asm!("cpsid f" : : : "memory" : "volatile");
    }

    /// Get Base Priority
    ///
    /// Returns the current value of the Base Priority register.
    #[inline]
    #[target_feature(enable = "mclass")]
    #[cfg_attr(test, assert_instr(mrs))]
    pub unsafe fn __get_BASEPRI() -> u32 {
        let result: u32;
        asm!("mrs $0, BASEPRI" : "=r"(result) : : : "volatile");
        result
    }

    /// Set Base Priority
    ///
    /// Assigns the given value to the Base Priority register.
    #[inline]
    #[target_feature(enable = "mclass")]
    #[cfg_attr(test, assert_instr(msr))]
    pub unsafe fn __set_BASEPRI(base_pri: u32) {
        asm!("msr BASEPRI, $0" : : "r"(base_pri) : "memory" : "volatile");
    }

    /// Set Base Priority with condition
    ///
    /// Assigns the given value to the Base Priority register only if BASEPRI
    /// masking is disabled, or the new value increases the BASEPRI
    /// priority level.
    #[inline]
    #[target_feature(enable = "mclass")]
    #[cfg_attr(test, assert_instr(mrs))]
    pub unsafe fn __set_BASEPRI_MAX(base_pri: u32) {
        asm!("msr BASEPRI_MAX, $0" : : "r"(base_pri) : "memory" : "volatile");
    }

    /// Get Fault Mask
    ///
    /// Returns the current value of the Fault Mask register.
    #[inline]
    #[target_feature(enable = "mclass")]
    #[cfg_attr(test, assert_instr(mrs))]
    pub unsafe fn __get_FAULTMASK() -> u32 {
        let result: u32;
        asm!("mrs $0, FAULTMASK" : "=r"(result) : : : "volatile");
        result
    }

    /// Set Fault Mask
    ///
    /// Assigns the given value to the Fault Mask register.
    #[inline]
    #[target_feature(enable = "mclass")]
    #[cfg_attr(test, assert_instr(msr))]
    pub unsafe fn __set_FAULTMASK(fault_mask: u32) {
        asm!("msr FAULTMASK, $0" : : "r"(fault_mask) : "memory" : "volatile");
    }
}

#[cfg(target_feature = "v7")]
pub use self::v7::*;

/* Core instruction access */

/// No Operation
///
/// No Operation does nothing. This instruction can be used for code alignment
/// purposes.
#[inline]
#[target_feature(enable = "mclass")]
#[cfg_attr(test, assert_instr(nop))]
pub unsafe fn __NOP() {
    asm!("nop" : : : : "volatile");
}

/// Wait For Interrupt
///
/// Wait For Interrupt is a hint instruction that suspends execution until one
/// of a number of events occurs.
#[inline]
#[target_feature(enable = "mclass")]
#[cfg_attr(test, assert_instr(wfi))]
pub unsafe fn __WFI() {
    asm!("wfi" : : : : "volatile");
}

/// Wait For Event
///
/// Wait For Event is a hint instruction that permits the processor to enter a
/// low-power state until one of a number of events occurs.
#[inline]
#[target_feature(enable = "mclass")]
#[cfg_attr(test, assert_instr(wfe))]
pub unsafe fn __WFE() {
    asm!("wfe" : : : : "volatile");
}

/// Send Event
///
/// Send Event is a hint instruction. It causes an event to be signaled to the
/// CPU.
#[inline]
#[target_feature(enable = "mclass")]
#[cfg_attr(test, assert_instr(sev))]
pub unsafe fn __SEV() {
    asm!("sev" : : : : "volatile");
}

/// Instruction Synchronization Barrier
///
/// Instruction Synchronization Barrier flushes the pipeline in the processor,
/// so that all instructions following the ISB are fetched from cache or
/// memory, after the instruction has been completed.
#[inline]
#[target_feature(enable = "mclass")]
#[cfg_attr(test, assert_instr(isb))]
pub unsafe fn __ISB() {
    asm!("isb 0xF" : : : "memory" : "volatile");
}

/// Data Synchronization Barrier
///
/// Acts as a special kind of Data Memory Barrier. It completes when all
/// explicit memory accesses before this instruction complete.
#[inline]
#[target_feature(enable = "mclass")]
#[cfg_attr(test, assert_instr(dsb))]
pub unsafe fn __DSB() {
    asm!("dsb 0xF" : : : "memory" : "volatile");
}

/// Data Memory Barrier
///
/// Ensures the apparent order of the explicit memory operations before and
/// after the instruction, without ensuring their completion.
#[inline]
#[target_feature(enable = "mclass")]
#[cfg_attr(test, assert_instr(dmb))]
pub unsafe fn __DMB() {
    asm!("dmb 0xF" : : : "memory" : "volatile");
}
<|endoftext|>


<commit_before>//! # Virtual File System
//!
//! VFS stores all files read by rust-analyzer. Reading file contents from VFS
//! always returns the same contents, unless VFS was explicitly modified with
//! `set_file_contents`. All changes to VFS are logged, and can be retrieved via
//! `take_changes` method. The pack of changes is then pushed to `salsa` and
//! triggers incremental recomputation.
//!
//! Files in VFS are identified with `FileId`s -- interned paths. The notion of
//! the path, `VfsPath` is somewhat abstract: at the moment, it is represented
//! as an `std::path::PathBuf` internally, but this is an implementation detail.
//!
//! VFS doesn't do IO or file watching itself. For that, see the `loader`
//! module. `loader::Handle` is an object-safe trait which abstracts both file
//! loading and file watching. `Handle` is dynamically configured with a set of
//! directory entries which should be scanned and watched. `Handle` then
//! asynchronously pushes file changes. Directory entries are configured in
//! free-form via list of globs, it's up to the `Handle` to interpret the globs
//! in any specific way.
//!
//! VFS stores a flat list of files. `FileSet` can partition this list of files
//! into disjoint sets of files. Traversal-like operations (including getting
//! the neighbor file by the relative path) are handled by the `FileSet`.
//! `FileSet`s are also pushed to salsa and cause it to re-check `mod foo;`
//! declarations when files are created or deleted.
//!
//! `file_set::FileSet` and `loader::Entry` play similar, but different roles.
//! Both specify the "set of paths/files", one is geared towards file watching,
//! the other towards salsa changes. In particular, single `file_set::FileSet`
//! may correspond to several `loader::Entry`. For example, a crate from
//! crates.io which uses code generation would have two `Entries` -- for sources
//! in `~/.cargo`, and for generated code in `./target/debug/build`. It will
//! have a single `FileSet` which unions the two sources.
mod vfs_path;
mod path_interner;
mod anchored_path;
pub mod file_set;
pub mod loader;

use std::{fmt, mem};

use crate::path_interner::PathInterner;

pub use crate::{
    anchored_path::{AnchoredPath, AnchoredPathBuf},
    vfs_path::VfsPath,
};
pub use paths::{AbsPath, AbsPathBuf};

#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub struct FileId(pub u32);

#[derive(Default)]
pub struct Vfs {
    interner: PathInterner,
    data: Vec<Option<Vec<u8>>>,
    changes: Vec<ChangedFile>,
}

pub struct ChangedFile {
    pub file_id: FileId,
    pub change_kind: ChangeKind,
}

impl ChangedFile {
    pub fn exists(&self) -> bool {
        self.change_kind != ChangeKind::Delete
    }
    pub fn is_created_or_deleted(&self) -> bool {
        matches!(self.change_kind, ChangeKind::Create | ChangeKind::Delete)
    }
}

#[derive(Eq, PartialEq, Copy, Clone, Debug)]
pub enum ChangeKind {
    Create,
    Modify,
    Delete,
}

impl Vfs {
    pub fn len(&self) -> usize {
        self.data.len()
    }
    pub fn file_id(&self, path: &VfsPath) -> Option<FileId> {
        self.interner.get(path).filter(|&it| self.get(it).is_some())
    }
    pub fn file_path(&self, file_id: FileId) -> VfsPath {
        self.interner.lookup(file_id).clone()
    }
    pub fn file_contents(&self, file_id: FileId) -> &[u8] {
        self.get(file_id).as_deref().unwrap()
    }
    pub fn iter(&self) -> impl Iterator<Item = (FileId, &VfsPath)> + '_ {
        (0..self.data.len())
            .map(|it| FileId(it as u32))
            .filter(move |&file_id| self.get(file_id).is_some())
            .map(move |file_id| {
                let path = self.interner.lookup(file_id);
                (file_id, path)
            })
    }
    pub fn set_file_contents(&mut self, path: VfsPath, contents: Option<Vec<u8>>) -> bool {
        let file_id = self.alloc_file_id(path);
        let change_kind = match (&self.get(file_id), &contents) {
            (None, None) => return false,
            (None, Some(_)) => ChangeKind::Create,
            (Some(_), None) => ChangeKind::Delete,
            (Some(old), Some(new)) if old == new => return false,
            (Some(_), Some(_)) => ChangeKind::Modify,
        };

        *self.get_mut(file_id) = contents;
        self.changes.push(ChangedFile { file_id, change_kind });
        true
    }
    pub fn has_changes(&self) -> bool {
        !self.changes.is_empty()
    }
    pub fn take_changes(&mut self) -> Vec<ChangedFile> {
        mem::take(&mut self.changes)
    }
    fn alloc_file_id(&mut self, path: VfsPath) -> FileId {
        let file_id = self.interner.intern(path);
        let idx = file_id.0 as usize;
        let len = self.data.len().max(idx + 1);
        self.data.resize_with(len, || None);
        file_id
    }
    fn get(&self, file_id: FileId) -> &Option<Vec<u8>> {
        &self.data[file_id.0 as usize]
    }
    fn get_mut(&mut self, file_id: FileId) -> &mut Option<Vec<u8>> {
        &mut self.data[file_id.0 as usize]
    }
}

impl fmt::Debug for Vfs {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Vfs").field("n_files", &self.data.len()).finish()
    }
}
<commit_msg>Use intra-doc links in `vfs` crate documentation<commit_after>//! # Virtual File System
//!
//! VFS stores all files read by rust-analyzer. Reading file contents from VFS
//! always returns the same contents, unless VFS was explicitly modified with
//! [`set_file_contents`]. All changes to VFS are logged, and can be retrieved via
//! [`take_changes`] method. The pack of changes is then pushed to `salsa` and
//! triggers incremental recomputation.
//!
//! Files in VFS are identified with [`FileId`]s -- interned paths. The notion of
//! the path, [`VfsPath`] is somewhat abstract: at the moment, it is represented
//! as an [`std::path::PathBuf`] internally, but this is an implementation detail.
//!
//! VFS doesn't do IO or file watching itself. For that, see the [`loader`]
//! module. [`loader::Handle`] is an object-safe trait which abstracts both file
//! loading and file watching. [`Handle`] is dynamically configured with a set of
//! directory entries which should be scanned and watched. [`Handle`] then
//! asynchronously pushes file changes. Directory entries are configured in
//! free-form via list of globs, it's up to the [`Handle`] to interpret the globs
//! in any specific way.
//!
//! VFS stores a flat list of files. [`FileSet`] can partition this list of files
//! into disjoint sets of files. Traversal-like operations (including getting
//! the neighbor file by the relative path) are handled by the [`FileSet`].
//! [`FileSet`]s are also pushed to salsa and cause it to re-check `mod foo;`
//! declarations when files are created or deleted.
//!
//! [`file_set::FileSet`] and [`loader::Entry`] play similar, but different roles.
//! Both specify the "set of paths/files", one is geared towards file watching,
//! the other towards salsa changes. In particular, single [`file_set::FileSet`]
//! may correspond to several [`loader::Entry`]. For example, a crate from
//! crates.io which uses code generation would have two [`Entries`] -- for sources
//! in `~/.cargo`, and for generated code in `./target/debug/build`. It will
//! have a single [`FileSet`] which unions the two sources.
//!
//! [`set_file_contents`]: Vfs::set_file_contents
//! [`take_changes`]: Vfs::take_changes
//! [`FileSet`]: file_set::FileSet
//! [`Handle`]: loader::Handle
//! [`Entries`]: loader::Entry
mod anchored_path;
pub mod file_set;
pub mod loader;
mod path_interner;
mod vfs_path;

use std::{fmt, mem};

use crate::path_interner::PathInterner;

pub use crate::{
    anchored_path::{AnchoredPath, AnchoredPathBuf},
    vfs_path::VfsPath,
};
pub use paths::{AbsPath, AbsPathBuf};

#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub struct FileId(pub u32);

#[derive(Default)]
pub struct Vfs {
    interner: PathInterner,
    data: Vec<Option<Vec<u8>>>,
    changes: Vec<ChangedFile>,
}

pub struct ChangedFile {
    pub file_id: FileId,
    pub change_kind: ChangeKind,
}

impl ChangedFile {
    pub fn exists(&self) -> bool {
        self.change_kind != ChangeKind::Delete
    }
    pub fn is_created_or_deleted(&self) -> bool {
        matches!(self.change_kind, ChangeKind::Create | ChangeKind::Delete)
    }
}

#[derive(Eq, PartialEq, Copy, Clone, Debug)]
pub enum ChangeKind {
    Create,
    Modify,
    Delete,
}

impl Vfs {
    pub fn len(&self) -> usize {
        self.data.len()
    }
    pub fn file_id(&self, path: &VfsPath) -> Option<FileId> {
        self.interner.get(path).filter(|&it| self.get(it).is_some())
    }
    pub fn file_path(&self, file_id: FileId) -> VfsPath {
        self.interner.lookup(file_id).clone()
    }
    pub fn file_contents(&self, file_id: FileId) -> &[u8] {
        self.get(file_id).as_deref().unwrap()
    }
    pub fn iter(&self) -> impl Iterator<Item = (FileId, &VfsPath)> + '_ {
        (0..self.data.len())
            .map(|it| FileId(it as u32))
            .filter(move |&file_id| self.get(file_id).is_some())
            .map(move |file_id| {
                let path = self.interner.lookup(file_id);
                (file_id, path)
            })
    }
    pub fn set_file_contents(&mut self, path: VfsPath, contents: Option<Vec<u8>>) -> bool {
        let file_id = self.alloc_file_id(path);
        let change_kind = match (&self.get(file_id), &contents) {
            (None, None) => return false,
            (None, Some(_)) => ChangeKind::Create,
            (Some(_), None) => ChangeKind::Delete,
            (Some(old), Some(new)) if old == new => return false,
            (Some(_), Some(_)) => ChangeKind::Modify,
        };

        *self.get_mut(file_id) = contents;
        self.changes.push(ChangedFile { file_id, change_kind });
        true
    }
    pub fn has_changes(&self) -> bool {
        !self.changes.is_empty()
    }
    pub fn take_changes(&mut self) -> Vec<ChangedFile> {
        mem::take(&mut self.changes)
    }
    fn alloc_file_id(&mut self, path: VfsPath) -> FileId {
        let file_id = self.interner.intern(path);
        let idx = file_id.0 as usize;
        let len = self.data.len().max(idx + 1);
        self.data.resize_with(len, || None);
        file_id
    }
    fn get(&self, file_id: FileId) -> &Option<Vec<u8>> {
        &self.data[file_id.0 as usize]
    }
    fn get_mut(&mut self, file_id: FileId) -> &mut Option<Vec<u8>> {
        &mut self.data[file_id.0 as usize]
    }
}

impl fmt::Debug for Vfs {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Vfs").field("n_files", &self.data.len()).finish()
    }
}
<|endoftext|>