<commit_before>use alloc::arc::{Arc, Weak};
use alloc::boxed::Box;

use collections::{BTreeMap, String};
use collections::string::ToString;

use core::cell::Cell;
use core::mem::size_of;

use scheduler::context::context_switch;

use schemes::{Result, Resource, ResourceSeek, KScheme, Url};

use sync::Intex;

use system::error::{Error, EBADF, EINVAL, ENOENT, ESPIPE};
use system::scheme::Packet;
use system::syscall::SYS_OPEN;

struct SchemeInner {
    name: String,
    next_id: Cell<usize>,
    todo: Intex<BTreeMap<usize, (usize, usize, usize, usize)>>,
    done: Intex<BTreeMap<usize, (usize, usize, usize, usize)>>,
}

impl SchemeInner {
    fn new(name: String) -> SchemeInner {
        SchemeInner {
            name: name,
            next_id: Cell::new(1),
            todo: Intex::new(BTreeMap::new()),
            done: Intex::new(BTreeMap::new()),
        }
    }

    fn recv(&self, packet: &mut Packet) {
        loop {
            {
                let mut todo = self.todo.lock();

                packet.id = if let Some(id) = todo.keys().next() {
                    *id
                } else {
                    0
                };

                if packet.id > 0 {
                    if let Some(regs) = todo.remove(&packet.id) {
                        packet.a = regs.0;
                        packet.b = regs.1;
                        packet.c = regs.2;
                        packet.d = regs.3;
                        return
                    }
                }
            }

            unsafe { context_switch(false) } ;
        }
    }

    fn send(&self, packet: &Packet) {
        self.done.lock().insert(packet.id, (packet.a, packet.b, packet.c, packet.d));
    }

    fn call(&self, a: usize, b: usize, c: usize, d: usize) -> usize {
        let id = self.next_id.get();

        //TODO: What should be done about collisions in self.todo or self.done?
        {
            let mut next_id = id + 1;
            if next_id <= 0 {
                next_id = 1;
            }
            self.next_id.set(next_id);
        }

        self.todo.lock().insert(id, (a, b, c, d));

        loop {
            if let Some(regs) = self.done.lock().remove(&id) {
                return regs.0;
            }

            unsafe { context_switch(false) } ;
        }
    }
}

pub struct SchemeResource {
    inner: Weak<SchemeInner>,
    file_id: usize,
}

impl Resource for SchemeResource {
    /// Duplicate the resource
    fn dup(&self) -> Result<Box<Resource>> {
        Err(Error::new(EBADF))
    }

    /// Return the url of this resource
    fn url(&self) -> Url {
        Url::new()
    }

    // TODO: Make use of Write and Read trait
    /// Read data to buffer
    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
        Err(Error::new(EBADF))
    }

    /// Write to resource
    fn write(&mut self, buf: &[u8]) -> Result<usize> {
        Err(Error::new(EBADF))
    }

    /// Seek
    fn seek(&mut self, pos: ResourceSeek) -> Result<usize> {
        Err(Error::new(EBADF))
    }

    /// Sync the resource
    fn sync(&mut self) -> Result<()> {
        Err(Error::new(EBADF))
    }

    fn truncate(&mut self, len: usize) -> Result<()> {
        Err(Error::new(EBADF))
    }
}

pub struct SchemeServerResource {
    inner: Weak<SchemeInner>,
}

impl Resource for SchemeServerResource {
    /// Duplicate the resource
    fn dup(&self) -> Result<Box<Resource>> {
        Ok(box SchemeServerResource {
            inner: self.inner.clone()
        })
    }

    /// Return the url of this resource
    fn url(&self) -> Url {
        if let Some(scheme) = self.inner.upgrade() {
            Url::from_string(":".to_string() + &scheme.name)
        } else {
            Url::new()
        }
    }

    // TODO: Make use of Write and Read trait
    /// Read data to buffer
    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
        if let Some(scheme) = self.inner.upgrade() {
            if buf.len() == size_of::<Packet>() {
                let packet_ptr: *mut Packet = buf.as_mut_ptr() as *mut Packet;
                scheme.recv(unsafe { &mut *packet_ptr });

                Ok(size_of::<Packet>())
            } else {
                Err(Error::new(EINVAL))
            }
        }else {
            Err(Error::new(EBADF))
        }
    }

    /// Write to resource
    fn write(&mut self, buf: &[u8]) -> Result<usize> {
        if let Some(scheme) = self.inner.upgrade() {
            if buf.len() == size_of::<Packet>() {
                let packet_ptr: *const Packet = buf.as_ptr() as *const Packet;
                scheme.send(unsafe { &*packet_ptr });

                Ok(size_of::<Packet>())
            } else {
                Err(Error::new(EINVAL))
            }
        }else {
            Err(Error::new(EBADF))
        }
    }

    /// Seek
    fn seek(&mut self, pos: ResourceSeek) -> Result<usize> {
        if let Some(scheme) = self.inner.upgrade() {
            Err(Error::new(ESPIPE))
        }else {
            Err(Error::new(EBADF))
        }
    }

    /// Sync the resource
    fn sync(&mut self) -> Result<()> {
        if let Some(scheme) = self.inner.upgrade() {
            Err(Error::new(EINVAL))
        }else {
            Err(Error::new(EBADF))
        }
    }

    fn truncate(&mut self, len: usize) -> Result<()> {
        if let Some(scheme) = self.inner.upgrade() {
            Err(Error::new(EINVAL))
        }else {
            Err(Error::new(EBADF))
        }
    }
}

/// Scheme has to be wrapped
pub struct Scheme {
    inner: Arc<SchemeInner>
}

impl Scheme {
    pub fn new(name: String) -> Box<Scheme> {
        box Scheme {
            inner: Arc::new(SchemeInner::new(name))
        }
    }

    pub fn server(&self) -> Box<Resource> {
        box SchemeServerResource {
            inner: Arc::downgrade(&self.inner)
        }
    }
}

impl KScheme for Scheme {
    fn on_irq(&mut self, irq: u8) {

    }

    fn on_poll(&mut self) {

    }

    fn scheme(&self) -> &str {
        &self.inner.name
    }

    fn open(&mut self, url: &Url, flags: usize) -> Result<Box<Resource>> {
        let c_str = url.string.clone() + "\0";
        debugln!("{} open: {}", self.inner.name, self.inner.call(SYS_OPEN, c_str.as_ptr() as usize, 0, 0));

        Err(Error::new(ENOENT))
    }

    fn unlink(&mut self, url: &Url) -> Result<()> {
        Err(Error::new(ENOENT))
    }
}
<commit_msg>Pass file operations and unlink to scheme<commit_after>use alloc::arc::{Arc, Weak};
use alloc::boxed::Box;

use collections::{BTreeMap, String};
use collections::string::ToString;

use core::cell::Cell;
use core::mem::size_of;

use scheduler::context::context_switch;

use schemes::{Result, Resource, ResourceSeek, KScheme, Url};

use sync::Intex;

use system::error::{Error, EBADF, EINVAL, ENOENT, ESPIPE};
use system::scheme::Packet;
use system::syscall::{SYS_FSYNC, SYS_FTRUNCATE, SYS_LSEEK, SEEK_SET, SEEK_CUR, SEEK_END, SYS_OPEN, SYS_READ, SYS_WRITE, SYS_UNLINK};

struct SchemeInner {
    name: String,
    next_id: Cell<usize>,
    todo: Intex<BTreeMap<usize, (usize, usize, usize, usize)>>,
    done: Intex<BTreeMap<usize, (usize, usize, usize, usize)>>,
}

impl SchemeInner {
    fn new(name: String) -> SchemeInner {
        SchemeInner {
            name: name,
            next_id: Cell::new(1),
            todo: Intex::new(BTreeMap::new()),
            done: Intex::new(BTreeMap::new()),
        }
    }

    fn recv(&self, packet: &mut Packet) {
        loop {
            {
                let mut todo = self.todo.lock();

                packet.id = if let Some(id) = todo.keys().next() {
                    *id
                } else {
                    0
                };

                if packet.id > 0 {
                    if let Some(regs) = todo.remove(&packet.id) {
                        packet.a = regs.0;
                        packet.b = regs.1;
                        packet.c = regs.2;
                        packet.d = regs.3;
                        return
                    }
                }
            }

            unsafe { context_switch(false) } ;
        }
    }

    fn send(&self, packet: &Packet) {
        self.done.lock().insert(packet.id, (packet.a, packet.b, packet.c, packet.d));
    }

    fn call(&self, a: usize, b: usize, c: usize, d: usize) -> usize {
        let id = self.next_id.get();

        //TODO: What should be done about collisions in self.todo or self.done?
        {
            let mut next_id = id + 1;
            if next_id <= 0 {
                next_id = 1;
            }
            self.next_id.set(next_id);
        }

        self.todo.lock().insert(id, (a, b, c, d));

        loop {
            if let Some(regs) = self.done.lock().remove(&id) {
                return regs.0;
            }

            unsafe { context_switch(false) } ;
        }
    }
}

pub struct SchemeResource {
    inner: Weak<SchemeInner>,
    file_id: usize,
}

impl Resource for SchemeResource {
    /// Duplicate the resource
    fn dup(&self) -> Result<Box<Resource>> {
        Err(Error::new(EBADF))
    }

    /// Return the url of this resource
    fn url(&self) -> Url {
        Url::new()
    }

    // TODO: Make use of Write and Read trait
    /// Read data to buffer
    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
        if let Some(scheme) = self.inner.upgrade() {
            Error::demux(scheme.call(SYS_READ, self.file_id, buf.as_mut_ptr() as usize, buf.len()))
        } else {
            Err(Error::new(EBADF))
        }
    }

    /// Write to resource
    fn write(&mut self, buf: &[u8]) -> Result<usize> {
        if let Some(scheme) = self.inner.upgrade() {
            Error::demux(scheme.call(SYS_WRITE, self.file_id, buf.as_ptr() as usize, buf.len()))
        } else {
            Err(Error::new(EBADF))
        }
    }

    /// Seek
    fn seek(&mut self, pos: ResourceSeek) -> Result<usize> {
        if let Some(scheme) = self.inner.upgrade() {
            let (whence, offset) = match pos {
                ResourceSeek::Start(offset) => (SEEK_SET, offset as usize),
                ResourceSeek::Current(offset) => (SEEK_CUR, offset as usize),
                ResourceSeek::End(offset) => (SEEK_END, offset as usize)
            };

            Error::demux(scheme.call(SYS_LSEEK, self.file_id, offset, whence))
        } else {
            Err(Error::new(EBADF))
        }
    }

    /// Sync the resource
    fn sync(&mut self) -> Result<()> {
        if let Some(scheme) = self.inner.upgrade() {
            Error::demux(scheme.call(SYS_FSYNC, self.file_id, 0, 0)).and(Ok(()))
        } else {
            Err(Error::new(EBADF))
        }
    }

    fn truncate(&mut self, len: usize) -> Result<()> {
        if let Some(scheme) = self.inner.upgrade() {
            Error::demux(scheme.call(SYS_FTRUNCATE, self.file_id, len, 0)).and(Ok(()))
        } else {
            Err(Error::new(EBADF))
        }
    }
}

pub struct SchemeServerResource {
    inner: Weak<SchemeInner>,
}

impl Resource for SchemeServerResource {
    /// Duplicate the resource
    fn dup(&self) -> Result<Box<Resource>> {
        Ok(box SchemeServerResource {
            inner: self.inner.clone()
        })
    }

    /// Return the url of this resource
    fn url(&self) -> Url {
        if let Some(scheme) = self.inner.upgrade() {
            Url::from_string(":".to_string() + &scheme.name)
        } else {
            Url::new()
        }
    }

    // TODO: Make use of Write and Read trait
    /// Read data to buffer
    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
        if let Some(scheme) = self.inner.upgrade() {
            if buf.len() == size_of::<Packet>() {
                let packet_ptr: *mut Packet = buf.as_mut_ptr() as *mut Packet;
                scheme.recv(unsafe { &mut *packet_ptr });

                Ok(size_of::<Packet>())
            } else {
                Err(Error::new(EINVAL))
            }
        }else {
            Err(Error::new(EBADF))
        }
    }

    /// Write to resource
    fn write(&mut self, buf: &[u8]) -> Result<usize> {
        if let Some(scheme) = self.inner.upgrade() {
            if buf.len() == size_of::<Packet>() {
                let packet_ptr: *const Packet = buf.as_ptr() as *const Packet;
                scheme.send(unsafe { &*packet_ptr });

                Ok(size_of::<Packet>())
            } else {
                Err(Error::new(EINVAL))
            }
        }else {
            Err(Error::new(EBADF))
        }
    }

    /// Seek
    fn seek(&mut self, pos: ResourceSeek) -> Result<usize> {
        if let Some(scheme) = self.inner.upgrade() {
            Err(Error::new(ESPIPE))
        }else {
            Err(Error::new(EBADF))
        }
    }

    /// Sync the resource
    fn sync(&mut self) -> Result<()> {
        if let Some(scheme) = self.inner.upgrade() {
            Err(Error::new(EINVAL))
        }else {
            Err(Error::new(EBADF))
        }
    }

    fn truncate(&mut self, len: usize) -> Result<()> {
        if let Some(scheme) = self.inner.upgrade() {
            Err(Error::new(EINVAL))
        }else {
            Err(Error::new(EBADF))
        }
    }
}

/// Scheme has to be wrapped
pub struct Scheme {
    inner: Arc<SchemeInner>
}

impl Scheme {
    pub fn new(name: String) -> Box<Scheme> {
        box Scheme {
            inner: Arc::new(SchemeInner::new(name))
        }
    }

    pub fn server(&self) -> Box<Resource> {
        box SchemeServerResource {
            inner: Arc::downgrade(&self.inner)
        }
    }
}

impl KScheme for Scheme {
    fn on_irq(&mut self, irq: u8) {

    }

    fn on_poll(&mut self) {

    }

    fn scheme(&self) -> &str {
        &self.inner.name
    }

    fn open(&mut self, url: &Url, flags: usize) -> Result<Box<Resource>> {
        let c_str = url.string.clone() + "\0";
        match Error::demux(self.inner.call(SYS_OPEN, c_str.as_ptr() as usize, 0, 0)) {
            Ok(file_id) => Ok(box SchemeResource {
                inner: Arc::downgrade(&self.inner),
                file_id: file_id,
            }),
            Err(err) => Err(err)
        }
    }

    fn unlink(&mut self, url: &Url) -> Result<()> {
        let c_str = url.string.clone() + "\0";
        Error::demux(self.inner.call(SYS_UNLINK, c_str.as_ptr() as usize, 0, 0)).and(Ok(()))
    }
}
<|endoftext|>


<commit_before>use scaii_defs::{Backend, BackendSupported, Module};
use scaii_defs::protos::{MultiMessage, RustFfiConfig, ScaiiPacket};

use std::error::Error;
use std::ops::{Deref, DerefMut};

use super::LoadedAs;

pub fn init_ffi(args: RustFfiConfig) -> Result<LoadedAs, Box<Error>> {
    use std::env;
    use libloading::Library;
    use scaii_defs::protos::init_as::InitAs;
    use scaii_defs::protos::ModuleInit;

    let mu = &::internal::OPEN_LIBS;
    {
        let mut lib_map = mu.lock()?;
        let plugin_path = format!(
            "{}/.scaii/{}",
            env::home_dir()
                .expect("Could not find home directory")
                .to_string_lossy(),
            args.plugin_path,
        );
        //rclib = rust-call lib
        let lib = lib_map
            .entry(plugin_path.clone())
            .or_insert(Library::new(&plugin_path).expect("Invalid library path"));

        match args.clone()
            .init_as
            .init_as
            .ok_or_else::<Box<Error>, _>(|| {
                From::from(format!("Malformed InitAs field in RustFfi {:?}", args))
            })? {
            InitAs::Backend(_) => {
                let lib = unsafe { lib.get::<fn() -> Box<Backend>>(b"new_backend\0")? };
                Ok(LoadedAs::Backend(Box::new(RustDynamicBackend {
                    backend: lib(),
                    plugin_path: plugin_path,
                })))
            }
            InitAs::Module(ModuleInit { name }) => {
                let lib = unsafe { lib.get::<fn(&str) -> Box<Module>>(b"new\0")? };
                Ok(LoadedAs::Module(
                    Box::new(RustDynamicModule {
                        module: lib(&name),
                        plugin_path: plugin_path,
                    }),
                    name,
                ))
            }
        }
    }
}

/// A backend loaded using FFI and
/// Rust calling conventions. See the
/// `Module` trait in `scaii_defs` for API info.
struct RustDynamicBackend {
    backend: Box<Backend>,
    pub plugin_path: String,
}

impl RustDynamicBackend {}

impl Deref for RustDynamicBackend {
    type Target = Backend;
    fn deref(&self) -> &Self::Target {
        &*self.backend
    }
}

impl DerefMut for RustDynamicBackend {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut *self.backend
    }
}

impl Module for RustDynamicBackend {
    fn process_msg(&mut self, msg: &ScaiiPacket) -> Result<(), Box<Error>> {
        self.backend.process_msg(msg)
    }

    fn get_messages(&mut self) -> MultiMessage {
        self.backend.get_messages()
    }
}

impl Backend for RustDynamicBackend {
    fn supported_behavior(&self) -> BackendSupported {
        self.backend.supported_behavior()
    }

    fn serialize(&mut self, into: Option<Vec<u8>>) -> Result<Vec<u8>, Box<Error>> {
        self.backend.serialize(into)
    }

    fn deserialize(&mut self, buf: &[u8]) -> Result<(), Box<Error>> {
        self.backend.deserialize(buf)
    }

    fn serialize_diverging(&mut self, into: Option<Vec<u8>>) -> Result<Vec<u8>, Box<Error>> {
        self.backend.serialize_diverging(into)
    }

    fn deserialize_diverging(&mut self, buf: &[u8]) -> Result<(), Box<Error>> {
        self.backend.deserialize_diverging(buf)
    }
}

struct RustDynamicModule {
    module: Box<Module>,
    pub plugin_path: String,
}

impl Deref for RustDynamicModule {
    type Target = Module;
    fn deref(&self) -> &Self::Target {
        &*self.module
    }
}

impl DerefMut for RustDynamicModule {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut *self.module
    }
}

impl Module for RustDynamicModule {
    fn process_msg(&mut self, msg: &ScaiiPacket) -> Result<(), Box<Error>> {
        self.module.process_msg(msg)
    }

    fn get_messages(&mut self) -> MultiMessage {
        self.module.get_messages()
    }
}

#[test]
fn load_destroy() {
    use scaii_defs::protos::{BackendInit, InitAs};
    use scaii_defs::protos::init_as;

    let args = RustFfiConfig {
        init_as: InitAs {
            init_as: Some(init_as::InitAs::Backend(BackendInit {})),
        },
        plugin_path: "../../sky-rts/backend/target/debug/backend.dll".to_string(),
    };

    init_ffi(args).unwrap();

    println!("Initialized")
}
<commit_msg>comment out test for now<commit_after>use scaii_defs::{Backend, BackendSupported, Module};
use scaii_defs::protos::{MultiMessage, RustFfiConfig, ScaiiPacket};

use std::error::Error;
use std::ops::{Deref, DerefMut};

use super::LoadedAs;

pub fn init_ffi(args: RustFfiConfig) -> Result<LoadedAs, Box<Error>> {
    use std::env;
    use libloading::Library;
    use scaii_defs::protos::init_as::InitAs;
    use scaii_defs::protos::ModuleInit;

    let mu = &::internal::OPEN_LIBS;
    {
        let mut lib_map = mu.lock()?;
        let plugin_path = format!(
            "{}/.scaii/{}",
            env::home_dir()
                .expect("Could not find home directory")
                .to_string_lossy(),
            args.plugin_path,
        );
        //rclib = rust-call lib
        let lib = lib_map
            .entry(plugin_path.clone())
            .or_insert(Library::new(&plugin_path).expect("Invalid library path"));

        match args.clone()
            .init_as
            .init_as
            .ok_or_else::<Box<Error>, _>(|| {
                From::from(format!("Malformed InitAs field in RustFfi {:?}", args))
            })? {
            InitAs::Backend(_) => {
                let lib = unsafe { lib.get::<fn() -> Box<Backend>>(b"new_backend\0")? };
                Ok(LoadedAs::Backend(Box::new(RustDynamicBackend {
                    backend: lib(),
                    plugin_path: plugin_path,
                })))
            }
            InitAs::Module(ModuleInit { name }) => {
                let lib = unsafe { lib.get::<fn(&str) -> Box<Module>>(b"new\0")? };
                Ok(LoadedAs::Module(
                    Box::new(RustDynamicModule {
                        module: lib(&name),
                        plugin_path: plugin_path,
                    }),
                    name,
                ))
            }
        }
    }
}

/// A backend loaded using FFI and
/// Rust calling conventions. See the
/// `Module` trait in `scaii_defs` for API info.
struct RustDynamicBackend {
    backend: Box<Backend>,
    pub plugin_path: String,
}

impl RustDynamicBackend {}

impl Deref for RustDynamicBackend {
    type Target = Backend;
    fn deref(&self) -> &Self::Target {
        &*self.backend
    }
}

impl DerefMut for RustDynamicBackend {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut *self.backend
    }
}

impl Module for RustDynamicBackend {
    fn process_msg(&mut self, msg: &ScaiiPacket) -> Result<(), Box<Error>> {
        self.backend.process_msg(msg)
    }

    fn get_messages(&mut self) -> MultiMessage {
        self.backend.get_messages()
    }
}

impl Backend for RustDynamicBackend {
    fn supported_behavior(&self) -> BackendSupported {
        self.backend.supported_behavior()
    }

    fn serialize(&mut self, into: Option<Vec<u8>>) -> Result<Vec<u8>, Box<Error>> {
        self.backend.serialize(into)
    }

    fn deserialize(&mut self, buf: &[u8]) -> Result<(), Box<Error>> {
        self.backend.deserialize(buf)
    }

    fn serialize_diverging(&mut self, into: Option<Vec<u8>>) -> Result<Vec<u8>, Box<Error>> {
        self.backend.serialize_diverging(into)
    }

    fn deserialize_diverging(&mut self, buf: &[u8]) -> Result<(), Box<Error>> {
        self.backend.deserialize_diverging(buf)
    }
}

struct RustDynamicModule {
    module: Box<Module>,
    pub plugin_path: String,
}

impl Deref for RustDynamicModule {
    type Target = Module;
    fn deref(&self) -> &Self::Target {
        &*self.module
    }
}

impl DerefMut for RustDynamicModule {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut *self.module
    }
}

impl Module for RustDynamicModule {
    fn process_msg(&mut self, msg: &ScaiiPacket) -> Result<(), Box<Error>> {
        self.module.process_msg(msg)
    }

    fn get_messages(&mut self) -> MultiMessage {
        self.module.get_messages()
    }
}

// #[test]
// fn load_destroy() {
//     use scaii_defs::protos::{BackendInit, InitAs};
//     use scaii_defs::protos::init_as;

//     let args = RustFfiConfig {
//         init_as: InitAs {
//             init_as: Some(init_as::InitAs::Backend(BackendInit {})),
//         },
//         plugin_path: "../../../Sky-RTS/backend/target/debug/backend.dll".to_string(),
//     };

//     init_ffi(args).unwrap();

//     println!("Initialized")
// }
<|endoftext|>


<commit_before>//! VFS stands for Virtual File System.
//!
//! When doing analysis, we don't want to do any IO, we want to keep all source
//! code in memory. However, the actual source code is stored on disk, so you
//! need to get it into the memory in the first place somehow. VFS is the
//! component which does this.
//!
//! It also is responsible for watching the disk for changes, and for merging
//! editor state (modified, unsaved files) with disk state.
//!
//! VFS is based on a concept of roots: a set of directories on the file system
//! whihc are watched for changes. Typically, there will be a root for each
//! Cargo package.
mod arena;
mod io;

use std::{
    fmt,
    mem,
    thread,
    cmp::Reverse,
    path::{Path, PathBuf},
    ffi::OsStr,
    sync::Arc,
    fs,
};

use rustc_hash::{FxHashMap, FxHashSet};
use relative_path::RelativePathBuf;
use crossbeam_channel::Receiver;
use walkdir::DirEntry;
use thread_worker::{WorkerHandle};

use crate::{
    arena::{ArenaId, Arena},
};

pub use crate::io::TaskResult as VfsTask;

/// `RootFilter` is a predicate that checks if a file can belong to a root. If
/// several filters match a file (nested dirs), the most nested one wins.
struct RootFilter {
    root: PathBuf,
    file_filter: fn(&Path) -> bool,
}

impl RootFilter {
    fn new(root: PathBuf) -> RootFilter {
        RootFilter {
            root,
            file_filter: has_rs_extension,
        }
    }
    /// Check if this root can contain `path`. NB: even if this returns
    /// true, the `path` might actually be conained in some nested root.
    fn can_contain(&self, path: &Path) -> Option<RelativePathBuf> {
        if !(self.file_filter)(path) {
            return None;
        }
        if !(path.starts_with(&self.root)) {
            return None;
        }
        let path = path.strip_prefix(&self.root).unwrap();
        let path = RelativePathBuf::from_path(path).unwrap();
        Some(path)
    }
}

fn has_rs_extension(p: &Path) -> bool {
    p.extension() == Some(OsStr::new("rs"))
}

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

impl ArenaId for VfsRoot {
    fn from_u32(idx: u32) -> VfsRoot {
        VfsRoot(idx)
    }
    fn to_u32(self) -> u32 {
        self.0
    }
}

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

impl ArenaId for VfsFile {
    fn from_u32(idx: u32) -> VfsFile {
        VfsFile(idx)
    }
    fn to_u32(self) -> u32 {
        self.0
    }
}

struct VfsFileData {
    root: VfsRoot,
    path: RelativePathBuf,
    text: Arc<String>,
}

pub struct Vfs {
    roots: Arena<VfsRoot, RootFilter>,
    files: Arena<VfsFile, VfsFileData>,
    root2files: FxHashMap<VfsRoot, FxHashSet<VfsFile>>,
    pending_changes: Vec<VfsChange>,
    worker: io::Worker,
    worker_handle: WorkerHandle,
}

impl fmt::Debug for Vfs {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str("Vfs { ... }")
    }
}

impl Vfs {
    pub fn new(mut roots: Vec<PathBuf>) -> (Vfs, Vec<VfsRoot>) {
        let (worker, worker_handle) = io::start();

        let mut res = Vfs {
            roots: Arena::default(),
            files: Arena::default(),
            root2files: FxHashMap::default(),
            worker,
            worker_handle,
            pending_changes: Vec::new(),
        };

        // A hack to make nesting work.
        roots.sort_by_key(|it| Reverse(it.as_os_str().len()));
        for (i, path) in roots.iter().enumerate() {
            let root = res.roots.alloc(RootFilter::new(path.clone()));
            res.root2files.insert(root, Default::default());
            let nested = roots[..i]
                .iter()
                .filter(|it| it.starts_with(path))
                .map(|it| it.clone())
                .collect::<Vec<_>>();
            let filter = move |entry: &DirEntry| {
                if entry.file_type().is_file() {
                    has_rs_extension(entry.path())
                } else {
                    nested.iter().all(|it| it != entry.path())
                }
            };
            let task = io::Task {
                root,
                path: path.clone(),
                filter: Box::new(filter),
            };
            res.worker.inp.send(task);
        }
        let roots = res.roots.iter().map(|(id, _)| id).collect();
        (res, roots)
    }

    pub fn root2path(&self, root: VfsRoot) -> PathBuf {
        self.roots[root].root.clone()
    }

    pub fn path2file(&self, path: &Path) -> Option<VfsFile> {
        if let Some((_root, _path, Some(file))) = self.find_root(path) {
            return Some(file);
        }
        None
    }

    pub fn file2path(&self, file: VfsFile) -> PathBuf {
        let rel_path = &self.files[file].path;
        let root_path = &self.roots[self.files[file].root].root;
        rel_path.to_path(root_path)
    }

    pub fn file_for_path(&self, path: &Path) -> Option<VfsFile> {
        if let Some((_root, _path, Some(file))) = self.find_root(path) {
            return Some(file);
        }
        None
    }

    pub fn load(&mut self, path: &Path) -> Option<VfsFile> {
        if let Some((root, rel_path, file)) = self.find_root(path) {
            return if let Some(file) = file {
                Some(file)
            } else {
                let text = fs::read_to_string(path).unwrap_or_default();
                let text = Arc::new(text);
                let file = self.add_file(root, rel_path.clone(), Arc::clone(&text));
                let change = VfsChange::AddFile {
                    file,
                    text,
                    root,
                    path: rel_path,
                };
                self.pending_changes.push(change);
                Some(file)
            };
        }
        None
    }

    pub fn task_receiver(&self) -> &Receiver<io::TaskResult> {
        &self.worker.out
    }

    pub fn handle_task(&mut self, task: io::TaskResult) {
        let mut files = Vec::new();
        // While we were scanning the root in the backgound, a file might have
        // been open in the editor, so we need to account for that.
        let exising = self.root2files[&task.root]
            .iter()
            .map(|&file| (self.files[file].path.clone(), file))
            .collect::<FxHashMap<_, _>>();
        for (path, text) in task.files {
            if let Some(&file) = exising.get(&path) {
                let text = Arc::clone(&self.files[file].text);
                files.push((file, path, text));
                continue;
            }
            let text = Arc::new(text);
            let file = self.add_file(task.root, path.clone(), Arc::clone(&text));
            files.push((file, path, text));
        }

        let change = VfsChange::AddRoot {
            root: task.root,
            files,
        };
        self.pending_changes.push(change);
    }

    pub fn add_file_overlay(&mut self, path: &Path, text: String) -> Option<VfsFile> {
        let mut res = None;
        if let Some((root, path, file)) = self.find_root(path) {
            let text = Arc::new(text);
            let change = if let Some(file) = file {
                res = Some(file);
                self.change_file(file, Arc::clone(&text));
                VfsChange::ChangeFile { file, text }
            } else {
                let file = self.add_file(root, path.clone(), Arc::clone(&text));
                res = Some(file);
                VfsChange::AddFile {
                    file,
                    text,
                    root,
                    path,
                }
            };
            self.pending_changes.push(change);
        }
        res
    }

    pub fn change_file_overlay(&mut self, path: &Path, new_text: String) {
        if let Some((_root, _path, file)) = self.find_root(path) {
            let file = file.expect("can't change a file which wasn't added");
            let text = Arc::new(new_text);
            self.change_file(file, Arc::clone(&text));
            let change = VfsChange::ChangeFile { file, text };
            self.pending_changes.push(change);
        }
    }

    pub fn remove_file_overlay(&mut self, path: &Path) -> Option<VfsFile> {
        let mut res = None;
        if let Some((root, path, file)) = self.find_root(path) {
            let file = file.expect("can't remove a file which wasn't added");
            res = Some(file);
            let full_path = path.to_path(&self.roots[root].root);
            let change = if let Ok(text) = fs::read_to_string(&full_path) {
                let text = Arc::new(text);
                self.change_file(file, Arc::clone(&text));
                VfsChange::ChangeFile { file, text }
            } else {
                self.remove_file(file);
                VfsChange::RemoveFile { root, file, path }
            };
            self.pending_changes.push(change);
        }
        res
    }

    pub fn commit_changes(&mut self) -> Vec<VfsChange> {
        mem::replace(&mut self.pending_changes, Vec::new())
    }

    /// Sutdown the VFS and terminate the background watching thread.
    pub fn shutdown(self) -> thread::Result<()> {
        let _ = self.worker.shutdown();
        self.worker_handle.shutdown()
    }

    fn add_file(&mut self, root: VfsRoot, path: RelativePathBuf, text: Arc<String>) -> VfsFile {
        let data = VfsFileData { root, path, text };
        let file = self.files.alloc(data);
        self.root2files.get_mut(&root).unwrap().insert(file);
        file
    }

    fn change_file(&mut self, file: VfsFile, new_text: Arc<String>) {
        self.files[file].text = new_text;
    }

    fn remove_file(&mut self, file: VfsFile) {
        //FIXME: use arena with removal
        self.files[file].text = Default::default();
        self.files[file].path = Default::default();
        let root = self.files[file].root;
        let removed = self.root2files.get_mut(&root).unwrap().remove(&file);
        assert!(removed);
    }

    fn find_root(&self, path: &Path) -> Option<(VfsRoot, RelativePathBuf, Option<VfsFile>)> {
        let (root, path) = self
            .roots
            .iter()
            .find_map(|(root, data)| data.can_contain(path).map(|it| (root, it)))?;
        let file = self.root2files[&root]
            .iter()
            .map(|&it| it)
            .find(|&file| self.files[file].path == path);
        Some((root, path, file))
    }
}

#[derive(Debug, Clone)]
pub enum VfsChange {
    AddRoot {
        root: VfsRoot,
        files: Vec<(VfsFile, RelativePathBuf, Arc<String>)>,
    },
    AddFile {
        root: VfsRoot,
        file: VfsFile,
        path: RelativePathBuf,
        text: Arc<String>,
    },
    RemoveFile {
        root: VfsRoot,
        file: VfsFile,
        path: RelativePathBuf,
    },
    ChangeFile {
        file: VfsFile,
        text: Arc<String>,
    },
}
<commit_msg>Improve comment contents<commit_after>//! VFS stands for Virtual File System.
//!
//! When doing analysis, we don't want to do any IO, we want to keep all source
//! code in memory. However, the actual source code is stored on disk, so you
//! need to get it into the memory in the first place somehow. VFS is the
//! component which does this.
//!
//! It is also responsible for watching the disk for changes, and for merging
//! editor state (modified, unsaved files) with disk state.
//! TODO: Some LSP clients support watching the disk, so this crate should
//! to support custom watcher events (related to https://github.com/rust-analyzer/rust-analyzer/issues/131)
//!
//! VFS is based on a concept of roots: a set of directories on the file system
//! whihc are watched for changes. Typically, there will be a root for each
//! Cargo package.
mod arena;
mod io;

use std::{
    fmt,
    mem,
    thread,
    cmp::Reverse,
    path::{Path, PathBuf},
    ffi::OsStr,
    sync::Arc,
    fs,
};

use rustc_hash::{FxHashMap, FxHashSet};
use relative_path::RelativePathBuf;
use crossbeam_channel::Receiver;
use walkdir::DirEntry;
use thread_worker::{WorkerHandle};

use crate::{
    arena::{ArenaId, Arena},
};

pub use crate::io::TaskResult as VfsTask;

/// `RootFilter` is a predicate that checks if a file can belong to a root. If
/// several filters match a file (nested dirs), the most nested one wins.
struct RootFilter {
    root: PathBuf,
    file_filter: fn(&Path) -> bool,
}

impl RootFilter {
    fn new(root: PathBuf) -> RootFilter {
        RootFilter {
            root,
            file_filter: has_rs_extension,
        }
    }
    /// Check if this root can contain `path`. NB: even if this returns
    /// true, the `path` might actually be conained in some nested root.
    fn can_contain(&self, path: &Path) -> Option<RelativePathBuf> {
        if !(self.file_filter)(path) {
            return None;
        }
        if !(path.starts_with(&self.root)) {
            return None;
        }
        let path = path.strip_prefix(&self.root).unwrap();
        let path = RelativePathBuf::from_path(path).unwrap();
        Some(path)
    }
}

fn has_rs_extension(p: &Path) -> bool {
    p.extension() == Some(OsStr::new("rs"))
}

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

impl ArenaId for VfsRoot {
    fn from_u32(idx: u32) -> VfsRoot {
        VfsRoot(idx)
    }
    fn to_u32(self) -> u32 {
        self.0
    }
}

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

impl ArenaId for VfsFile {
    fn from_u32(idx: u32) -> VfsFile {
        VfsFile(idx)
    }
    fn to_u32(self) -> u32 {
        self.0
    }
}

struct VfsFileData {
    root: VfsRoot,
    path: RelativePathBuf,
    text: Arc<String>,
}

pub struct Vfs {
    roots: Arena<VfsRoot, RootFilter>,
    files: Arena<VfsFile, VfsFileData>,
    root2files: FxHashMap<VfsRoot, FxHashSet<VfsFile>>,
    pending_changes: Vec<VfsChange>,
    worker: io::Worker,
    worker_handle: WorkerHandle,
}

impl fmt::Debug for Vfs {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str("Vfs { ... }")
    }
}

impl Vfs {
    pub fn new(mut roots: Vec<PathBuf>) -> (Vfs, Vec<VfsRoot>) {
        let (worker, worker_handle) = io::start();

        let mut res = Vfs {
            roots: Arena::default(),
            files: Arena::default(),
            root2files: FxHashMap::default(),
            worker,
            worker_handle,
            pending_changes: Vec::new(),
        };

        // A hack to make nesting work.
        roots.sort_by_key(|it| Reverse(it.as_os_str().len()));
        for (i, path) in roots.iter().enumerate() {
            let root = res.roots.alloc(RootFilter::new(path.clone()));
            res.root2files.insert(root, Default::default());
            let nested = roots[..i]
                .iter()
                .filter(|it| it.starts_with(path))
                .map(|it| it.clone())
                .collect::<Vec<_>>();
            let filter = move |entry: &DirEntry| {
                if entry.file_type().is_file() {
                    has_rs_extension(entry.path())
                } else {
                    nested.iter().all(|it| it != entry.path())
                }
            };
            let task = io::Task {
                root,
                path: path.clone(),
                filter: Box::new(filter),
            };
            res.worker.inp.send(task);
        }
        let roots = res.roots.iter().map(|(id, _)| id).collect();
        (res, roots)
    }

    pub fn root2path(&self, root: VfsRoot) -> PathBuf {
        self.roots[root].root.clone()
    }

    pub fn path2file(&self, path: &Path) -> Option<VfsFile> {
        if let Some((_root, _path, Some(file))) = self.find_root(path) {
            return Some(file);
        }
        None
    }

    pub fn file2path(&self, file: VfsFile) -> PathBuf {
        let rel_path = &self.files[file].path;
        let root_path = &self.roots[self.files[file].root].root;
        rel_path.to_path(root_path)
    }

    pub fn file_for_path(&self, path: &Path) -> Option<VfsFile> {
        if let Some((_root, _path, Some(file))) = self.find_root(path) {
            return Some(file);
        }
        None
    }

    pub fn load(&mut self, path: &Path) -> Option<VfsFile> {
        if let Some((root, rel_path, file)) = self.find_root(path) {
            return if let Some(file) = file {
                Some(file)
            } else {
                let text = fs::read_to_string(path).unwrap_or_default();
                let text = Arc::new(text);
                let file = self.add_file(root, rel_path.clone(), Arc::clone(&text));
                let change = VfsChange::AddFile {
                    file,
                    text,
                    root,
                    path: rel_path,
                };
                self.pending_changes.push(change);
                Some(file)
            };
        }
        None
    }

    pub fn task_receiver(&self) -> &Receiver<io::TaskResult> {
        &self.worker.out
    }

    pub fn handle_task(&mut self, task: io::TaskResult) {
        let mut files = Vec::new();
        // While we were scanning the root in the backgound, a file might have
        // been open in the editor, so we need to account for that.
        let exising = self.root2files[&task.root]
            .iter()
            .map(|&file| (self.files[file].path.clone(), file))
            .collect::<FxHashMap<_, _>>();
        for (path, text) in task.files {
            if let Some(&file) = exising.get(&path) {
                let text = Arc::clone(&self.files[file].text);
                files.push((file, path, text));
                continue;
            }
            let text = Arc::new(text);
            let file = self.add_file(task.root, path.clone(), Arc::clone(&text));
            files.push((file, path, text));
        }

        let change = VfsChange::AddRoot {
            root: task.root,
            files,
        };
        self.pending_changes.push(change);
    }

    pub fn add_file_overlay(&mut self, path: &Path, text: String) -> Option<VfsFile> {
        let mut res = None;
        if let Some((root, path, file)) = self.find_root(path) {
            let text = Arc::new(text);
            let change = if let Some(file) = file {
                res = Some(file);
                self.change_file(file, Arc::clone(&text));
                VfsChange::ChangeFile { file, text }
            } else {
                let file = self.add_file(root, path.clone(), Arc::clone(&text));
                res = Some(file);
                VfsChange::AddFile {
                    file,
                    text,
                    root,
                    path,
                }
            };
            self.pending_changes.push(change);
        }
        res
    }

    pub fn change_file_overlay(&mut self, path: &Path, new_text: String) {
        if let Some((_root, _path, file)) = self.find_root(path) {
            let file = file.expect("can't change a file which wasn't added");
            let text = Arc::new(new_text);
            self.change_file(file, Arc::clone(&text));
            let change = VfsChange::ChangeFile { file, text };
            self.pending_changes.push(change);
        }
    }

    pub fn remove_file_overlay(&mut self, path: &Path) -> Option<VfsFile> {
        let mut res = None;
        if let Some((root, path, file)) = self.find_root(path) {
            let file = file.expect("can't remove a file which wasn't added");
            res = Some(file);
            let full_path = path.to_path(&self.roots[root].root);
            let change = if let Ok(text) = fs::read_to_string(&full_path) {
                let text = Arc::new(text);
                self.change_file(file, Arc::clone(&text));
                VfsChange::ChangeFile { file, text }
            } else {
                self.remove_file(file);
                VfsChange::RemoveFile { root, file, path }
            };
            self.pending_changes.push(change);
        }
        res
    }

    pub fn commit_changes(&mut self) -> Vec<VfsChange> {
        mem::replace(&mut self.pending_changes, Vec::new())
    }

    /// Sutdown the VFS and terminate the background watching thread.
    pub fn shutdown(self) -> thread::Result<()> {
        let _ = self.worker.shutdown();
        self.worker_handle.shutdown()
    }

    fn add_file(&mut self, root: VfsRoot, path: RelativePathBuf, text: Arc<String>) -> VfsFile {
        let data = VfsFileData { root, path, text };
        let file = self.files.alloc(data);
        self.root2files.get_mut(&root).unwrap().insert(file);
        file
    }

    fn change_file(&mut self, file: VfsFile, new_text: Arc<String>) {
        self.files[file].text = new_text;
    }

    fn remove_file(&mut self, file: VfsFile) {
        //FIXME: use arena with removal
        self.files[file].text = Default::default();
        self.files[file].path = Default::default();
        let root = self.files[file].root;
        let removed = self.root2files.get_mut(&root).unwrap().remove(&file);
        assert!(removed);
    }

    fn find_root(&self, path: &Path) -> Option<(VfsRoot, RelativePathBuf, Option<VfsFile>)> {
        let (root, path) = self
            .roots
            .iter()
            .find_map(|(root, data)| data.can_contain(path).map(|it| (root, it)))?;
        let file = self.root2files[&root]
            .iter()
            .map(|&it| it)
            .find(|&file| self.files[file].path == path);
        Some((root, path, file))
    }
}

#[derive(Debug, Clone)]
pub enum VfsChange {
    AddRoot {
        root: VfsRoot,
        files: Vec<(VfsFile, RelativePathBuf, Arc<String>)>,
    },
    AddFile {
        root: VfsRoot,
        file: VfsFile,
        path: RelativePathBuf,
        text: Arc<String>,
    },
    RemoveFile {
        root: VfsRoot,
        file: VfsFile,
        path: RelativePathBuf,
    },
    ChangeFile {
        file: VfsFile,
        text: Arc<String>,
    },
}
<|endoftext|>


<commit_before>//! cargo_check provides the functionality needed to run `cargo check` or
//! another compatible command (f.x. clippy) in a background thread and provide
//! LSP diagnostics based on the output of the command.
mod conv;

use std::{
    error, fmt,
    io::{BufRead, BufReader},
    path::{Path, PathBuf},
    process::{Command, Stdio},
    time::Instant,
};

use cargo_metadata::Message;
use crossbeam_channel::{never, select, unbounded, Receiver, RecvError, Sender};
use lsp_types::{
    CodeAction, CodeActionOrCommand, Diagnostic, Url, WorkDoneProgress, WorkDoneProgressBegin,
    WorkDoneProgressEnd, WorkDoneProgressReport,
};

use crate::conv::{map_rust_diagnostic_to_lsp, MappedRustDiagnostic};

pub use crate::conv::url_from_path_with_drive_lowercasing;

#[derive(Clone, Debug)]
pub struct CheckConfig {
    pub args: Vec<String>,
    pub command: String,
    pub all_targets: bool,
}

/// Flycheck wraps the shared state and communication machinery used for
/// running `cargo check` (or other compatible command) and providing
/// diagnostics based on the output.
/// The spawned thread is shut down when this struct is dropped.
#[derive(Debug)]
pub struct Flycheck {
    // XXX: drop order is significant
    cmd_send: Sender<CheckCommand>,
    handle: Option<jod_thread::JoinHandle<()>>,
    pub task_recv: Receiver<CheckTask>,
}

impl Flycheck {
    pub fn new(config: CheckConfig, workspace_root: PathBuf) -> Flycheck {
        let (task_send, task_recv) = unbounded::<CheckTask>();
        let (cmd_send, cmd_recv) = unbounded::<CheckCommand>();
        let handle = jod_thread::spawn(move || {
            let mut check = FlycheckThread::new(config, workspace_root);
            check.run(&task_send, &cmd_recv);
        });
        Flycheck { task_recv, cmd_send, handle: Some(handle) }
    }

    /// Schedule a re-start of the cargo check worker.
    pub fn update(&self) {
        self.cmd_send.send(CheckCommand::Update).unwrap();
    }
}

#[derive(Debug)]
pub enum CheckTask {
    /// Request a clearing of all cached diagnostics from the check watcher
    ClearDiagnostics,

    /// Request adding a diagnostic with fixes included to a file
    AddDiagnostic { url: Url, diagnostic: Diagnostic, fixes: Vec<CodeActionOrCommand> },

    /// Request check progress notification to client
    Status(WorkDoneProgress),
}

pub enum CheckCommand {
    /// Request re-start of check thread
    Update,
}

struct FlycheckThread {
    options: CheckConfig,
    workspace_root: PathBuf,
    last_update_req: Option<Instant>,
    // XXX: drop order is significant
    message_recv: Receiver<CheckEvent>,
    /// WatchThread exists to wrap around the communication needed to be able to
    /// run `cargo check` without blocking. Currently the Rust standard library
    /// doesn't provide a way to read sub-process output without blocking, so we
    /// have to wrap sub-processes output handling in a thread and pass messages
    /// back over a channel.
    check_process: Option<jod_thread::JoinHandle<()>>,
}

impl FlycheckThread {
    fn new(options: CheckConfig, workspace_root: PathBuf) -> FlycheckThread {
        FlycheckThread {
            options,
            workspace_root,
            last_update_req: None,
            message_recv: never(),
            check_process: None,
        }
    }

    fn run(&mut self, task_send: &Sender<CheckTask>, cmd_recv: &Receiver<CheckCommand>) {
        // If we rerun the thread, we need to discard the previous check results first
        self.clean_previous_results(task_send);

        loop {
            select! {
                recv(&cmd_recv) -> cmd => match cmd {
                    Ok(cmd) => self.handle_command(cmd),
                    Err(RecvError) => {
                        // Command channel has closed, so shut down
                        break;
                    },
                },
                recv(self.message_recv) -> msg => match msg {
                    Ok(msg) => self.handle_message(msg, task_send),
                    Err(RecvError) => {
                        // Watcher finished, replace it with a never channel to
                        // avoid busy-waiting.
                        self.message_recv = never();
                        self.check_process = None;
                    },
                }
            };

            if self.should_recheck() {
                self.last_update_req = None;
                task_send.send(CheckTask::ClearDiagnostics).unwrap();
                self.restart_check_process();
            }
        }
    }

    fn clean_previous_results(&self, task_send: &Sender<CheckTask>) {
        task_send.send(CheckTask::ClearDiagnostics).unwrap();
        task_send
            .send(CheckTask::Status(WorkDoneProgress::End(WorkDoneProgressEnd { message: None })))
            .unwrap();
    }

    fn should_recheck(&mut self) -> bool {
        if let Some(_last_update_req) = &self.last_update_req {
            // We currently only request an update on save, as we need up to
            // date source on disk for cargo check to do it's magic, so we
            // don't really need to debounce the requests at this point.
            return true;
        }
        false
    }

    fn handle_command(&mut self, cmd: CheckCommand) {
        match cmd {
            CheckCommand::Update => self.last_update_req = Some(Instant::now()),
        }
    }

    fn handle_message(&self, msg: CheckEvent, task_send: &Sender<CheckTask>) {
        match msg {
            CheckEvent::Begin => {
                task_send
                    .send(CheckTask::Status(WorkDoneProgress::Begin(WorkDoneProgressBegin {
                        title: "Running 'cargo check'".to_string(),
                        cancellable: Some(false),
                        message: None,
                        percentage: None,
                    })))
                    .unwrap();
            }

            CheckEvent::End => {
                task_send
                    .send(CheckTask::Status(WorkDoneProgress::End(WorkDoneProgressEnd {
                        message: None,
                    })))
                    .unwrap();
            }

            CheckEvent::Msg(Message::CompilerArtifact(msg)) => {
                task_send
                    .send(CheckTask::Status(WorkDoneProgress::Report(WorkDoneProgressReport {
                        cancellable: Some(false),
                        message: Some(msg.target.name),
                        percentage: None,
                    })))
                    .unwrap();
            }

            CheckEvent::Msg(Message::CompilerMessage(msg)) => {
                let map_result = map_rust_diagnostic_to_lsp(&msg.message, &self.workspace_root);
                if map_result.is_empty() {
                    return;
                }

                for MappedRustDiagnostic { location, diagnostic, fixes } in map_result {
                    let fixes = fixes
                        .into_iter()
                        .map(|fix| {
                            CodeAction { diagnostics: Some(vec![diagnostic.clone()]), ..fix }.into()
                        })
                        .collect();

                    task_send
                        .send(CheckTask::AddDiagnostic { url: location.uri, diagnostic, fixes })
                        .unwrap();
                }
            }

            CheckEvent::Msg(Message::BuildScriptExecuted(_msg)) => {}
            CheckEvent::Msg(Message::Unknown) => {}
        }
    }

    fn restart_check_process(&mut self) {
        // First, clear and cancel the old thread
        self.message_recv = never();
        self.check_process = None;

        let mut args: Vec<String> = vec![
            self.options.command.clone(),
            "--workspace".to_string(),
            "--message-format=json".to_string(),
            "--manifest-path".to_string(),
            format!("{}/Cargo.toml", self.workspace_root.display()),
        ];
        if self.options.all_targets {
            args.push("--all-targets".to_string());
        }
        args.extend(self.options.args.iter().cloned());

        let (message_send, message_recv) = unbounded();
        let workspace_root = self.workspace_root.to_owned();
        self.message_recv = message_recv;
        self.check_process = Some(jod_thread::spawn(move || {
            // If we trigger an error here, we will do so in the loop instead,
            // which will break out of the loop, and continue the shutdown
            let _ = message_send.send(CheckEvent::Begin);

            let res = run_cargo(&args, Some(&workspace_root), &mut |message| {
                // Skip certain kinds of messages to only spend time on what's useful
                match &message {
                    Message::CompilerArtifact(artifact) if artifact.fresh => return true,
                    Message::BuildScriptExecuted(_) => return true,
                    Message::Unknown => return true,
                    _ => {}
                }

                // if the send channel was closed, we want to shutdown
                message_send.send(CheckEvent::Msg(message)).is_ok()
            });

            if let Err(err) = res {
                // FIXME: make the `message_send` to be `Sender<Result<CheckEvent, CargoError>>`
                // to display user-caused misconfiguration errors instead of just logging them here
                log::error!("Cargo watcher failed {:?}", err);
            }

            // We can ignore any error here, as we are already in the progress
            // of shutting down.
            let _ = message_send.send(CheckEvent::End);
        }))
    }
}

#[derive(Debug)]
pub struct DiagnosticWithFixes {
    diagnostic: Diagnostic,
    fixes: Vec<CodeAction>,
}

enum CheckEvent {
    Begin,
    Msg(cargo_metadata::Message),
    End,
}

#[derive(Debug)]
pub struct CargoError(String);

impl fmt::Display for CargoError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Cargo failed: {}", self.0)
    }
}
impl error::Error for CargoError {}

fn run_cargo(
    args: &[String],
    current_dir: Option<&Path>,
    on_message: &mut dyn FnMut(cargo_metadata::Message) -> bool,
) -> Result<(), CargoError> {
    let mut command = Command::new("cargo");
    if let Some(current_dir) = current_dir {
        command.current_dir(current_dir);
    }

    let mut child = command
        .args(args)
        .stdout(Stdio::piped())
        .stderr(Stdio::null())
        .stdin(Stdio::null())
        .spawn()
        .expect("couldn't launch cargo");

    // We manually read a line at a time, instead of using serde's
    // stream deserializers, because the deserializer cannot recover
    // from an error, resulting in it getting stuck, because we try to
    // be resillient against failures.
    //
    // Because cargo only outputs one JSON object per line, we can
    // simply skip a line if it doesn't parse, which just ignores any
    // erroneus output.
    let stdout = BufReader::new(child.stdout.take().unwrap());
    let mut read_at_least_one_message = false;

    for line in stdout.lines() {
        let line = match line {
            Ok(line) => line,
            Err(err) => {
                log::error!("Couldn't read line from cargo: {}", err);
                continue;
            }
        };

        let message = serde_json::from_str::<cargo_metadata::Message>(&line);
        let message = match message {
            Ok(message) => message,
            Err(err) => {
                log::error!("Invalid json from cargo check, ignoring ({}): {:?} ", err, line);
                continue;
            }
        };

        read_at_least_one_message = true;

        if !on_message(message) {
            break;
        }
    }

    // It is okay to ignore the result, as it only errors if the process is already dead
    let _ = child.kill();

    let err_msg = match child.wait() {
        Ok(exit_code) if !exit_code.success() && !read_at_least_one_message => {
            // FIXME: Read the stderr to display the reason, see `read2()` reference in PR comment:
            // https://github.com/rust-analyzer/rust-analyzer/pull/3632#discussion_r395605298
            format!(
                "the command produced no valid metadata (exit code: {:?}): cargo {}",
                exit_code,
                args.join(" ")
            )
        }
        Err(err) => format!("io error: {:?}", err),
        Ok(_) => return Ok(()),
    };

    Err(CargoError(err_msg))
}
<commit_msg>Simplify<commit_after>//! cargo_check provides the functionality needed to run `cargo check` or
//! another compatible command (f.x. clippy) in a background thread and provide
//! LSP diagnostics based on the output of the command.
mod conv;

use std::{
    error, fmt,
    io::{BufRead, BufReader},
    path::{Path, PathBuf},
    process::{Command, Stdio},
    time::Instant,
};

use cargo_metadata::Message;
use crossbeam_channel::{never, select, unbounded, Receiver, RecvError, Sender};
use lsp_types::{
    CodeAction, CodeActionOrCommand, Diagnostic, Url, WorkDoneProgress, WorkDoneProgressBegin,
    WorkDoneProgressEnd, WorkDoneProgressReport,
};

use crate::conv::{map_rust_diagnostic_to_lsp, MappedRustDiagnostic};

pub use crate::conv::url_from_path_with_drive_lowercasing;

#[derive(Clone, Debug)]
pub struct CheckConfig {
    pub args: Vec<String>,
    pub command: String,
    pub all_targets: bool,
}

/// Flycheck wraps the shared state and communication machinery used for
/// running `cargo check` (or other compatible command) and providing
/// diagnostics based on the output.
/// The spawned thread is shut down when this struct is dropped.
#[derive(Debug)]
pub struct Flycheck {
    // XXX: drop order is significant
    cmd_send: Sender<CheckCommand>,
    handle: jod_thread::JoinHandle<()>,
    pub task_recv: Receiver<CheckTask>,
}

impl Flycheck {
    pub fn new(config: CheckConfig, workspace_root: PathBuf) -> Flycheck {
        let (task_send, task_recv) = unbounded::<CheckTask>();
        let (cmd_send, cmd_recv) = unbounded::<CheckCommand>();
        let handle = jod_thread::spawn(move || {
            let mut check = FlycheckThread::new(config, workspace_root);
            check.run(&task_send, &cmd_recv);
        });
        Flycheck { task_recv, cmd_send, handle }
    }

    /// Schedule a re-start of the cargo check worker.
    pub fn update(&self) {
        self.cmd_send.send(CheckCommand::Update).unwrap();
    }
}

#[derive(Debug)]
pub enum CheckTask {
    /// Request a clearing of all cached diagnostics from the check watcher
    ClearDiagnostics,

    /// Request adding a diagnostic with fixes included to a file
    AddDiagnostic { url: Url, diagnostic: Diagnostic, fixes: Vec<CodeActionOrCommand> },

    /// Request check progress notification to client
    Status(WorkDoneProgress),
}

pub enum CheckCommand {
    /// Request re-start of check thread
    Update,
}

struct FlycheckThread {
    options: CheckConfig,
    workspace_root: PathBuf,
    last_update_req: Option<Instant>,
    // XXX: drop order is significant
    message_recv: Receiver<CheckEvent>,
    /// WatchThread exists to wrap around the communication needed to be able to
    /// run `cargo check` without blocking. Currently the Rust standard library
    /// doesn't provide a way to read sub-process output without blocking, so we
    /// have to wrap sub-processes output handling in a thread and pass messages
    /// back over a channel.
    check_process: Option<jod_thread::JoinHandle<()>>,
}

impl FlycheckThread {
    fn new(options: CheckConfig, workspace_root: PathBuf) -> FlycheckThread {
        FlycheckThread {
            options,
            workspace_root,
            last_update_req: None,
            message_recv: never(),
            check_process: None,
        }
    }

    fn run(&mut self, task_send: &Sender<CheckTask>, cmd_recv: &Receiver<CheckCommand>) {
        // If we rerun the thread, we need to discard the previous check results first
        self.clean_previous_results(task_send);

        loop {
            select! {
                recv(&cmd_recv) -> cmd => match cmd {
                    Ok(cmd) => self.handle_command(cmd),
                    Err(RecvError) => {
                        // Command channel has closed, so shut down
                        break;
                    },
                },
                recv(self.message_recv) -> msg => match msg {
                    Ok(msg) => self.handle_message(msg, task_send),
                    Err(RecvError) => {
                        // Watcher finished, replace it with a never channel to
                        // avoid busy-waiting.
                        self.message_recv = never();
                        self.check_process = None;
                    },
                }
            };

            if self.should_recheck() {
                self.last_update_req = None;
                task_send.send(CheckTask::ClearDiagnostics).unwrap();
                self.restart_check_process();
            }
        }
    }

    fn clean_previous_results(&self, task_send: &Sender<CheckTask>) {
        task_send.send(CheckTask::ClearDiagnostics).unwrap();
        task_send
            .send(CheckTask::Status(WorkDoneProgress::End(WorkDoneProgressEnd { message: None })))
            .unwrap();
    }

    fn should_recheck(&mut self) -> bool {
        if let Some(_last_update_req) = &self.last_update_req {
            // We currently only request an update on save, as we need up to
            // date source on disk for cargo check to do it's magic, so we
            // don't really need to debounce the requests at this point.
            return true;
        }
        false
    }

    fn handle_command(&mut self, cmd: CheckCommand) {
        match cmd {
            CheckCommand::Update => self.last_update_req = Some(Instant::now()),
        }
    }

    fn handle_message(&self, msg: CheckEvent, task_send: &Sender<CheckTask>) {
        match msg {
            CheckEvent::Begin => {
                task_send
                    .send(CheckTask::Status(WorkDoneProgress::Begin(WorkDoneProgressBegin {
                        title: "Running 'cargo check'".to_string(),
                        cancellable: Some(false),
                        message: None,
                        percentage: None,
                    })))
                    .unwrap();
            }

            CheckEvent::End => {
                task_send
                    .send(CheckTask::Status(WorkDoneProgress::End(WorkDoneProgressEnd {
                        message: None,
                    })))
                    .unwrap();
            }

            CheckEvent::Msg(Message::CompilerArtifact(msg)) => {
                task_send
                    .send(CheckTask::Status(WorkDoneProgress::Report(WorkDoneProgressReport {
                        cancellable: Some(false),
                        message: Some(msg.target.name),
                        percentage: None,
                    })))
                    .unwrap();
            }

            CheckEvent::Msg(Message::CompilerMessage(msg)) => {
                let map_result = map_rust_diagnostic_to_lsp(&msg.message, &self.workspace_root);
                if map_result.is_empty() {
                    return;
                }

                for MappedRustDiagnostic { location, diagnostic, fixes } in map_result {
                    let fixes = fixes
                        .into_iter()
                        .map(|fix| {
                            CodeAction { diagnostics: Some(vec![diagnostic.clone()]), ..fix }.into()
                        })
                        .collect();

                    task_send
                        .send(CheckTask::AddDiagnostic { url: location.uri, diagnostic, fixes })
                        .unwrap();
                }
            }

            CheckEvent::Msg(Message::BuildScriptExecuted(_msg)) => {}
            CheckEvent::Msg(Message::Unknown) => {}
        }
    }

    fn restart_check_process(&mut self) {
        // First, clear and cancel the old thread
        self.message_recv = never();
        self.check_process = None;

        let mut args: Vec<String> = vec![
            self.options.command.clone(),
            "--workspace".to_string(),
            "--message-format=json".to_string(),
            "--manifest-path".to_string(),
            format!("{}/Cargo.toml", self.workspace_root.display()),
        ];
        if self.options.all_targets {
            args.push("--all-targets".to_string());
        }
        args.extend(self.options.args.iter().cloned());

        let (message_send, message_recv) = unbounded();
        let workspace_root = self.workspace_root.to_owned();
        self.message_recv = message_recv;
        self.check_process = Some(jod_thread::spawn(move || {
            // If we trigger an error here, we will do so in the loop instead,
            // which will break out of the loop, and continue the shutdown
            let _ = message_send.send(CheckEvent::Begin);

            let res = run_cargo(&args, Some(&workspace_root), &mut |message| {
                // Skip certain kinds of messages to only spend time on what's useful
                match &message {
                    Message::CompilerArtifact(artifact) if artifact.fresh => return true,
                    Message::BuildScriptExecuted(_) => return true,
                    Message::Unknown => return true,
                    _ => {}
                }

                // if the send channel was closed, we want to shutdown
                message_send.send(CheckEvent::Msg(message)).is_ok()
            });

            if let Err(err) = res {
                // FIXME: make the `message_send` to be `Sender<Result<CheckEvent, CargoError>>`
                // to display user-caused misconfiguration errors instead of just logging them here
                log::error!("Cargo watcher failed {:?}", err);
            }

            // We can ignore any error here, as we are already in the progress
            // of shutting down.
            let _ = message_send.send(CheckEvent::End);
        }))
    }
}

#[derive(Debug)]
pub struct DiagnosticWithFixes {
    diagnostic: Diagnostic,
    fixes: Vec<CodeAction>,
}

enum CheckEvent {
    Begin,
    Msg(cargo_metadata::Message),
    End,
}

#[derive(Debug)]
pub struct CargoError(String);

impl fmt::Display for CargoError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Cargo failed: {}", self.0)
    }
}
impl error::Error for CargoError {}

fn run_cargo(
    args: &[String],
    current_dir: Option<&Path>,
    on_message: &mut dyn FnMut(cargo_metadata::Message) -> bool,
) -> Result<(), CargoError> {
    let mut command = Command::new("cargo");
    if let Some(current_dir) = current_dir {
        command.current_dir(current_dir);
    }

    let mut child = command
        .args(args)
        .stdout(Stdio::piped())
        .stderr(Stdio::null())
        .stdin(Stdio::null())
        .spawn()
        .expect("couldn't launch cargo");

    // We manually read a line at a time, instead of using serde's
    // stream deserializers, because the deserializer cannot recover
    // from an error, resulting in it getting stuck, because we try to
    // be resillient against failures.
    //
    // Because cargo only outputs one JSON object per line, we can
    // simply skip a line if it doesn't parse, which just ignores any
    // erroneus output.
    let stdout = BufReader::new(child.stdout.take().unwrap());
    let mut read_at_least_one_message = false;

    for line in stdout.lines() {
        let line = match line {
            Ok(line) => line,
            Err(err) => {
                log::error!("Couldn't read line from cargo: {}", err);
                continue;
            }
        };

        let message = serde_json::from_str::<cargo_metadata::Message>(&line);
        let message = match message {
            Ok(message) => message,
            Err(err) => {
                log::error!("Invalid json from cargo check, ignoring ({}): {:?} ", err, line);
                continue;
            }
        };

        read_at_least_one_message = true;

        if !on_message(message) {
            break;
        }
    }

    // It is okay to ignore the result, as it only errors if the process is already dead
    let _ = child.kill();

    let err_msg = match child.wait() {
        Ok(exit_code) if !exit_code.success() && !read_at_least_one_message => {
            // FIXME: Read the stderr to display the reason, see `read2()` reference in PR comment:
            // https://github.com/rust-analyzer/rust-analyzer/pull/3632#discussion_r395605298
            format!(
                "the command produced no valid metadata (exit code: {:?}): cargo {}",
                exit_code,
                args.join(" ")
            )
        }
        Err(err) => format!("io error: {:?}", err),
        Ok(_) => return Ok(()),
    };

    Err(CargoError(err_msg))
}
<|endoftext|>


<commit_before>/// Used to align a widget vertical or horizontal.
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum Alignment {
    Start,
    Center,
    End,
    Stretch,
}

impl Default for Alignment {
    fn default() -> Self {
        Alignment::Stretch
    }
}

impl Alignment {
    /// Calculates the position (x or y) of the widget depending on the available measure, the goal measure
    /// margin and alignment.
    pub fn align_position(
        self,
        available_measure: f64,
        measure: f64,
        margin_start: f64,
        margin_end: f64,
    ) -> f64 {
        match self {
            Alignment::End => available_measure - measure - margin_end,
            Alignment::Center => (available_measure - measure) / 2.0,
            _ => margin_start,
        }
    }

    /// Calculates the measure (measure or height) of the widget depending on the available measure, the goal measure
    /// margin and horizontal alignment.
    pub fn align_measure(
        self,
        available_measure: f64,
        measure: f64,
        margin_start: f64,
        margin_end: f64,
    ) -> f64 {
        match self {
            Alignment::Stretch => available_measure - margin_start - margin_end,
            _ => measure,
        }
    }
}

impl From<&str> for Alignment {
    fn from(t: &str) -> Self {
        match t {
            "End" | "end" => Alignment::End,
            "Center" | "center" => Alignment::Center,
            "Start" | "start" => Alignment::Start,
            _ => Alignment::Stretch,
        }
    }
}

#[cfg(test)]
mod tests {
    use crate::prelude::*;

    #[test]
    fn test_align_position() {
        let available_measure = 100.0;
        let measure = 50.0;

        let alignment = Alignment::Stretch;
        assert_eq!(
            alignment.align_position(available_measure, measure, 0.0, 0.0),
            0.0
        );

        let alignment = Alignment::Center;
        assert_eq!(
            alignment.align_position(available_measure, measure, 0.0, 0.0),
            25.0
        );

        let alignment = Alignment::Start;
        assert_eq!(
            alignment.align_position(available_measure, measure, 0.0, 0.0),
            0.0
        );

        let alignment = Alignment::End;
        assert_eq!(
            alignment.align_position(available_measure, measure, 0.0, 0.0),
            50.0
        );
    }

    #[test]
    fn test_align_measure() {
        let available_measure = 100.0;
        let measure = 50.0;

        let alignment = Alignment::Stretch;
        assert_eq!(
            alignment.align_measure(available_measure, measure, 0.0, 0.0),
            available_measure
        );

        let alignment = Alignment::Center;
        assert_eq!(
            alignment.align_measure(available_measure, measure, 0.0, 0.0),
            measure
        );

        let alignment = Alignment::Start;
        assert_eq!(
            alignment.align_measure(available_measure, measure, 0.0, 0.0),
            measure
        );

        let alignment = Alignment::End;
        assert_eq!(
            alignment.align_measure(available_measure, measure, 0.0, 0.0),
            measure
        );
    }

    #[test]
    fn test_into() {
        let alignment: Alignment = "start".into();
        assert_eq!(alignment, Alignment::Start);

        let alignment: Alignment = "start".into();
        assert_eq!(alignment, Alignment::Start);

        let alignment: Alignment = "Center".into();
        assert_eq!(alignment, Alignment::Center);

        let alignment: Alignment = "center".into();
        assert_eq!(alignment, Alignment::Center);

        let alignment: Alignment = "end".into();
        assert_eq!(alignment, Alignment::End);

        let alignment: Alignment = "end".into();
        assert_eq!(alignment, Alignment::End);

        let alignment: Alignment = "Stretch".into();
        assert_eq!(alignment, Alignment::Stretch);

        let alignment: Alignment = "stretch".into();
        assert_eq!(alignment, Alignment::Stretch);

        let alignment: Alignment = "other".into();
        assert_eq!(alignment, Alignment::Stretch);
    }
}
<commit_msg>Broke lines wider than 120 char<commit_after>/// Used to align a widget vertical or horizontal.
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum Alignment {
    Start,
    Center,
    End,
    Stretch,
}

impl Default for Alignment {
    fn default() -> Self {
        Alignment::Stretch
    }
}

impl Alignment {
    /// Calculates the position (x or y) of the widget depending on the available measure,
    /// the goal measure margin and alignment.
    pub fn align_position(
        self,
        available_measure: f64,
        measure: f64,
        margin_start: f64,
        margin_end: f64,
    ) -> f64 {
        match self {
            Alignment::End => available_measure - measure - margin_end,
            Alignment::Center => (available_measure - measure) / 2.0,
            _ => margin_start,
        }
    }

    /// Calculates the measure (measure or height) of the widget depending on the available measure,
    /// the goal measure margin and horizontal alignment.
    pub fn align_measure(
        self,
        available_measure: f64,
        measure: f64,
        margin_start: f64,
        margin_end: f64,
    ) -> f64 {
        match self {
            Alignment::Stretch => available_measure - margin_start - margin_end,
            _ => measure,
        }
    }
}

impl From<&str> for Alignment {
    fn from(t: &str) -> Self {
        match t {
            "End" | "end" => Alignment::End,
            "Center" | "center" => Alignment::Center,
            "Start" | "start" => Alignment::Start,
            _ => Alignment::Stretch,
        }
    }
}

#[cfg(test)]
mod tests {
    use crate::prelude::*;

    #[test]
    fn test_align_position() {
        let available_measure = 100.0;
        let measure = 50.0;

        let alignment = Alignment::Stretch;
        assert_eq!(
            alignment.align_position(available_measure, measure, 0.0, 0.0),
            0.0
        );

        let alignment = Alignment::Center;
        assert_eq!(
            alignment.align_position(available_measure, measure, 0.0, 0.0),
            25.0
        );

        let alignment = Alignment::Start;
        assert_eq!(
            alignment.align_position(available_measure, measure, 0.0, 0.0),
            0.0
        );

        let alignment = Alignment::End;
        assert_eq!(
            alignment.align_position(available_measure, measure, 0.0, 0.0),
            50.0
        );
    }

    #[test]
    fn test_align_measure() {
        let available_measure = 100.0;
        let measure = 50.0;

        let alignment = Alignment::Stretch;
        assert_eq!(
            alignment.align_measure(available_measure, measure, 0.0, 0.0),
            available_measure
        );

        let alignment = Alignment::Center;
        assert_eq!(
            alignment.align_measure(available_measure, measure, 0.0, 0.0),
            measure
        );

        let alignment = Alignment::Start;
        assert_eq!(
            alignment.align_measure(available_measure, measure, 0.0, 0.0),
            measure
        );

        let alignment = Alignment::End;
        assert_eq!(
            alignment.align_measure(available_measure, measure, 0.0, 0.0),
            measure
        );
    }

    #[test]
    fn test_into() {
        let alignment: Alignment = "start".into();
        assert_eq!(alignment, Alignment::Start);

        let alignment: Alignment = "start".into();
        assert_eq!(alignment, Alignment::Start);

        let alignment: Alignment = "Center".into();
        assert_eq!(alignment, Alignment::Center);

        let alignment: Alignment = "center".into();
        assert_eq!(alignment, Alignment::Center);

        let alignment: Alignment = "end".into();
        assert_eq!(alignment, Alignment::End);

        let alignment: Alignment = "end".into();
        assert_eq!(alignment, Alignment::End);

        let alignment: Alignment = "Stretch".into();
        assert_eq!(alignment, Alignment::Stretch);

        let alignment: Alignment = "stretch".into();
        assert_eq!(alignment, Alignment::Stretch);

        let alignment: Alignment = "other".into();
        assert_eq!(alignment, Alignment::Stretch);
    }
}
<|endoftext|>


<commit_before>fn main() {
    let mut s = String::new();

    let data = "random stuff";

    let s_to_string = data.to_string();

    let s_from = String::from("Initial content");

    s.push_str("pushed");

    s.push('l');

    let s1 = String::from("First");
    let s2 = String::from("Second");

    let s3 = s1 + &s2;  // s1 is no longer valid

}
<commit_msg>Example of string concatenation with format! macro<commit_after>fn main() {
    let mut s = String::new();

    let data = "random stuff";

    let s_to_string = data.to_string();

    let s_from = String::from("Initial content");

    s.push_str("pushed");

    s.push('l');

    let s1 = String::from("First");
    let s2 = String::from("Second");

    let s3 = s1 + &s2;  // s1 is no longer valid

    let macro_s1 = String::from("tic");
    let macro_s2 = String::from("tac");
    let macro_s3 = String::from("toe");

    let macro_s3 = format!("{}-{}-{}", macro_s1, macro_s2, macro_s3); // does not take ownership of any element

}
<|endoftext|>


<commit_before>#[macro_use]
extern crate nom;

extern crate structopt;
#[macro_use]
extern crate structopt_derive;

use structopt::StructOpt;
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct Vec3 {
    x: isize,
    y: isize,
    z: isize,
}

impl Vec3 {
    fn direction(&self) -> Vec3 {
        Vec3 {
            x: self.x.signum(),
            y: self.y.signum(),
            z: self.z.signum(),
        }
    }
    fn manhattan_magnitude(&self) -> isize {
        self.x.abs() + self.y.abs() + self.z.abs()
    }
}

impl std::ops::Add for Vec3 {
    type Output = Self;
    fn add(self, rhs: Self) -> Self::Output {
        Vec3 {
            x: self.x + rhs.x,
            y: self.y + rhs.y,
            z: self.z + rhs.z,
        }
    }
}

#[derive(Debug, Clone, Hash)]
pub struct Particle {
    position: Vec3,
    velocity: Vec3,
    acceleration: Vec3,
}

impl Particle {
    fn step(&self) -> Particle {
        let new_velocity = self.velocity.clone() + self.acceleration.clone();
        let new_position = self.position.clone() + new_velocity.clone();
        Particle {
            position: new_position,
            velocity: new_velocity,
            acceleration: self.acceleration.clone(),
        }
    }
}

fn part1(particles: &[Particle]) -> usize {
    particles
        .iter()
        .enumerate()
        .min_by(|&(_, a), &(_, b)| {
            a.acceleration
                .manhattan_magnitude()
                .cmp(&b.acceleration.manhattan_magnitude())
                .then(
                    a.velocity
                        .manhattan_magnitude()
                        .cmp(&b.velocity.manhattan_magnitude()),
                )
                .then(
                    a.position
                        .manhattan_magnitude()
                        .cmp(&b.position.manhattan_magnitude()),
                )
        })
        .expect("Need more than one particle")
        .0
}

fn main() {
    let opt = Opt::from_args();
    let mut contents = String::new();
    if opt.input.to_str() == Some("-") {
        std::io::stdin()
            .read_to_string(&mut contents)
            .expect("could not read stdin");
    } else {
        let mut file = File::open(&opt.input)
            .expect(&format!("file {} not found", opt.input.display()));
        file.read_to_string(&mut contents)
            .expect(&format!("could not read file {}", opt.input.display()));
    }
    let particles = parsing::particles(contents.as_bytes())
        .to_result()
        .expect("Could not parse input");
    println!("Part 1: {}", part1(&particles));
}

#[derive(StructOpt, Debug)]
#[structopt(name = "day13", about = "Advent of code 2017 day 13")]
struct Opt {
    #[structopt(help = "Input file", parse(from_os_str))] input: PathBuf,
}

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

    #[test]
    fn part1_test() {
        let a = parsing::particle(b"p=<3,0,0>, v=<2,0,0>, a=<-1,0,0>")
            .to_result()
            .unwrap();
        let b = parsing::particle(b"p=<4,0,0>, v=<0,0,0>, a=<-2,0,0>")
            .to_result()
            .unwrap();
        assert_eq!(part1(&[a, b]), 0);
    }
}

mod parsing {
    use super::{Particle, Vec3};
    use nom::*;

    named!(
        pub particles <Vec<Particle> >,
        separated_list_complete!(newline, particle)
    );

    named!(
        pub particle <Particle>,
        do_parse!(
            tag!("p=") >>
                p: vec3 >>
                tag!(", v=") >>
                v: vec3 >>
                tag!(", a=") >>
                a: vec3 >>
                (Particle {position: p, velocity: v, acceleration: a})
        )
    );

    named!(
        vec3<Vec3>,
        delimited!(
            tag!("<"),
            do_parse!(
                x: integer >> tag!(",") >> y: integer >> tag!(",") >> z: integer
                    >> (Vec3 { x: x, y: y, z: z })
            ),
            tag!(">")
        )
    );

    named!(
        integer<isize>,
        map_res!(
            map_res!(
                take_while!(|b| is_digit(b) || b == b'-'),
                ::std::str::from_utf8
            ),
            ::std::str::FromStr::from_str
        )
    );

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

        #[test]
        fn integer_test() {
            assert!(integer(b"123").is_done());
            assert!(integer(b"-123").is_done());
        }

        #[test]
        fn vec3_test() {
            assert!(vec3(b"<123,456,789>").is_done());
            assert!(vec3(b"<-1,1,-1>").is_done());
        }

        #[test]
        fn particle_test() {
            assert!(particle(b"p=<3,0,0>, v=<2,0,0>, a=<-1,0,0>").is_done());
            assert!(particle(b"p=<4,0,0>, v=<0,0,0>, a=<-2,0,0>").is_done());
        }
    }
}
<commit_msg>2017 day 20 part 02<commit_after>#[macro_use]
extern crate nom;

extern crate structopt;
#[macro_use]
extern crate structopt_derive;

use structopt::StructOpt;
use std::collections::HashSet;
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;

#[derive(Debug, Clone, PartialEq, Eq)]
struct Vec3 {
    x: isize,
    y: isize,
    z: isize,
}

impl Vec3 {
    fn manhattan_magnitude(&self) -> isize {
        self.x.abs() + self.y.abs() + self.z.abs()
    }
    fn manhattan_distance(&self, other: &Vec3) -> isize {
        (self.x - other.x).abs() + (self.y - other.y).abs()
            + (self.z - other.z).abs()
    }
}

impl std::ops::Add for Vec3 {
    type Output = Self;
    fn add(self, rhs: Self) -> Self::Output {
        Vec3 {
            x: self.x + rhs.x,
            y: self.y + rhs.y,
            z: self.z + rhs.z,
        }
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Particle {
    position: Vec3,
    velocity: Vec3,
    acceleration: Vec3,
}

impl Particle {
    fn step(&self) -> Particle {
        let new_velocity = self.velocity.clone() + self.acceleration.clone();
        let new_position = self.position.clone() + new_velocity.clone();
        Particle {
            position: new_position,
            velocity: new_velocity,
            acceleration: self.acceleration.clone(),
        }
    }
    fn done_turning(&self) -> bool {
        (self.acceleration.x == 0
            || self.acceleration.x.signum() == self.velocity.x.signum())
            && (self.acceleration.y == 0
                || self.acceleration.y.signum() == self.velocity.y.signum())
            && (self.acceleration.z == 0
                || self.acceleration.z.signum() == self.velocity.z.signum())
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
enum TrackingStatus {
    Tracking(Particle, Particle),
    Safe,
    Collided,
}

fn track(a: &Particle, b: &Particle) -> TrackingStatus {
    // parallel particles are an edge case here
    if a.velocity == b.velocity && a.acceleration == b.acceleration
        && a.position != b.position
    {
        return TrackingStatus::Safe;
    }
    let new_a = a.step();
    let new_b = b.step();
    if a.position == b.position {
        TrackingStatus::Collided
    } else if a.done_turning() && b.done_turning()
        && new_a.position.manhattan_distance(&new_b.position)
            > a.position.manhattan_distance(&b.position)
    {
        TrackingStatus::Safe
    } else {
        TrackingStatus::Tracking(new_a, new_b)
    }
}

fn part1(particles: &[Particle]) -> usize {
    particles
        .iter()
        .enumerate()
        .min_by(|&(_, a), &(_, b)| {
            a.acceleration
                .manhattan_magnitude()
                .cmp(&b.acceleration.manhattan_magnitude())
                .then(
                    a.velocity
                        .manhattan_magnitude()
                        .cmp(&b.velocity.manhattan_magnitude()),
                )
                .then(
                    a.position
                        .manhattan_magnitude()
                        .cmp(&b.position.manhattan_magnitude()),
                )
        })
        .expect("Need more than one particle")
        .0
}

fn part2(particles: &[Particle]) -> usize {
    let mut tracking_pairs =
        Vec::with_capacity(particles.len() * particles.len() / 2);
    for (idx_a, a) in particles.iter().enumerate() {
        tracking_pairs.extend(
            particles
                .iter()
                .enumerate()
                .skip(idx_a + 1)
                .map(|(idx_b, b)| ((idx_a, idx_b), (a.clone(), b.clone()))),
        );
    }
    let mut collided = HashSet::new();
    while !tracking_pairs.is_empty() {
        let mut new_tracking_pairs = Vec::with_capacity(tracking_pairs.len());
        for ((idx_a, idx_b), (a, b)) in tracking_pairs.drain(..) {
            match track(&a, &b) {
                TrackingStatus::Safe => {}
                TrackingStatus::Collided => {
                    collided.insert(idx_a);
                    collided.insert(idx_b);
                }
                TrackingStatus::Tracking(new_a, new_b) => {
                    new_tracking_pairs.push(((idx_a, idx_b), (new_a, new_b)));
                }
            }
        }
        tracking_pairs = new_tracking_pairs;
    }
    particles.len() - collided.len()
}

fn main() {
    let opt = Opt::from_args();
    let mut contents = String::new();
    if opt.input.to_str() == Some("-") {
        std::io::stdin()
            .read_to_string(&mut contents)
            .expect("could not read stdin");
    } else {
        let mut file = File::open(&opt.input)
            .expect(&format!("file {} not found", opt.input.display()));
        file.read_to_string(&mut contents)
            .expect(&format!("could not read file {}", opt.input.display()));
    }
    let particles = parsing::particles(contents.as_bytes())
        .to_result()
        .expect("Could not parse input");
    println!("Part 1: {}", part1(&particles));
    println!("Part 2: {}", part2(&particles));
}

#[derive(StructOpt, Debug)]
#[structopt(name = "day20", about = "Advent of code 2017 day 20")]
struct Opt {
    #[structopt(help = "Input file", parse(from_os_str))] input: PathBuf,
}

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

    #[test]
    fn part1_test() {
        let a = parsing::particle(b"p=<3,0,0>, v=<2,0,0>, a=<-1,0,0>")
            .to_result()
            .unwrap();
        let b = parsing::particle(b"p=<4,0,0>, v=<0,0,0>, a=<-2,0,0>")
            .to_result()
            .unwrap();
        assert_eq!(part1(&[a, b]), 0);
    }

    #[test]
    fn part2_test() {
        let particles = [
            parsing::particle(b"p=<-6,0,0>, v=<3,0,0>, a=<0,0,0>")
                .to_result()
                .unwrap(),
            parsing::particle(b"p=<-4,0,0>, v=<2,0,0>, a=<0,0,0>")
                .to_result()
                .unwrap(),
            parsing::particle(b"p=<-2,0,0>, v=<1,0,0>, a=<0,0,0>")
                .to_result()
                .unwrap(),
            parsing::particle(b"p=<3,0,0>, v=<-1,0,0>, a=<0,0,0>")
                .to_result()
                .unwrap(),
        ];
        assert_eq!(part2(&particles), 1);
    }
}

mod parsing {
    use super::{Particle, Vec3};
    use nom::*;

    named!(
        pub particles <Vec<Particle> >,
        separated_list_complete!(newline, particle)
    );

    named!(
        pub particle <Particle>,
        do_parse!(
            tag!("p=") >>
                p: vec3 >>
                tag!(", v=") >>
                v: vec3 >>
                tag!(", a=") >>
                a: vec3 >>
                (Particle {position: p, velocity: v, acceleration: a})
        )
    );

    named!(
        vec3<Vec3>,
        delimited!(
            tag!("<"),
            do_parse!(
                x: integer >> tag!(",") >> y: integer >> tag!(",") >> z: integer
                    >> (Vec3 { x: x, y: y, z: z })
            ),
            tag!(">")
        )
    );

    named!(
        integer<isize>,
        map_res!(
            map_res!(
                take_while!(|b| is_digit(b) || b == b'-'),
                ::std::str::from_utf8
            ),
            ::std::str::FromStr::from_str
        )
    );

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

        #[test]
        fn integer_test() {
            assert!(integer(b"123").is_done());
            assert!(integer(b"-123").is_done());
        }

        #[test]
        fn vec3_test() {
            assert!(vec3(b"<123,456,789>").is_done());
            assert!(vec3(b"<-1,1,-1>").is_done());
        }

        #[test]
        fn particle_test() {
            assert!(particle(b"p=<3,0,0>, v=<2,0,0>, a=<-1,0,0>").is_done());
            assert!(particle(b"p=<4,0,0>, v=<0,0,0>, a=<-2,0,0>").is_done());
        }
    }
}
<|endoftext|>


<commit_before>use skeptic::*;

fn main() {
    let cargo_toml_path = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
    let book_root_path = cargo_toml_path.parent().unwrap().parent().unwrap();
    let book_src_path = book_root_path.join("src");
    let book_src_path_str = format!("{}", book_src_path.display());
    let mdbook_files = markdown_files_of_directory(&book_src_path_str);
    generate_doc_tests(&mdbook_files);
}
<commit_msg>Fix build.rs bug where Cargo.toml was not found<commit_after>use skeptic::*;
use std::{env, fs, io};
use std::path::{Path, PathBuf};

// Check the given path and parent directories for a path with the given name.
fn check_parents(name: &str, path: &Path) -> io::Result<Option<PathBuf>> {
    match check_dir(name, path) {
        Ok(None) => match path.parent() {
            None => Ok(None),
            Some(parent) => check_parents(name, parent),
        },
        other_result => other_result,
    }
}

// Check the given directory for a path with the matching name.
fn check_dir(name: &str, path: &Path) -> io::Result<Option<PathBuf>> {
    for entry in fs::read_dir(path)? {
        let entry = entry?;
        let entry_path = entry.path();
        if entry_path.ends_with(name) {
            return Ok(Some(entry_path))
        }
    }
    Ok(None)
}

fn main() {
    let out_dir = env::var("OUT_DIR")
        .expect("failed to retrieve OUT_DIR");
    let cargo_toml_path = check_parents("Cargo.toml", Path::new(&out_dir))
        .expect("an error occurred while searching for Cargo.toml")
        .expect("no Cargo.toml found");
    let book_root_path = cargo_toml_path.parent().unwrap().parent().unwrap();
    let book_src_path = book_root_path.join("src");
    let book_src_path_str = format!("{}", book_src_path.display());
    let mdbook_files = markdown_files_of_directory(&book_src_path_str);
    generate_doc_tests(&mdbook_files);
}
<|endoftext|>