<commit_before>/*
 * Copyright 2019 Jeehoon Kang
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use core::mem;
use core::ops::DerefMut;
use core::ptr;

use crate::page::*;
use crate::slist::{IsElement, List, ListEntry};
use crate::spinlock::SpinLock;
use crate::types::*;
use crate::utils::*;

#[repr(C)]
struct Chunk {
    entry: ListEntry,
    size: usize,
}

#[repr(C)]
struct Entry {
    entry: ListEntry,
}

impl Chunk {
    const_assert!(chunk_size; mem::size_of::<Chunk>() <= mem::size_of::<RawPage>());

    pub const fn new(size: usize) -> Self {
        Self {
            entry: ListEntry::new(),
            size,
        }
    }
}

impl Entry {
    const_assert!(entry_size; mem::size_of::<Entry>() <= mem::size_of::<RawPage>());

    pub const fn new() -> Self {
        Self {
            entry: ListEntry::new(),
        }
    }
}

impl IsElement<Chunk> for Chunk {
    fn entry_of(element: &Chunk) -> &ListEntry {
        &element.entry
    }

    unsafe fn element_of(entry: &ListEntry) -> &Chunk {
        &*(entry as *const _ as *const _)
    }
}

impl IsElement<Entry> for Entry {
    fn entry_of(element: &Entry) -> &ListEntry {
        &element.entry
    }

    unsafe fn element_of(entry: &ListEntry) -> &Entry {
        &*(entry as *const _ as *const _)
    }
}

/// Page pool.
#[repr(C)]
pub struct Pool {
    chunk_list: List<Chunk>,
    entry_list: List<Entry>,
}

impl Pool {
    /// Creates a new page pool.
    pub const fn new() -> Self {
        Self {
            chunk_list: List::new(),
            entry_list: List::new(),
        }
    }

    /// Allocates a page.
    pub fn alloc(&mut self) -> Option<Page> {
        if let Some(entry) = unsafe { self.entry_list.pop() } {
            return Some(unsafe { Page::from_raw(entry as *mut RawPage) });
        }

        let chunk = unsafe { self.chunk_list.pop()? };
        let size = unsafe { (*chunk).size };
        assert_ne!(size, 0);
        let page = unsafe { Page::from_raw(chunk as *mut RawPage) };

        if size == 2 {
            let entry = unsafe { &mut *((chunk as usize + PAGE_SIZE) as *mut Entry) };
            unsafe {
                self.entry_list.push(entry);
            }
        } else if size > 2 {
            let new_chunk = unsafe { &mut *((chunk as usize + PAGE_SIZE) as *mut Chunk) };
            new_chunk.size = size - 1;
            unsafe { self.chunk_list.push(new_chunk) };
        }

        Some(page)
    }

    /// Allocates a number of contiguous and aligned pages.
    pub fn alloc_pages(&mut self, size: usize, align: usize) -> Option<Pages> {
        if size == 1 && align == 1 {
            return self
                .alloc()
                .map(|page| unsafe { Pages::from_raw(page.into_raw(), 1) });
        }

        let (chunk, (chunk_start, chunk_end, start, end)) = unsafe {
            self.chunk_list.pop_if_some(|chunk| {
                // Calculate where the new chunk would be if we consume the requested number of
                // entries. Then check if this chunk is big enough to satisfy the request.
                let chunk_size = (*chunk).size;
                let chunk_start = chunk as *const _ as usize;
                let chunk_end = chunk_start + chunk_size * PAGE_SIZE;
                let start = round_up(chunk_start, align * PAGE_SIZE);
                let end = start + size * PAGE_SIZE;

                if chunk_start <= start && start <= end && end <= chunk_end {
                    return Some((chunk_start, chunk_end, start, end));
                } else {
                    None
                }
            })?
        };

        // Adds `[chunk_start, start)` back to the pool.
        if chunk_start < start {
            let chunk = unsafe { &mut *(chunk as *mut Chunk) };
            chunk.size = (start - chunk_start) / PAGE_SIZE;
            unsafe { self.chunk_list.push(chunk) };
        }

        // Adds `[end, chunk_end)` back to the pool.
        if end < chunk_end {
            let new_chunk = unsafe { &mut *(end as *mut Chunk) };
            new_chunk.size = (chunk_end - end) / PAGE_SIZE;
            unsafe { self.chunk_list.push(new_chunk) };
        }

        Some(unsafe { Pages::from_raw(start as *mut RawPage, size) })
    }

    /// Frees a page back into the given page pool, making it available for reuse.
    ///
    /// This is meant to be used for freeing single pages. To free multiple pages, call
    /// `free_pages()` instead.
    pub fn free(&mut self, mut page: Page) {
        let entry = unsafe { &*(page.deref_mut() as *mut RawPage as *mut Entry) };
        mem::forget(page);
        unsafe { self.entry_list.push(entry) };
    }

    /// Frees a number of contiguous pages to the given page pool.
    pub fn free_pages(&mut self, pages: Pages) {
        let size = pages.len();
        let chunk = unsafe { &mut *(pages.into_raw() as *mut Chunk) };
        chunk.size = size;
        unsafe { self.chunk_list.push(chunk) };
    }
}

#[repr(C)]
pub struct MPool {
    pool: SpinLock<Pool>,
    fallback: *const MPool,
}

unsafe impl Sync for MPool {}

impl MPool {
    /// Initialises the given memory pool with the given entry size, which must be at least the size
    /// of two pointers.
    ///
    /// All entries stored in the memory pool will be aligned to at least the entry size.
    pub const fn new() -> Self {
        Self {
            pool: SpinLock::new(Pool::new()),
            fallback: ptr::null(),
        }
    }

    /// Initialises the given memory pool by replicating the properties of `from`. It also pulls the
    /// chunk and free lists from `from`, consuming all its resources and making them available via
    /// the new memory pool.
    pub fn new_from(from: &Self) -> Self {
        Self {
            pool: SpinLock::new(mem::replace(&mut from.pool.lock(), Pool::new())),
            fallback: from.fallback,
        }

        // TODO(@jeehoonkang): it's different from the original C implementation, where
        // `from.fallback` is re-initialized. But it seems the difference doesn't matter.
    }

    /// Initialises the given memory pool with a fallback memory pool if this pool runs out of
    /// memory.
    pub fn new_with_fallback(fallback: *const Self) -> Self {
        let mut pool = Self::new();
        pool.fallback = fallback;
        pool
    }

    /// Allocates an entry from the given memory pool, if one is available. If there isn't one
    /// available, try and allocate from the fallback if there is one.
    pub fn alloc(&self) -> Option<Page> {
        if let Some(result) = self.pool.lock().alloc() {
            return Some(result);
        }

        if let Some(fallback) = unsafe { self.fallback.as_ref() } {
            return fallback.alloc();
        }

        None
    }

    /// Allocates a number of contiguous and aligned entries. This is a best-effort operation and
    /// only succeeds if such entries can be found in the chunks list or the chunks of the fallbacks
    /// (i.e., the entry list is never used to satisfy these allocations).
    ///
    /// The alignment is specified as the number of entries, that is, if `align` is 4, the alignment
    /// in bytes will be 4 * entry_size.
    ///
    /// The caller can enventually free the returned entries by calling mpool_add_chunk.
    pub fn alloc_pages(&self, count: usize, align: usize) -> Option<Pages> {
        if let Some(result) = self.pool.lock().alloc_pages(count, align) {
            return Some(result);
        }

        if let Some(fallback) = unsafe { self.fallback.as_ref() } {
            return fallback.alloc_pages(count, align);
        }

        None
    }

    /// Frees an entry back into the memory pool, making it available for reuse.
    ///
    /// This is meant to be used for freeing single entries. To free multiple entries, one must call
    /// mpool_add_chunk instead.
    pub fn free(&self, page: Page) {
        self.pool.lock().free(page);
    }

    /// Adds a contiguous chunk of memory to the given memory pool. The chunk will eventually be
    /// broken up into entries of the size held by the memory pool.
    ///
    /// Only the portions aligned to the entry size will be added to the pool.
    ///
    /// Returns true if at least a portion of the chunk was added to pool, or false if none of the
    /// buffer was usable in the pool.
    pub fn free_pages(&self, pages: Pages) {
        self.pool.lock().free_pages(pages);
    }
}

impl Drop for MPool {
    /// Finishes the given memory pool, giving all free memory to the fallback pool if there is one.
    fn drop(&mut self) {
        if let Some(fallback) = unsafe { self.fallback.as_ref() } {
            let mut pool = self.pool.lock();
            let mut pool_fallback = fallback.pool.lock();

            unsafe {
                // Merge the freelist into the fallback.
                while let Some(entry) = pool.entry_list.pop() {
                    pool_fallback.entry_list.push(&*entry);
                }

                // Merge the chunk list into the fallback.
                while let Some(chunk) = pool.chunk_list.pop() {
                    pool_fallback.chunk_list.push(&*chunk);
                }
            }

            // TODO(@jeehoonkang): it's different from the original C implementation, where
            // `self.pool.fallback` is re-initialized. But it seems the difference doesn't matter.
        }

        // TODO(@jeehoonkang): here we're leaking all the memory chunks in this pool.
    }
}

#[no_mangle]
pub unsafe extern "C" fn mpool_enable_locks() {}

#[no_mangle]
pub unsafe extern "C" fn mpool_init(p: *mut MPool, entry_size: size_t) {
    assert_eq!(PAGE_SIZE, entry_size as usize);
    ptr::write(p, MPool::new());
}

#[no_mangle]
pub unsafe extern "C" fn mpool_init_from(p: *mut MPool, from: *mut MPool) {
    ptr::write(p, MPool::new_from(&*from));
    (*from).fallback = ptr::null();
}

#[no_mangle]
pub unsafe extern "C" fn mpool_init_with_fallback(p: *mut MPool, fallback: *const MPool) {
    ptr::write(p, MPool::new_with_fallback(fallback));
}

#[no_mangle]
pub unsafe extern "C" fn mpool_fini(p: *mut MPool) {
    ptr::drop_in_place(p);
    (*p).fallback = ptr::null();
}

#[no_mangle]
pub unsafe extern "C" fn mpool_add_chunk(p: *mut MPool, begin: *mut c_void, size: size_t) -> bool {
    Pages::from_raw_u8(begin as *mut u8, size)
        .map(|pages| (*p).free_pages(pages))
        .is_some()
}

#[no_mangle]
pub unsafe extern "C" fn mpool_alloc(p: *mut MPool) -> *mut c_void {
    (*p).alloc()
        .map(|page| page.into_raw() as *mut c_void)
        .unwrap_or_else(|| ptr::null_mut())
}

#[no_mangle]
pub unsafe extern "C" fn mpool_alloc_contiguous(
    p: *mut MPool,
    count: size_t,
    align: size_t,
) -> *mut c_void {
    (*p).alloc_pages(count as usize, align as usize)
        .map(|pages| pages.into_raw() as *mut c_void)
        .unwrap_or_else(|| ptr::null_mut())
}

#[no_mangle]
pub unsafe extern "C" fn mpool_free(p: *mut MPool, ptr: *mut c_void) {
    (*p).free(Page::from_raw(ptr as *mut RawPage));
}
<commit_msg>TODO about making a trait generalizing MPool<commit_after>/*
 * Copyright 2019 Jeehoon Kang
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use core::mem;
use core::ops::DerefMut;
use core::ptr;

use crate::page::*;
use crate::slist::{IsElement, List, ListEntry};
use crate::spinlock::SpinLock;
use crate::types::*;
use crate::utils::*;

#[repr(C)]
struct Chunk {
    entry: ListEntry,
    size: usize,
}

#[repr(C)]
struct Entry {
    entry: ListEntry,
}

impl Chunk {
    const_assert!(chunk_size; mem::size_of::<Chunk>() <= mem::size_of::<RawPage>());

    pub const fn new(size: usize) -> Self {
        Self {
            entry: ListEntry::new(),
            size,
        }
    }
}

impl Entry {
    const_assert!(entry_size; mem::size_of::<Entry>() <= mem::size_of::<RawPage>());

    pub const fn new() -> Self {
        Self {
            entry: ListEntry::new(),
        }
    }
}

impl IsElement<Chunk> for Chunk {
    fn entry_of(element: &Chunk) -> &ListEntry {
        &element.entry
    }

    unsafe fn element_of(entry: &ListEntry) -> &Chunk {
        &*(entry as *const _ as *const _)
    }
}

impl IsElement<Entry> for Entry {
    fn entry_of(element: &Entry) -> &ListEntry {
        &element.entry
    }

    unsafe fn element_of(entry: &ListEntry) -> &Entry {
        &*(entry as *const _ as *const _)
    }
}

/// Page pool.
#[repr(C)]
pub struct Pool {
    chunk_list: List<Chunk>,
    entry_list: List<Entry>,
}

impl Pool {
    /// Creates a new page pool.
    pub const fn new() -> Self {
        Self {
            chunk_list: List::new(),
            entry_list: List::new(),
        }
    }

    /// Allocates a page.
    pub fn alloc(&mut self) -> Option<Page> {
        if let Some(entry) = unsafe { self.entry_list.pop() } {
            return Some(unsafe { Page::from_raw(entry as *mut RawPage) });
        }

        let chunk = unsafe { self.chunk_list.pop()? };
        let size = unsafe { (*chunk).size };
        assert_ne!(size, 0);
        let page = unsafe { Page::from_raw(chunk as *mut RawPage) };

        if size == 2 {
            let entry = unsafe { &mut *((chunk as usize + PAGE_SIZE) as *mut Entry) };
            unsafe {
                self.entry_list.push(entry);
            }
        } else if size > 2 {
            let new_chunk = unsafe { &mut *((chunk as usize + PAGE_SIZE) as *mut Chunk) };
            new_chunk.size = size - 1;
            unsafe { self.chunk_list.push(new_chunk) };
        }

        Some(page)
    }

    /// Allocates a number of contiguous and aligned pages.
    pub fn alloc_pages(&mut self, size: usize, align: usize) -> Option<Pages> {
        if size == 1 && align == 1 {
            return self
                .alloc()
                .map(|page| unsafe { Pages::from_raw(page.into_raw(), 1) });
        }

        let (chunk, (chunk_start, chunk_end, start, end)) = unsafe {
            self.chunk_list.pop_if_some(|chunk| {
                // Calculate where the new chunk would be if we consume the requested number of
                // entries. Then check if this chunk is big enough to satisfy the request.
                let chunk_size = (*chunk).size;
                let chunk_start = chunk as *const _ as usize;
                let chunk_end = chunk_start + chunk_size * PAGE_SIZE;
                let start = round_up(chunk_start, align * PAGE_SIZE);
                let end = start + size * PAGE_SIZE;

                if chunk_start <= start && start <= end && end <= chunk_end {
                    return Some((chunk_start, chunk_end, start, end));
                } else {
                    None
                }
            })?
        };

        // Adds `[chunk_start, start)` back to the pool.
        if chunk_start < start {
            let chunk = unsafe { &mut *(chunk as *mut Chunk) };
            chunk.size = (start - chunk_start) / PAGE_SIZE;
            unsafe { self.chunk_list.push(chunk) };
        }

        // Adds `[end, chunk_end)` back to the pool.
        if end < chunk_end {
            let new_chunk = unsafe { &mut *(end as *mut Chunk) };
            new_chunk.size = (chunk_end - end) / PAGE_SIZE;
            unsafe { self.chunk_list.push(new_chunk) };
        }

        Some(unsafe { Pages::from_raw(start as *mut RawPage, size) })
    }

    /// Frees a page back into the given page pool, making it available for reuse.
    ///
    /// This is meant to be used for freeing single pages. To free multiple pages, call
    /// `free_pages()` instead.
    pub fn free(&mut self, mut page: Page) {
        let entry = unsafe { &*(page.deref_mut() as *mut RawPage as *mut Entry) };
        mem::forget(page);
        unsafe { self.entry_list.push(entry) };
    }

    /// Frees a number of contiguous pages to the given page pool.
    pub fn free_pages(&mut self, pages: Pages) {
        let size = pages.len();
        let chunk = unsafe { &mut *(pages.into_raw() as *mut Chunk) };
        chunk.size = size;
        unsafe { self.chunk_list.push(chunk) };
    }
}

/// Memory pool equipped with spinlock and fallback pool.
/// TODO(HfO2): Make a trait, which generalizes Pool (linked list) and a pair of
/// (pool, fallback) (#35.)
#[repr(C)]
pub struct MPool {
    pool: SpinLock<Pool>,
    fallback: *const MPool,
}

unsafe impl Sync for MPool {}

impl MPool {
    /// Initialises the given memory pool with the given entry size, which must be at least the size
    /// of two pointers.
    ///
    /// All entries stored in the memory pool will be aligned to at least the entry size.
    pub const fn new() -> Self {
        Self {
            pool: SpinLock::new(Pool::new()),
            fallback: ptr::null(),
        }
    }

    /// Initialises the given memory pool by replicating the properties of `from`. It also pulls the
    /// chunk and free lists from `from`, consuming all its resources and making them available via
    /// the new memory pool.
    pub fn new_from(from: &Self) -> Self {
        Self {
            pool: SpinLock::new(mem::replace(&mut from.pool.lock(), Pool::new())),
            fallback: from.fallback,
        }

        // TODO(@jeehoonkang): it's different from the original C implementation, where
        // `from.fallback` is re-initialized. But it seems the difference doesn't matter.
    }

    /// Initialises the given memory pool with a fallback memory pool if this pool runs out of
    /// memory.
    pub fn new_with_fallback(fallback: *const Self) -> Self {
        let mut pool = Self::new();
        pool.fallback = fallback;
        pool
    }

    /// Allocates an entry from the given memory pool, if one is available. If there isn't one
    /// available, try and allocate from the fallback if there is one.
    pub fn alloc(&self) -> Option<Page> {
        if let Some(result) = self.pool.lock().alloc() {
            return Some(result);
        }

        if let Some(fallback) = unsafe { self.fallback.as_ref() } {
            return fallback.alloc();
        }

        None
    }

    /// Allocates a number of contiguous and aligned entries. This is a best-effort operation and
    /// only succeeds if such entries can be found in the chunks list or the chunks of the fallbacks
    /// (i.e., the entry list is never used to satisfy these allocations).
    ///
    /// The alignment is specified as the number of entries, that is, if `align` is 4, the alignment
    /// in bytes will be 4 * entry_size.
    ///
    /// The caller can enventually free the returned entries by calling mpool_add_chunk.
    pub fn alloc_pages(&self, count: usize, align: usize) -> Option<Pages> {
        if let Some(result) = self.pool.lock().alloc_pages(count, align) {
            return Some(result);
        }

        if let Some(fallback) = unsafe { self.fallback.as_ref() } {
            return fallback.alloc_pages(count, align);
        }

        None
    }

    /// Frees an entry back into the memory pool, making it available for reuse.
    ///
    /// This is meant to be used for freeing single entries. To free multiple entries, one must call
    /// mpool_add_chunk instead.
    pub fn free(&self, page: Page) {
        self.pool.lock().free(page);
    }

    /// Adds a contiguous chunk of memory to the given memory pool. The chunk will eventually be
    /// broken up into entries of the size held by the memory pool.
    ///
    /// Only the portions aligned to the entry size will be added to the pool.
    ///
    /// Returns true if at least a portion of the chunk was added to pool, or false if none of the
    /// buffer was usable in the pool.
    pub fn free_pages(&self, pages: Pages) {
        self.pool.lock().free_pages(pages);
    }
}

impl Drop for MPool {
    /// Finishes the given memory pool, giving all free memory to the fallback pool if there is one.
    fn drop(&mut self) {
        if let Some(fallback) = unsafe { self.fallback.as_ref() } {
            let mut pool = self.pool.lock();
            let mut pool_fallback = fallback.pool.lock();

            unsafe {
                // Merge the freelist into the fallback.
                while let Some(entry) = pool.entry_list.pop() {
                    pool_fallback.entry_list.push(&*entry);
                }

                // Merge the chunk list into the fallback.
                while let Some(chunk) = pool.chunk_list.pop() {
                    pool_fallback.chunk_list.push(&*chunk);
                }
            }

            // TODO(@jeehoonkang): it's different from the original C implementation, where
            // `self.pool.fallback` is re-initialized. But it seems the difference doesn't matter.
        }

        // TODO(@jeehoonkang): here we're leaking all the memory chunks in this pool.
    }
}

#[no_mangle]
pub unsafe extern "C" fn mpool_enable_locks() {}

#[no_mangle]
pub unsafe extern "C" fn mpool_init(p: *mut MPool, entry_size: size_t) {
    assert_eq!(PAGE_SIZE, entry_size as usize);
    ptr::write(p, MPool::new());
}

#[no_mangle]
pub unsafe extern "C" fn mpool_init_from(p: *mut MPool, from: *mut MPool) {
    ptr::write(p, MPool::new_from(&*from));
    (*from).fallback = ptr::null();
}

#[no_mangle]
pub unsafe extern "C" fn mpool_init_with_fallback(p: *mut MPool, fallback: *const MPool) {
    ptr::write(p, MPool::new_with_fallback(fallback));
}

#[no_mangle]
pub unsafe extern "C" fn mpool_fini(p: *mut MPool) {
    ptr::drop_in_place(p);
    (*p).fallback = ptr::null();
}

#[no_mangle]
pub unsafe extern "C" fn mpool_add_chunk(p: *mut MPool, begin: *mut c_void, size: size_t) -> bool {
    Pages::from_raw_u8(begin as *mut u8, size)
        .map(|pages| (*p).free_pages(pages))
        .is_some()
}

#[no_mangle]
pub unsafe extern "C" fn mpool_alloc(p: *mut MPool) -> *mut c_void {
    (*p).alloc()
        .map(|page| page.into_raw() as *mut c_void)
        .unwrap_or_else(|| ptr::null_mut())
}

#[no_mangle]
pub unsafe extern "C" fn mpool_alloc_contiguous(
    p: *mut MPool,
    count: size_t,
    align: size_t,
) -> *mut c_void {
    (*p).alloc_pages(count as usize, align as usize)
        .map(|pages| pages.into_raw() as *mut c_void)
        .unwrap_or_else(|| ptr::null_mut())
}

#[no_mangle]
pub unsafe extern "C" fn mpool_free(p: *mut MPool, ptr: *mut c_void) {
    (*p).free(Page::from_raw(ptr as *mut RawPage));
}
<|endoftext|>


<commit_before>use rustc::hir;
use rustc::mir::ProjectionElem;
use rustc::mir::{Mir, Place, PlaceBase, Mutability, Static, StaticKind};
use rustc::ty::{self, TyCtxt};
use crate::borrow_check::borrow_set::LocalsStateAtExit;

/// Extension methods for the `Place` type.
crate trait PlaceExt<'tcx> {
    /// Returns `true` if we can safely ignore borrows of this place.
    /// This is true whenever there is no action that the user can do
    /// to the place `self` that would invalidate the borrow. This is true
    /// for borrows of raw pointer dereferents as well as shared references.
    fn ignore_borrow(
        &self,
        tcx: TyCtxt<'_, '_, 'tcx>,
        mir: &Mir<'tcx>,
        locals_state_at_exit: &LocalsStateAtExit,
        ) -> bool;
}

impl<'tcx> PlaceExt<'tcx> for Place<'tcx> {
    fn ignore_borrow(
        &self,
        tcx: TyCtxt<'_, '_, 'tcx>,
        mir: &Mir<'tcx>,
        locals_state_at_exit: &LocalsStateAtExit,
    ) -> bool {
        match self {
            // If a local variable is immutable, then we only need to track borrows to guard
            // against two kinds of errors:
            // * The variable being dropped while still borrowed (e.g., because the fn returns
            //   a reference to a local variable)
            // * The variable being moved while still borrowed
            //
            // In particular, the variable cannot be mutated -- the "access checks" will fail --
            // so we don't have to worry about mutation while borrowed.
            Place::Base(PlaceBase::Local(index)) => {
                match locals_state_at_exit {
                    LocalsStateAtExit::AllAreInvalidated => false,
                    LocalsStateAtExit::SomeAreInvalidated { has_storage_dead_or_moved } => {
                        let ignore = !has_storage_dead_or_moved.contains(*index) &&
                            mir.local_decls[*index].mutability == Mutability::Not;
                        debug!("ignore_borrow: local {:?} => {:?}", index, ignore);
                        ignore
                    }
                }
            }
            Place::Base(PlaceBase::Static(box Static{ kind: StaticKind::Promoted(_), .. })) =>
                false,
            Place::Base(PlaceBase::Static(box Static{ kind: StaticKind::Static(def_id), .. })) => {
                tcx.is_mutable_static(*def_id)
            }
            Place::Projection(proj) => match proj.elem {
                ProjectionElem::Field(..)
                | ProjectionElem::Downcast(..)
                | ProjectionElem::Subslice { .. }
                | ProjectionElem::ConstantIndex { .. }
                | ProjectionElem::Index(_) => proj.base.ignore_borrow(
                    tcx, mir, locals_state_at_exit),

                ProjectionElem::Deref => {
                    let ty = proj.base.ty(mir, tcx).ty;
                    match ty.sty {
                        // For both derefs of raw pointers and `&T`
                        // references, the original path is `Copy` and
                        // therefore not significant.  In particular,
                        // there is nothing the user can do to the
                        // original path that would invalidate the
                        // newly created reference -- and if there
                        // were, then the user could have copied the
                        // original path into a new variable and
                        // borrowed *that* one, leaving the original
                        // path unborrowed.
                        ty::RawPtr(..) | ty::Ref(_, _, hir::MutImmutable) => true,
                        _ => proj.base.ignore_borrow(tcx, mir, locals_state_at_exit),
                    }
                }
            },
        }
    }
}
<commit_msg>Rollup merge of #61099 - spastorino:ignore-borrow-iterate, r=oli-obk<commit_after>use rustc::hir;
use rustc::mir::ProjectionElem;
use rustc::mir::{Mir, Place, PlaceBase, Mutability, Static, StaticKind};
use rustc::ty::{self, TyCtxt};
use crate::borrow_check::borrow_set::LocalsStateAtExit;

/// Extension methods for the `Place` type.
crate trait PlaceExt<'tcx> {
    /// Returns `true` if we can safely ignore borrows of this place.
    /// This is true whenever there is no action that the user can do
    /// to the place `self` that would invalidate the borrow. This is true
    /// for borrows of raw pointer dereferents as well as shared references.
    fn ignore_borrow(
        &self,
        tcx: TyCtxt<'_, '_, 'tcx>,
        mir: &Mir<'tcx>,
        locals_state_at_exit: &LocalsStateAtExit,
        ) -> bool;
}

impl<'tcx> PlaceExt<'tcx> for Place<'tcx> {
    fn ignore_borrow(
        &self,
        tcx: TyCtxt<'_, '_, 'tcx>,
        mir: &Mir<'tcx>,
        locals_state_at_exit: &LocalsStateAtExit,
    ) -> bool {
        self.iterate(|place_base, place_projection| {
            let ignore = match place_base {
                // If a local variable is immutable, then we only need to track borrows to guard
                // against two kinds of errors:
                // * The variable being dropped while still borrowed (e.g., because the fn returns
                //   a reference to a local variable)
                // * The variable being moved while still borrowed
                //
                // In particular, the variable cannot be mutated -- the "access checks" will fail --
                // so we don't have to worry about mutation while borrowed.
                PlaceBase::Local(index) => {
                    match locals_state_at_exit {
                        LocalsStateAtExit::AllAreInvalidated => false,
                        LocalsStateAtExit::SomeAreInvalidated { has_storage_dead_or_moved } => {
                            let ignore = !has_storage_dead_or_moved.contains(*index) &&
                                mir.local_decls[*index].mutability == Mutability::Not;
                            debug!("ignore_borrow: local {:?} => {:?}", index, ignore);
                            ignore
                        }
                    }
                }
                PlaceBase::Static(box Static{ kind: StaticKind::Promoted(_), .. }) =>
                    false,
                PlaceBase::Static(box Static{ kind: StaticKind::Static(def_id), .. }) => {
                    tcx.is_mutable_static(*def_id)
                }
            };

            for proj in place_projection {
                if proj.elem == ProjectionElem::Deref {
                    let ty = proj.base.ty(mir, tcx).ty;
                    match ty.sty {
                        // For both derefs of raw pointers and `&T`
                        // references, the original path is `Copy` and
                        // therefore not significant.  In particular,
                        // there is nothing the user can do to the
                        // original path that would invalidate the
                        // newly created reference -- and if there
                        // were, then the user could have copied the
                        // original path into a new variable and
                        // borrowed *that* one, leaving the original
                        // path unborrowed.
                        ty::RawPtr(..) | ty::Ref(_, _, hir::MutImmutable) => return true,
                        _ => {}
                    }
                }
            }

            ignore
        })
    }
}
<|endoftext|>


<commit_before>use std::cmp;

use crate::clean::{self, DocFragment, DocFragmentKind, Item};
use crate::core::DocContext;
use crate::fold::{self, DocFolder};
use crate::passes::Pass;

#[cfg(test)]
mod tests;

crate const UNINDENT_COMMENTS: Pass = Pass {
    name: "unindent-comments",
    run: unindent_comments,
    description: "removes excess indentation on comments in order for markdown to like it",
};

crate fn unindent_comments(krate: clean::Crate, _: &mut DocContext<'_>) -> clean::Crate {
    CommentCleaner.fold_crate(krate)
}

struct CommentCleaner;

impl fold::DocFolder for CommentCleaner {
    fn fold_item(&mut self, mut i: Item) -> Option<Item> {
        i.attrs.unindent_doc_comments();
        Some(self.fold_item_recur(i))
    }
}

impl clean::Attributes {
    crate fn unindent_doc_comments(&mut self) {
        unindent_fragments(&mut self.doc_strings);
    }
}

fn unindent_fragments(docs: &mut Vec<DocFragment>) {
    // `add` is used in case the most common sugared doc syntax is used ("/// "). The other
    // fragments kind's lines are never starting with a whitespace unless they are using some
    // markdown formatting requiring it. Therefore, if the doc block have a mix between the two,
    // we need to take into account the fact that the minimum indent minus one (to take this
    // whitespace into account).
    //
    // For example:
    //
    // /// hello!
    // #[doc = "another"]
    //
    // In this case, you want "hello! another" and not "hello!  another".
    let add = if docs.windows(2).any(|arr| arr[0].kind != arr[1].kind)
        && docs.iter().any(|d| d.kind == DocFragmentKind::SugaredDoc)
    {
        // In case we have a mix of sugared doc comments and "raw" ones, we want the sugared one to
        // "decide" how much the minimum indent will be.
        1
    } else {
        0
    };

    // `min_indent` is used to know how much whitespaces from the start of each lines must be
    // removed. Example:
    //
    // ///     hello!
    // #[doc = "another"]
    //
    // In here, the `min_indent` is 1 (because non-sugared fragment are always counted with minimum
    // 1 whitespace), meaning that "hello!" will be considered a codeblock because it starts with 4
    // (5 - 1) whitespaces.
    let min_indent = match docs
        .iter()
        .map(|fragment| {
            fragment.doc.as_str().lines().fold(usize::MAX, |min_indent, line| {
                if line.chars().all(|c| c.is_whitespace()) {
                    min_indent
                } else {
                    // Compare against either space or tab, ignoring whether they are
                    // mixed or not.
                    let whitespace = line.chars().take_while(|c| *c == ' ' || *c == '\t').count();
                    cmp::min(min_indent, whitespace)
                        + if fragment.kind == DocFragmentKind::SugaredDoc { 0 } else { add }
                }
            })
        })
        .min()
    {
        Some(x) => x,
        None => return,
    };

    for fragment in docs {
        if fragment.doc.as_str().lines().next().is_none() {
            continue;
        }

        let min_indent = if fragment.kind != DocFragmentKind::SugaredDoc && min_indent > 0 {
            min_indent - add
        } else {
            min_indent
        };

        fragment.indent = min_indent;
    }
}
<commit_msg>Use fast comparison against `kw::Empty`<commit_after>use std::cmp;

use rustc_span::symbol::kw;

use crate::clean::{self, DocFragment, DocFragmentKind, Item};
use crate::core::DocContext;
use crate::fold::{self, DocFolder};
use crate::passes::Pass;

#[cfg(test)]
mod tests;

crate const UNINDENT_COMMENTS: Pass = Pass {
    name: "unindent-comments",
    run: unindent_comments,
    description: "removes excess indentation on comments in order for markdown to like it",
};

crate fn unindent_comments(krate: clean::Crate, _: &mut DocContext<'_>) -> clean::Crate {
    CommentCleaner.fold_crate(krate)
}

struct CommentCleaner;

impl fold::DocFolder for CommentCleaner {
    fn fold_item(&mut self, mut i: Item) -> Option<Item> {
        i.attrs.unindent_doc_comments();
        Some(self.fold_item_recur(i))
    }
}

impl clean::Attributes {
    crate fn unindent_doc_comments(&mut self) {
        unindent_fragments(&mut self.doc_strings);
    }
}

fn unindent_fragments(docs: &mut Vec<DocFragment>) {
    // `add` is used in case the most common sugared doc syntax is used ("/// "). The other
    // fragments kind's lines are never starting with a whitespace unless they are using some
    // markdown formatting requiring it. Therefore, if the doc block have a mix between the two,
    // we need to take into account the fact that the minimum indent minus one (to take this
    // whitespace into account).
    //
    // For example:
    //
    // /// hello!
    // #[doc = "another"]
    //
    // In this case, you want "hello! another" and not "hello!  another".
    let add = if docs.windows(2).any(|arr| arr[0].kind != arr[1].kind)
        && docs.iter().any(|d| d.kind == DocFragmentKind::SugaredDoc)
    {
        // In case we have a mix of sugared doc comments and "raw" ones, we want the sugared one to
        // "decide" how much the minimum indent will be.
        1
    } else {
        0
    };

    // `min_indent` is used to know how much whitespaces from the start of each lines must be
    // removed. Example:
    //
    // ///     hello!
    // #[doc = "another"]
    //
    // In here, the `min_indent` is 1 (because non-sugared fragment are always counted with minimum
    // 1 whitespace), meaning that "hello!" will be considered a codeblock because it starts with 4
    // (5 - 1) whitespaces.
    let min_indent = match docs
        .iter()
        .map(|fragment| {
            fragment.doc.as_str().lines().fold(usize::MAX, |min_indent, line| {
                if line.chars().all(|c| c.is_whitespace()) {
                    min_indent
                } else {
                    // Compare against either space or tab, ignoring whether they are
                    // mixed or not.
                    let whitespace = line.chars().take_while(|c| *c == ' ' || *c == '\t').count();
                    cmp::min(min_indent, whitespace)
                        + if fragment.kind == DocFragmentKind::SugaredDoc { 0 } else { add }
                }
            })
        })
        .min()
    {
        Some(x) => x,
        None => return,
    };

    for fragment in docs {
        if fragment.doc == kw::Empty {
            continue;
        }

        let min_indent = if fragment.kind != DocFragmentKind::SugaredDoc && min_indent > 0 {
            min_indent - add
        } else {
            min_indent
        };

        fragment.indent = min_indent;
    }
}
<|endoftext|>


<commit_before>//! Workspace information we get from cargo consists of two pieces. The first is
//! the output of `cargo metadata`. The second is the output of running
//! `build.rs` files (`OUT_DIR` env var, extra cfg flags) and compiling proc
//! macro.
//!
//! This module implements this second part. We use "build script" terminology
//! here, but it covers procedural macros as well.

use std::{
    io,
    path::PathBuf,
    process::{Command, Stdio},
};

use cargo_metadata::{camino::Utf8Path, Message};
use la_arena::ArenaMap;
use paths::AbsPathBuf;
use rustc_hash::FxHashMap;
use serde::Deserialize;

use crate::{cfg_flag::CfgFlag, CargoConfig, CargoWorkspace, Package};

#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct WorkspaceBuildScripts {
    pub(crate) outputs: ArenaMap<Package, BuildScriptOutput>,
    error: Option<String>,
}

#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub(crate) struct BuildScriptOutput {
    /// List of config flags defined by this package's build script.
    pub(crate) cfgs: Vec<CfgFlag>,
    /// List of cargo-related environment variables with their value.
    ///
    /// If the package has a build script which defines environment variables,
    /// they can also be found here.
    pub(crate) envs: Vec<(String, String)>,
    /// Directory where a build script might place its output.
    pub(crate) out_dir: Option<AbsPathBuf>,
    /// Path to the proc-macro library file if this package exposes proc-macros.
    pub(crate) proc_macro_dylib_path: Option<AbsPathBuf>,
}

impl WorkspaceBuildScripts {
    fn build_command(config: &CargoConfig) -> Command {
        if let Some([program, args @ ..]) = config.run_build_script_command.as_deref() {
            let mut cmd = Command::new(program);
            cmd.args(args);
            return cmd;
        }

        let mut cmd = Command::new(toolchain::cargo());

        cmd.args(&["check", "--quiet", "--workspace", "--message-format=json"]);

        // --all-targets includes tests, benches and examples in addition to the
        // default lib and bins. This is an independent concept from the --targets
        // flag below.
        cmd.arg("--all-targets");

        if let Some(target) = &config.target {
            cmd.args(&["--target", target]);
        }

        if config.all_features {
            cmd.arg("--all-features");
        } else {
            if config.no_default_features {
                cmd.arg("--no-default-features");
            }
            if !config.features.is_empty() {
                cmd.arg("--features");
                cmd.arg(config.features.join(" "));
            }
        }

        cmd
    }
    pub(crate) fn run(
        config: &CargoConfig,
        workspace: &CargoWorkspace,
        progress: &dyn Fn(String),
    ) -> io::Result<WorkspaceBuildScripts> {
        let mut cmd = Self::build_command(config);

        if config.wrap_rustc_in_build_scripts {
            // Setup RUSTC_WRAPPER to point to `rust-analyzer` binary itself. We use
            // that to compile only proc macros and build scripts during the initial
            // `cargo check`.
            let myself = std::env::current_exe()?;
            cmd.env("RUSTC_WRAPPER", myself);
            cmd.env("RA_RUSTC_WRAPPER", "1");
        }

        cmd.current_dir(workspace.workspace_root());

        cmd.stdout(Stdio::piped()).stderr(Stdio::piped()).stdin(Stdio::null());

        let mut res = WorkspaceBuildScripts::default();
        // NB: Cargo.toml could have been modified between `cargo metadata` and
        // `cargo check`. We shouldn't assume that package ids we see here are
        // exactly those from `config`.
        let mut by_id: FxHashMap<String, Package> = FxHashMap::default();

        for package in workspace.packages() {
            res.outputs.insert(package, BuildScriptOutput::default());
            by_id.insert(workspace[package].id.clone(), package);
        }

        let mut cfg_err = None;
        let mut stderr = String::new();
        let output = stdx::process::streaming_output(
            cmd,
            &mut |line| {
                if cfg_err.is_some() {
                    return;
                }

                // Copy-pasted from existing cargo_metadata. It seems like we
                // should be using serde_stacker here?
                let mut deserializer = serde_json::Deserializer::from_str(line);
                deserializer.disable_recursion_limit();
                let message = Message::deserialize(&mut deserializer)
                    .unwrap_or_else(|_| Message::TextLine(line.to_string()));

                match message {
                    Message::BuildScriptExecuted(message) => {
                        let package = match by_id.get(&message.package_id.repr) {
                            Some(&it) => it,
                            None => return,
                        };
                        let cfgs = {
                            let mut acc = Vec::new();
                            for cfg in message.cfgs {
                                match cfg.parse::<CfgFlag>() {
                                    Ok(it) => acc.push(it),
                                    Err(err) => {
                                        cfg_err = Some(format!(
                                            "invalid cfg from cargo-metadata: {}",
                                            err
                                        ));
                                        return;
                                    }
                                };
                            }
                            acc
                        };
                        let package_build_data = &mut res.outputs[package];
                        // cargo_metadata crate returns default (empty) path for
                        // older cargos, which is not absolute, so work around that.
                        if !message.out_dir.as_str().is_empty() {
                            let out_dir =
                                AbsPathBuf::assert(PathBuf::from(message.out_dir.into_os_string()));
                            package_build_data.out_dir = Some(out_dir);
                            package_build_data.cfgs = cfgs;
                        }

                        package_build_data.envs = message.env;
                    }
                    Message::CompilerArtifact(message) => {
                        let package = match by_id.get(&message.package_id.repr) {
                            Some(it) => *it,
                            None => return,
                        };

                        progress(format!("metadata {}", message.target.name));

                        if message.target.kind.iter().any(|k| k == "proc-macro") {
                            // Skip rmeta file
                            if let Some(filename) =
                                message.filenames.iter().find(|name| is_dylib(name))
                            {
                                let filename = AbsPathBuf::assert(PathBuf::from(&filename));
                                res.outputs[package].proc_macro_dylib_path = Some(filename);
                            }
                        }
                    }
                    Message::CompilerMessage(message) => {
                        progress(message.target.name);
                    }
                    Message::BuildFinished(_) => {}
                    Message::TextLine(_) => {}
                    _ => {}
                }
            },
            &mut |line| {
                stderr.push_str(line);
                stderr.push('\n');
            },
        )?;

        for package in workspace.packages() {
            let package_build_data = &mut res.outputs[package];
            tracing::info!(
                "{} BuildScriptOutput: {:?}",
                workspace[package].manifest.parent().display(),
                package_build_data,
            );
            // inject_cargo_env(package, package_build_data);
            if let Some(out_dir) = &package_build_data.out_dir {
                // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!()
                if let Some(out_dir) = out_dir.as_os_str().to_str().map(|s| s.to_owned()) {
                    package_build_data.envs.push(("OUT_DIR".to_string(), out_dir));
                }
            }
        }

        if let Some(cfg_err) = cfg_err {
            stderr.push_str(&cfg_err);
            stderr.push('\n');
        }

        if !output.status.success() {
            if stderr.is_empty() {
                stderr = "cargo check failed".to_string();
            }
            res.error = Some(stderr)
        }

        Ok(res)
    }

    pub fn error(&self) -> Option<&str> {
        self.error.as_deref()
    }
}

// FIXME: File a better way to know if it is a dylib.
fn is_dylib(path: &Utf8Path) -> bool {
    match path.extension().map(|e| e.to_string().to_lowercase()) {
        None => false,
        Some(ext) => matches!(ext.as_str(), "dll" | "dylib" | "so"),
    }
}
<commit_msg>Auto merge of #12329 - jonas-schievink:build-script-errors, r=jonas-schievink<commit_after>//! Workspace information we get from cargo consists of two pieces. The first is
//! the output of `cargo metadata`. The second is the output of running
//! `build.rs` files (`OUT_DIR` env var, extra cfg flags) and compiling proc
//! macro.
//!
//! This module implements this second part. We use "build script" terminology
//! here, but it covers procedural macros as well.

use std::{
    cell::RefCell,
    io,
    path::PathBuf,
    process::{Command, Stdio},
};

use cargo_metadata::{camino::Utf8Path, Message};
use la_arena::ArenaMap;
use paths::AbsPathBuf;
use rustc_hash::FxHashMap;
use serde::Deserialize;

use crate::{cfg_flag::CfgFlag, CargoConfig, CargoWorkspace, Package};

#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct WorkspaceBuildScripts {
    pub(crate) outputs: ArenaMap<Package, BuildScriptOutput>,
    error: Option<String>,
}

#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub(crate) struct BuildScriptOutput {
    /// List of config flags defined by this package's build script.
    pub(crate) cfgs: Vec<CfgFlag>,
    /// List of cargo-related environment variables with their value.
    ///
    /// If the package has a build script which defines environment variables,
    /// they can also be found here.
    pub(crate) envs: Vec<(String, String)>,
    /// Directory where a build script might place its output.
    pub(crate) out_dir: Option<AbsPathBuf>,
    /// Path to the proc-macro library file if this package exposes proc-macros.
    pub(crate) proc_macro_dylib_path: Option<AbsPathBuf>,
}

impl WorkspaceBuildScripts {
    fn build_command(config: &CargoConfig) -> Command {
        if let Some([program, args @ ..]) = config.run_build_script_command.as_deref() {
            let mut cmd = Command::new(program);
            cmd.args(args);
            return cmd;
        }

        let mut cmd = Command::new(toolchain::cargo());

        cmd.args(&["check", "--quiet", "--workspace", "--message-format=json"]);

        // --all-targets includes tests, benches and examples in addition to the
        // default lib and bins. This is an independent concept from the --targets
        // flag below.
        cmd.arg("--all-targets");

        if let Some(target) = &config.target {
            cmd.args(&["--target", target]);
        }

        if config.all_features {
            cmd.arg("--all-features");
        } else {
            if config.no_default_features {
                cmd.arg("--no-default-features");
            }
            if !config.features.is_empty() {
                cmd.arg("--features");
                cmd.arg(config.features.join(" "));
            }
        }

        cmd
    }
    pub(crate) fn run(
        config: &CargoConfig,
        workspace: &CargoWorkspace,
        progress: &dyn Fn(String),
    ) -> io::Result<WorkspaceBuildScripts> {
        let mut cmd = Self::build_command(config);

        if config.wrap_rustc_in_build_scripts {
            // Setup RUSTC_WRAPPER to point to `rust-analyzer` binary itself. We use
            // that to compile only proc macros and build scripts during the initial
            // `cargo check`.
            let myself = std::env::current_exe()?;
            cmd.env("RUSTC_WRAPPER", myself);
            cmd.env("RA_RUSTC_WRAPPER", "1");
        }

        cmd.current_dir(workspace.workspace_root());

        cmd.stdout(Stdio::piped()).stderr(Stdio::piped()).stdin(Stdio::null());

        let mut res = WorkspaceBuildScripts::default();
        // NB: Cargo.toml could have been modified between `cargo metadata` and
        // `cargo check`. We shouldn't assume that package ids we see here are
        // exactly those from `config`.
        let mut by_id: FxHashMap<String, Package> = FxHashMap::default();

        for package in workspace.packages() {
            res.outputs.insert(package, BuildScriptOutput::default());
            by_id.insert(workspace[package].id.clone(), package);
        }

        let errors = RefCell::new(String::new());
        let push_err = |err: &str| {
            let mut e = errors.borrow_mut();
            e.push_str(err);
            e.push('\n');
        };
        let output = stdx::process::streaming_output(
            cmd,
            &mut |line| {
                // Copy-pasted from existing cargo_metadata. It seems like we
                // should be using serde_stacker here?
                let mut deserializer = serde_json::Deserializer::from_str(line);
                deserializer.disable_recursion_limit();
                let message = Message::deserialize(&mut deserializer)
                    .unwrap_or_else(|_| Message::TextLine(line.to_string()));

                match message {
                    Message::BuildScriptExecuted(message) => {
                        let package = match by_id.get(&message.package_id.repr) {
                            Some(&it) => it,
                            None => return,
                        };
                        let cfgs = {
                            let mut acc = Vec::new();
                            for cfg in message.cfgs {
                                match cfg.parse::<CfgFlag>() {
                                    Ok(it) => acc.push(it),
                                    Err(err) => {
                                        push_err(&format!(
                                            "invalid cfg from cargo-metadata: {}",
                                            err
                                        ));
                                        return;
                                    }
                                };
                            }
                            acc
                        };
                        let package_build_data = &mut res.outputs[package];
                        // cargo_metadata crate returns default (empty) path for
                        // older cargos, which is not absolute, so work around that.
                        if !message.out_dir.as_str().is_empty() {
                            let out_dir =
                                AbsPathBuf::assert(PathBuf::from(message.out_dir.into_os_string()));
                            package_build_data.out_dir = Some(out_dir);
                            package_build_data.cfgs = cfgs;
                        }

                        package_build_data.envs = message.env;
                    }
                    Message::CompilerArtifact(message) => {
                        let package = match by_id.get(&message.package_id.repr) {
                            Some(it) => *it,
                            None => return,
                        };

                        progress(format!("metadata {}", message.target.name));

                        if message.target.kind.iter().any(|k| k == "proc-macro") {
                            // Skip rmeta file
                            if let Some(filename) =
                                message.filenames.iter().find(|name| is_dylib(name))
                            {
                                let filename = AbsPathBuf::assert(PathBuf::from(&filename));
                                res.outputs[package].proc_macro_dylib_path = Some(filename);
                            }
                        }
                    }
                    Message::CompilerMessage(message) => {
                        progress(message.target.name);

                        if let Some(diag) = message.message.rendered.as_deref() {
                            push_err(diag);
                        }
                    }
                    Message::BuildFinished(_) => {}
                    Message::TextLine(_) => {}
                    _ => {}
                }
            },
            &mut |line| {
                push_err(line);
            },
        )?;

        for package in workspace.packages() {
            let package_build_data = &mut res.outputs[package];
            tracing::info!(
                "{} BuildScriptOutput: {:?}",
                workspace[package].manifest.parent().display(),
                package_build_data,
            );
            // inject_cargo_env(package, package_build_data);
            if let Some(out_dir) = &package_build_data.out_dir {
                // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!()
                if let Some(out_dir) = out_dir.as_os_str().to_str().map(|s| s.to_owned()) {
                    package_build_data.envs.push(("OUT_DIR".to_string(), out_dir));
                }
            }
        }

        let mut errors = errors.into_inner();
        if !output.status.success() {
            if errors.is_empty() {
                errors = "cargo check failed".to_string();
            }
            res.error = Some(errors);
        }

        Ok(res)
    }

    pub fn error(&self) -> Option<&str> {
        self.error.as_deref()
    }
}

// FIXME: File a better way to know if it is a dylib.
fn is_dylib(path: &Utf8Path) -> bool {
    match path.extension().map(|e| e.to_string().to_lowercase()) {
        None => false,
        Some(ext) => matches!(ext.as_str(), "dll" | "dylib" | "so"),
    }
}
<|endoftext|>


<commit_before>use cairo;
use core::ops::Range;

use crate::float_eq_cairo::ApproxEqCairo;

pub trait RectangleExt {
    fn new(x: f64, y: f64, width: f64, height: f64) -> cairo::Rectangle;
    fn from_size(width: f64, height: f64) -> cairo::Rectangle;
    fn from_extents(x0: f64, y0: f64, x1: f64, y1: f64) -> cairo::Rectangle;
    fn is_empty(&self) -> bool;
    fn intersection(&self, rect: &cairo::Rectangle) -> Option<cairo::Rectangle>;
    fn union(&self, rect: &cairo::Rectangle) -> cairo::Rectangle;
    fn translate(&self, by: (f64, f64)) -> cairo::Rectangle;
}

impl RectangleExt for cairo::Rectangle {
    fn new(x: f64, y: f64, width: f64, height: f64) -> cairo::Rectangle {
        cairo::Rectangle {
            x,
            y,
            width,
            height,
        }
    }

    fn from_size(width: f64, height: f64) -> cairo::Rectangle {
        cairo::Rectangle {
            x: 0.0,
            y: 0.0,
            width,
            height,
        }
    }

    fn from_extents(x0: f64, y0: f64, x1: f64, y1: f64) -> cairo::Rectangle {
        cairo::Rectangle {
            x: x0,
            y: y0,
            width: x1 - x0,
            height: y1 - y0,
        }
    }

    fn is_empty(&self) -> bool {
        self.width.approx_eq_cairo(0.0) || self.height.approx_eq_cairo(0.0)
    }

    fn intersection(&self, rect: &cairo::Rectangle) -> Option<cairo::Rectangle> {
        let (x1, y1, x2, y2) = (
            self.x.max(rect.x),
            self.y.max(rect.y),
            (self.x + self.width).min(rect.x + rect.width),
            (self.y + self.height).min(rect.y + rect.height),
        );

        if x2 > x1 && y2 > y1 {
            Some(cairo::Rectangle {
                x: x1,
                y: y1,
                width: x2 - x1,
                height: y2 - y1,
            })
        } else {
            None
        }
    }

    fn union(&self, rect: &cairo::Rectangle) -> cairo::Rectangle {
        let (x1, y1, x2, y2) = (
            self.x.min(rect.x),
            self.y.min(rect.y),
            (self.x + self.width).max(rect.x + rect.width),
            (self.y + self.height).max(rect.y + rect.height),
        );

        cairo::Rectangle {
            x: x1,
            y: y1,
            width: x2 - x1,
            height: y2 - y1,
        }
    }

    fn translate(&self, by: (f64, f64)) -> cairo::Rectangle {
        cairo::Rectangle {
            x: self.x + by.0,
            y: self.y + by.1,
            width: self.width,
            height: self.height,
        }
    }
}

pub trait TransformRect {
    fn transform_rect(&self, rect: &cairo::Rectangle) -> cairo::Rectangle;
}

impl TransformRect for cairo::Matrix {
    fn transform_rect(&self, rect: &cairo::Rectangle) -> cairo::Rectangle {
        let points = vec![
            self.transform_point(rect.x, rect.y),
            self.transform_point(rect.x + rect.width, rect.y),
            self.transform_point(rect.x, rect.y + rect.height),
            self.transform_point(rect.x + rect.width, rect.y + rect.height),
        ];

        let (mut xmin, mut ymin, mut xmax, mut ymax) = {
            let (x, y) = points[0];

            (x, y, x, y)
        };

        for &(x, y) in points.iter().take(4).skip(1) {
            if x < xmin {
                xmin = x;
            }

            if x > xmax {
                xmax = x;
            }

            if y < ymin {
                ymin = y;
            }

            if y > ymax {
                ymax = y;
            }
        }

        cairo::Rectangle {
            x: xmin,
            y: ymin,
            width: xmax - xmin,
            height: ymax - ymin,
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct IRect {
    pub x0: i32,
    pub y0: i32,
    pub x1: i32,
    pub y1: i32,
}

impl IRect {
    #[inline]
    pub fn new(x0: i32, y0: i32, x1: i32, y1: i32) -> IRect {
        IRect { x0, y0, x1, y1 }
    }

    #[inline]
    pub fn from_size(w: i32, h: i32) -> IRect {
        IRect {
            x0: 0,
            y0: 0,
            x1: w,
            y1: h,
        }
    }

    #[inline]
    pub fn width(&self) -> i32 {
        self.x1 - self.x0
    }

    #[inline]
    pub fn height(&self) -> i32 {
        self.y1 - self.y0
    }

    #[inline]
    pub fn x_range(&self) -> Range<i32> {
        self.x0..self.x1
    }

    #[inline]
    pub fn y_range(&self) -> Range<i32> {
        self.y0..self.y1
    }

    /// Returns true if the `IRect` contains the given coordinates.
    #[inline]
    pub fn contains(self, x: i32, y: i32) -> bool {
        x >= self.x0 && x < self.x1 && y >= self.y0 && y < self.y1
    }

    /// Returns an `IRect` scaled by the given amounts.
    ///
    /// The returned `IRect` encompasses all, even partially covered, pixels after the scaling.
    #[inline]
    pub fn scale(self, x: f64, y: f64) -> IRect {
        IRect {
            x0: (f64::from(self.x0) * x).floor() as i32,
            y0: (f64::from(self.y0) * y).floor() as i32,
            x1: (f64::from(self.x1) * x).ceil() as i32,
            y1: (f64::from(self.y1) * y).ceil() as i32,
        }
    }

    /// Returns an `IRect` translated by the given amounts.
    #[inline]
    pub fn translate(&self, by: (i32, i32)) -> IRect {
        IRect {
            x0: self.x0 + by.0,
            y0: self.y0 + by.1,
            x1: self.x1 + by.0,
            y1: self.y1 + by.1,
        }
    }

    #[inline]
    pub fn intersection(&self, rect: &Self) -> Option<IRect> {
        let (x0, y0, x1, y1) = (
            self.x0.max(rect.x0),
            self.y0.max(rect.y0),
            self.x1.min(rect.x1),
            self.y1.min(rect.y1),
        );

        if x1 > x0 && y1 > y0 {
            Some(IRect { x0, y0, x1, y1 })
        } else {
            None
        }
    }
}

impl From<cairo::Rectangle> for IRect {
    #[inline]
    fn from(
        cairo::Rectangle {
            x,
            y,
            width,
            height,
        }: cairo::Rectangle,
    ) -> Self {
        Self {
            x0: x.floor() as i32,
            y0: y.floor() as i32,
            x1: (x + width).ceil() as i32,
            y1: (y + height).ceil() as i32,
        }
    }
}

impl From<IRect> for cairo::Rectangle {
    #[inline]
    fn from(IRect { x0, y0, x1, y1 }: IRect) -> Self {
        Self {
            x: f64::from(x0),
            y: f64::from(y0),
            width: f64::from(x1 - x0),
            height: f64::from(y1 - y0),
        }
    }
}

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

    #[test]
    fn empty_rect() {
        let empty = cairo::Rectangle {
            x: 0.42,
            y: 0.42,
            width: 0.0,
            height: 0.0,
        };
        let not_empty = cairo::Rectangle {
            x: 0.22,
            y: 0.22,
            width: 3.14,
            height: 3.14,
        };

        assert!(empty.is_empty());
        assert!(!not_empty.is_empty());
    }

    #[test]
    fn intersect_rects() {
        let r1 = cairo::Rectangle {
            x: 0.42,
            y: 0.42,
            width: 4.14,
            height: 4.14,
        };
        let r2 = cairo::Rectangle {
            x: 0.22,
            y: 0.22,
            width: 3.14,
            height: 3.14,
        };
        let r3 = cairo::Rectangle {
            x: 10.0,
            y: 10.0,
            width: 3.14,
            height: 3.14,
        };

        let r = r1.intersection(&r2).unwrap();
        assert_approx_eq_cairo!(0.42_f64, r.x);
        assert_approx_eq_cairo!(0.42_f64, r.y);
        assert_approx_eq_cairo!(2.94_f64, r.width);
        assert_approx_eq_cairo!(2.94_f64, r.height);

        let r = r1.intersection(&r3);
        assert!(r.is_none());
    }

    #[test]
    fn union_rects() {
        let r1 = cairo::Rectangle {
            x: 0.42,
            y: 0.42,
            width: 4.14,
            height: 4.14,
        };
        let r2 = cairo::Rectangle {
            x: 0.22,
            y: 0.22,
            width: 3.14,
            height: 3.14,
        };

        let r = r1.union(&r2);
        assert_approx_eq_cairo!(0.22_f64, r.x);
        assert_approx_eq_cairo!(0.22_f64, r.y);
        assert_approx_eq_cairo!(4.34_f64, r.width);
        assert_approx_eq_cairo!(4.34_f64, r.height);
    }

    #[test]
    fn transform_rect() {
        let r = cairo::Rectangle {
            x: 0.42,
            y: 0.42,
            width: 3.14,
            height: 3.14,
        };

        let m = cairo::Matrix::identity();
        let tr = m.transform_rect(&r);
        assert_eq!(tr, r);

        let m = cairo::Matrix::new(2.0, 0.0, 0.0, 2.0, 1.5, 1.5);
        let tr = m.transform_rect(&r);
        assert_approx_eq_cairo!(2.34_f64, tr.x);
        assert_approx_eq_cairo!(2.34_f64, tr.y);
        assert_approx_eq_cairo!(6.28_f64, tr.width);
        assert_approx_eq_cairo!(6.28_f64, tr.height);
    }
}
<commit_msg>rect: replace IRect with a generic rect<commit_after>use cairo;

use crate::float_eq_cairo::ApproxEqCairo;

mod rect {
    use core::ops::{Add, Range, Sub};
    use num_traits::Zero;

    // Use our own min() and max() that are acceptable for floating point

    fn min<T: PartialOrd>(x: T, y: T) -> T {
        if x <= y {
            x
        } else {
            y
        }
    }

    fn max<T: PartialOrd>(x: T, y: T) -> T {
        if x >= y {
            x
        } else {
            y
        }
    }

    #[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
    pub struct Rect<T> {
        pub x0: T,
        pub y0: T,
        pub x1: T,
        pub y1: T,
    }

    impl<T> Rect<T> {
        #[inline]
        pub fn new(x0: T, y0: T, x1: T, y1: T) -> Self {
            Self { x0, y0, x1, y1 }
        }
    }

    impl<T> Rect<T>
    where
        T: Copy + PartialOrd + PartialEq + Add<T, Output = T> + Sub<T, Output = T> + Zero,
    {
        #[inline]
        pub fn from_size(w: T, h: T) -> Self {
            Self {
                x0: Zero::zero(),
                y0: Zero::zero(),
                x1: w,
                y1: h,
            }
        }

        #[inline]
        pub fn width(&self) -> T {
            self.x1 - self.x0
        }

        #[inline]
        pub fn height(&self) -> T {
            self.y1 - self.y0
        }

        #[inline]
        pub fn size(&self) -> (T, T) {
            (self.width(), self.height())
        }

        #[inline]
        pub fn x_range(&self) -> Range<T> {
            self.x0..self.x1
        }

        #[inline]
        pub fn y_range(&self) -> Range<T> {
            self.y0..self.y1
        }

        #[inline]
        pub fn contains(self, x: T, y: T) -> bool {
            x >= self.x0 && x < self.x1 && y >= self.y0 && y < self.y1
        }

        #[inline]
        pub fn translate(&self, by: (T, T)) -> Self {
            Self {
                x0: self.x0 + by.0,
                y0: self.y0 + by.1,
                x1: self.x1 + by.0,
                y1: self.y1 + by.1,
            }
        }

        #[inline]
        pub fn intersection(&self, rect: &Self) -> Option<Self> {
            let (x0, y0, x1, y1) = (
                max(self.x0, rect.x0),
                max(self.y0, rect.y0),
                min(self.x1, rect.x1),
                min(self.y1, rect.y1),
            );

            if x1 > x0 && y1 > y0 {
                Some(Self { x0, y0, x1, y1 })
            } else {
                None
            }
        }

        #[inline]
        pub fn union(&self, rect: &Self) -> Self {
            Self {
                x0: min(self.x0, rect.x0),
                y0: min(self.y0, rect.y0),
                x1: max(self.x1, rect.x1),
                y1: max(self.y1, rect.y1),
            }
        }
    }

    impl Rect<i32> {
        #[inline]
        pub fn is_empty(&self) -> bool {
            self.width() == Zero::zero() || self.height() == Zero::zero()
        }

        #[inline]
        pub fn scale(self, x: f64, y: f64) -> Self {
            Self {
                x0: (f64::from(self.x0) * x).floor() as i32,
                y0: (f64::from(self.y0) * y).floor() as i32,
                x1: (f64::from(self.x1) * x).ceil() as i32,
                y1: (f64::from(self.y1) * y).ceil() as i32,
            }
        }
    }
}

pub type IRect = rect::Rect<i32>;

impl From<cairo::Rectangle> for IRect {
    #[inline]
    fn from(r: cairo::Rectangle) -> Self {
        Self {
            x0: r.x.floor() as i32,
            y0: r.y.floor() as i32,
            x1: (r.x + r.width).ceil() as i32,
            y1: (r.y + r.height).ceil() as i32,
        }
    }
}

impl From<IRect> for cairo::Rectangle {
    #[inline]
    fn from(r: IRect) -> Self {
        Self {
            x: f64::from(r.x0),
            y: f64::from(r.y0),
            width: f64::from(r.x1 - r.x0),
            height: f64::from(r.y1 - r.y0),
        }
    }
}

pub trait RectangleExt {
    fn new(x: f64, y: f64, width: f64, height: f64) -> cairo::Rectangle;
    fn from_size(width: f64, height: f64) -> cairo::Rectangle;
    fn from_extents(x0: f64, y0: f64, x1: f64, y1: f64) -> cairo::Rectangle;
    fn is_empty(&self) -> bool;
    fn intersection(&self, rect: &cairo::Rectangle) -> Option<cairo::Rectangle>;
    fn union(&self, rect: &cairo::Rectangle) -> cairo::Rectangle;
    fn translate(&self, by: (f64, f64)) -> cairo::Rectangle;
}

impl RectangleExt for cairo::Rectangle {
    fn new(x: f64, y: f64, width: f64, height: f64) -> cairo::Rectangle {
        cairo::Rectangle {
            x,
            y,
            width,
            height,
        }
    }

    fn from_size(width: f64, height: f64) -> cairo::Rectangle {
        cairo::Rectangle {
            x: 0.0,
            y: 0.0,
            width,
            height,
        }
    }

    fn from_extents(x0: f64, y0: f64, x1: f64, y1: f64) -> cairo::Rectangle {
        cairo::Rectangle {
            x: x0,
            y: y0,
            width: x1 - x0,
            height: y1 - y0,
        }
    }

    fn is_empty(&self) -> bool {
        self.width.approx_eq_cairo(0.0) || self.height.approx_eq_cairo(0.0)
    }

    fn intersection(&self, rect: &cairo::Rectangle) -> Option<cairo::Rectangle> {
        let (x1, y1, x2, y2) = (
            self.x.max(rect.x),
            self.y.max(rect.y),
            (self.x + self.width).min(rect.x + rect.width),
            (self.y + self.height).min(rect.y + rect.height),
        );

        if x2 > x1 && y2 > y1 {
            Some(cairo::Rectangle {
                x: x1,
                y: y1,
                width: x2 - x1,
                height: y2 - y1,
            })
        } else {
            None
        }
    }

    fn union(&self, rect: &cairo::Rectangle) -> cairo::Rectangle {
        let (x1, y1, x2, y2) = (
            self.x.min(rect.x),
            self.y.min(rect.y),
            (self.x + self.width).max(rect.x + rect.width),
            (self.y + self.height).max(rect.y + rect.height),
        );

        cairo::Rectangle {
            x: x1,
            y: y1,
            width: x2 - x1,
            height: y2 - y1,
        }
    }

    fn translate(&self, by: (f64, f64)) -> cairo::Rectangle {
        cairo::Rectangle {
            x: self.x + by.0,
            y: self.y + by.1,
            width: self.width,
            height: self.height,
        }
    }
}

pub trait TransformRect {
    fn transform_rect(&self, rect: &cairo::Rectangle) -> cairo::Rectangle;
}

impl TransformRect for cairo::Matrix {
    fn transform_rect(&self, rect: &cairo::Rectangle) -> cairo::Rectangle {
        let points = vec![
            self.transform_point(rect.x, rect.y),
            self.transform_point(rect.x + rect.width, rect.y),
            self.transform_point(rect.x, rect.y + rect.height),
            self.transform_point(rect.x + rect.width, rect.y + rect.height),
        ];

        let (mut xmin, mut ymin, mut xmax, mut ymax) = {
            let (x, y) = points[0];

            (x, y, x, y)
        };

        for &(x, y) in points.iter().take(4).skip(1) {
            if x < xmin {
                xmin = x;
            }

            if x > xmax {
                xmax = x;
            }

            if y < ymin {
                ymin = y;
            }

            if y > ymax {
                ymax = y;
            }
        }

        cairo::Rectangle {
            x: xmin,
            y: ymin,
            width: xmax - xmin,
            height: ymax - ymin,
        }
    }
}

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

    #[test]
    fn empty_rect() {
        let empty = cairo::Rectangle {
            x: 0.42,
            y: 0.42,
            width: 0.0,
            height: 0.0,
        };
        let not_empty = cairo::Rectangle {
            x: 0.22,
            y: 0.22,
            width: 3.14,
            height: 3.14,
        };

        assert!(empty.is_empty());
        assert!(!not_empty.is_empty());
    }

    #[test]
    fn intersect_rects() {
        let r1 = cairo::Rectangle {
            x: 0.42,
            y: 0.42,
            width: 4.14,
            height: 4.14,
        };
        let r2 = cairo::Rectangle {
            x: 0.22,
            y: 0.22,
            width: 3.14,
            height: 3.14,
        };
        let r3 = cairo::Rectangle {
            x: 10.0,
            y: 10.0,
            width: 3.14,
            height: 3.14,
        };

        let r = r1.intersection(&r2).unwrap();
        assert_approx_eq_cairo!(0.42_f64, r.x);
        assert_approx_eq_cairo!(0.42_f64, r.y);
        assert_approx_eq_cairo!(2.94_f64, r.width);
        assert_approx_eq_cairo!(2.94_f64, r.height);

        let r = r1.intersection(&r3);
        assert!(r.is_none());
    }

    #[test]
    fn union_rects() {
        let r1 = cairo::Rectangle {
            x: 0.42,
            y: 0.42,
            width: 4.14,
            height: 4.14,
        };
        let r2 = cairo::Rectangle {
            x: 0.22,
            y: 0.22,
            width: 3.14,
            height: 3.14,
        };

        let r = r1.union(&r2);
        assert_approx_eq_cairo!(0.22_f64, r.x);
        assert_approx_eq_cairo!(0.22_f64, r.y);
        assert_approx_eq_cairo!(4.34_f64, r.width);
        assert_approx_eq_cairo!(4.34_f64, r.height);
    }

    #[test]
    fn transform_rect() {
        let r = cairo::Rectangle {
            x: 0.42,
            y: 0.42,
            width: 3.14,
            height: 3.14,
        };

        let m = cairo::Matrix::identity();
        let tr = m.transform_rect(&r);
        assert_eq!(tr, r);

        let m = cairo::Matrix::new(2.0, 0.0, 0.0, 2.0, 1.5, 1.5);
        let tr = m.transform_rect(&r);
        assert_approx_eq_cairo!(2.34_f64, tr.x);
        assert_approx_eq_cairo!(2.34_f64, tr.y);
        assert_approx_eq_cairo!(6.28_f64, tr.width);
        assert_approx_eq_cairo!(6.28_f64, tr.height);
    }
}
<|endoftext|>


<commit_before>use rusqlite::Connection;

#[derive(Debug)]
pub struct FsFile {
    pub id: i32,
    pub fk_fs_dir: i32,
    pub name: String,
}

pub fn table_create_fs_file(conn: &Connection) -> &Connection {
    conn.execute(
        "CREATE TABLE FS_FILE (
                  id                INTEGER PRIMARY KEY ASC,
                  fk_fs_dir         INTEGER NOT NULL,
                  name              TEXT NOT NULL UNIQUE
                  )",
        &[],
    ).unwrap();
    return conn;
}

pub fn insert_fs_file(
    conn: &Connection,
    fk_fs_dir: i32,
    name: String,
) -> Result<i32, &'static str> {
    let me = FsFile {
        id: 0,
        name: name,
        fk_fs_dir: fk_fs_dir,
    };
    let variable_pair_instance = conn.execute(
        "INSERT INTO FS_FILE (name, fk_fs_dir)
                  VALUES (?1, ?2)",
        &[&me.name, &me.fk_fs_dir],
    );
    if variable_pair_instance.is_err() {
        return Err("Insert failed");
    }
    variable_pair_instance.unwrap();
    return Ok(0);
}

pub fn list_fs_file(conn: &Connection) -> Vec<FsFile> {
    let mut stmt = conn.prepare("SELECT id, fk_fs_dir,name  FROM FS_FILE")
        .unwrap();
    let fs_file_iter = stmt.query_map(&[], |row| FsFile {
        id: row.get(0),
        fk_fs_dir: row.get(1),
        name: row.get(2),
    }).unwrap();
    let mut items = Vec::<FsFile>::new();
    for person in fs_file_iter {
        items.push(person.unwrap());
    }
    return items;
}

pub fn pk_fs_file_by_name(conn: &Connection, name: String, pk: &mut i32) {
    let mut stmt = conn.prepare("SELECT id, fk_fs_dir,name  FROM FS_FILE WHERE name =?1")
        .unwrap();
    let fs_file_iter = stmt.query_map(&[&name], |row| FsFile {
        id: row.get(0),
        fk_fs_dir: row.get(1),
        name: row.get(2),
    }).unwrap();
    for person in fs_file_iter {
        let bill = person.unwrap();
        *pk = bill.id;
        return;
    }
}

pub fn list_fs_file_type(conn: &Connection, fk_fs_type: &i32) -> Vec<FsFile> {
    let mut stmt = conn.prepare(
        "SELECT FS_FILE.id, FS_FILE.name, FS_FILE.fk_fs_dir FROM FS_FILE
        INNER JOIN FS_DIR
        on FS_FILE.fk_fs_dir = FS_DIR.id
        INNER JOIN FS_DIR_TYPE
        on FS_DIR.fk_type = FS_DIR_TYPE.id
        WHERE FS_DIR_TYPE.id =?1
        ",
    ).unwrap();
    let fs_file_iter = stmt.query_map(&[fk_fs_type], |row| FsFile {
        id: row.get(0),
        fk_fs_dir: row.get(2),
        name: row.get(1),
    }).unwrap();
    let mut items = Vec::<FsFile>::new();
    for person in fs_file_iter {
        items.push(person.unwrap());
    }
    return items;
}

#[cfg(test)]
mod tests {
    #[test]
    fn insert_fs_file() {
        use db;
        use db_fs_dir_type;
        use db_fs_dir;
        let conn = db::connect();
        db::create_tables(&conn);
        let str_job_files_list = String::from("job_files");
        let _ = db_fs_dir_type::insert_fs_dir_type(&conn, &str_job_files_list);
        let vec_dir_type = db_fs_dir_type::list_fs_dir_type(&conn);
        let mut pk_dir_type = 0;
        let mut counter = 0;
        for dir_type in vec_dir_type {
            counter += 1;
            pk_dir_type = dir_type.id;
        }
        assert!(counter == 1);
        assert!(pk_dir_type == 1);
        let str_directory_path = String::from("directory_path");
        let insert_fs_dir_result =
            db_fs_dir::insert_fs_dir(&conn, &pk_dir_type, &str_directory_path);
        let fs_dir_result = insert_fs_dir_result.unwrap();
        assert!(fs_dir_result == 0);
        let list_fs_dir_result =
            db_fs_dir::list_fs_dir_by_all(&conn, &pk_dir_type, &str_directory_path);
        let vec_dir = list_fs_dir_result.unwrap();
        counter = 0;
        let mut pk_dir = 0;
        for dir in vec_dir {
            counter += 1;
            pk_dir = dir.id;
        }
        assert!(counter == 1);
        assert!(pk_dir == 1);
    }
}
<commit_msg>Beutify the rust code<commit_after>use rusqlite::Connection;

#[derive(Debug)]
pub struct FsFile {
    pub id: i32,
    pub fk_fs_dir: i32,
    pub name: String,
}

pub fn table_create_fs_file(conn: &Connection) -> &Connection {
    conn.execute(
        "CREATE TABLE FS_FILE (
                  id                INTEGER PRIMARY KEY ASC,
                  fk_fs_dir         INTEGER NOT NULL,
                  name              TEXT NOT NULL UNIQUE
                  )",
        &[],
    ).unwrap();
    return conn;
}

pub fn insert_fs_file(
    conn: &Connection,
    fk_fs_dir: i32,
    name: String,
) -> Result<i32, &'static str> {
    let me = FsFile {
        id: 0,
        name: name,
        fk_fs_dir: fk_fs_dir,
    };
    let variable_pair_instance = conn.execute(
        "INSERT INTO FS_FILE (name, fk_fs_dir)
                  VALUES (?1, ?2)",
        &[&me.name, &me.fk_fs_dir],
    );
    if variable_pair_instance.is_err() {
        return Err("Insert failed");
    }
    variable_pair_instance.unwrap();
    return Ok(0);
}

pub fn list_fs_file(conn: &Connection) -> Vec<FsFile> {
    let mut stmt = conn
        .prepare("SELECT id, fk_fs_dir,name  FROM FS_FILE")
        .unwrap();
    let fs_file_iter =
        stmt.query_map(&[], |row| FsFile {
            id: row.get(0),
            fk_fs_dir: row.get(1),
            name: row.get(2),
        }).unwrap();
    let mut items = Vec::<FsFile>::new();
    for person in fs_file_iter {
        items.push(person.unwrap());
    }
    return items;
}

pub fn pk_fs_file_by_name(conn: &Connection, name: String, pk: &mut i32) {
    let mut stmt = conn
        .prepare("SELECT id, fk_fs_dir,name  FROM FS_FILE WHERE name =?1")
        .unwrap();
    let fs_file_iter =
        stmt.query_map(&[&name], |row| FsFile {
            id: row.get(0),
            fk_fs_dir: row.get(1),
            name: row.get(2),
        }).unwrap();
    for person in fs_file_iter {
        let bill = person.unwrap();
        *pk = bill.id;
        return;
    }
}

pub fn list_fs_file_type(conn: &Connection, fk_fs_type: &i32) -> Vec<FsFile> {
    let mut stmt =
        conn.prepare(
            "SELECT FS_FILE.id, FS_FILE.name, FS_FILE.fk_fs_dir FROM FS_FILE
        INNER JOIN FS_DIR
        on FS_FILE.fk_fs_dir = FS_DIR.id
        INNER JOIN FS_DIR_TYPE
        on FS_DIR.fk_type = FS_DIR_TYPE.id
        WHERE FS_DIR_TYPE.id =?1
        ",
        ).unwrap();
    let fs_file_iter =
        stmt.query_map(&[fk_fs_type], |row| FsFile {
            id: row.get(0),
            fk_fs_dir: row.get(2),
            name: row.get(1),
        }).unwrap();
    let mut items = Vec::<FsFile>::new();
    for person in fs_file_iter {
        items.push(person.unwrap());
    }
    return items;
}

#[cfg(test)]
mod tests {
    #[test]
    fn insert_fs_file() {
        use db;
        use db_fs_dir;
        use db_fs_dir_type;
        let conn = db::connect();
        db::create_tables(&conn);
        let str_job_files_list = String::from("job_files");
        let _ = db_fs_dir_type::insert_fs_dir_type(&conn, &str_job_files_list);
        let vec_dir_type = db_fs_dir_type::list_fs_dir_type(&conn);
        let mut pk_dir_type = 0;
        let mut counter = 0;
        for dir_type in vec_dir_type {
            counter += 1;
            pk_dir_type = dir_type.id;
        }
        assert!(counter == 1);
        assert!(pk_dir_type == 1);
        let str_directory_path = String::from("directory_path");
        let insert_fs_dir_result =
            db_fs_dir::insert_fs_dir(&conn, &pk_dir_type, &str_directory_path);
        let fs_dir_result = insert_fs_dir_result.unwrap();
        assert!(fs_dir_result == 0);
        let list_fs_dir_result =
            db_fs_dir::list_fs_dir_by_all(&conn, &pk_dir_type, &str_directory_path);
        let vec_dir = list_fs_dir_result.unwrap();
        counter = 0;
        let mut pk_dir = 0;
        for dir in vec_dir {
            counter += 1;
            pk_dir = dir.id;
        }
        assert!(counter == 1);
        assert!(pk_dir == 1);
    }
}
<|endoftext|>


<commit_before>use crate::common::State;
use crate::http::{http_main_server, http_redirect_server};
use anyhow::Result;
use extendedmind_engine::tcp;
use std::path::PathBuf;
use tide_acme::rustls_acme::caches::DirCache;
use tide_acme::{AcmeConfig, TideRustlsExt};

fn setup_logging(log_to_stderr: bool) {
    let base_config = fern::Dispatch::new()
        .format(|out, message, record| {
            out.finish(format_args!(
                "{}[{}][{}] {}",
                chrono::Local::now().format("[%Y-%m-%d %H:%M:%S]"),
                record.target(),
                record.level(),
                message
            ))
        })
        .level(log::LevelFilter::Debug);
    let std_config = if log_to_stderr {
        fern::Dispatch::new().chain(std::io::stderr())
    } else {
        fern::Dispatch::new().chain(std::io::stdout())
    };

    base_config.chain(std_config).apply().unwrap();
}

pub async fn start_server(
    initial_state: State,
    static_root_dir: Option<PathBuf>,
    http_port: Option<u16>,
    https_port: Option<u16>,
    domain: Option<String>,
    acme_email: Option<String>,
    acme_dir: Option<String>,
    tcp_port: Option<u16>,
    skip_compress_mime: Option<Vec<String>>,
    cache_ttl_sec: Option<u64>,
    cache_tti_sec: Option<u64>,
    inline_css_path: Option<Vec<String>>,
    immutable_path: Option<Vec<String>>,
) -> Result<()> {
    let engine = initial_state.engine.clone();

    if let Some(http_port) = http_port {
        let main_server = http_main_server(
            initial_state,
            static_root_dir,
            skip_compress_mime,
            cache_ttl_sec,
            cache_tti_sec,
            inline_css_path,
            immutable_path,
        )
        .unwrap();
        if let Some(https_port) = https_port {
            if domain.is_none() || acme_dir.is_none() || acme_email.is_none() {
                anyhow::bail!(
                    "With --https-port also --domain, --acme-email and --acme-dir are required"
                );
            }
            let domain = domain.unwrap();
            let acme_dir = acme_dir.unwrap();
            let acme_email = acme_email.unwrap();
            let redirect_server =
                http_redirect_server(format!("https://{}:{}", &domain, &https_port).as_str())
                    .unwrap();
            let redirect_listener = redirect_server.listen(format!("0.0.0.0:{}", &http_port));
            let main_listener = main_server.listen(
                tide_rustls::TlsListener::build()
                    .addrs(format!("0.0.0.0:{}", &https_port))
                    .acme(
                        AcmeConfig::new(vec![domain])
                            .contact_push(format!("mailto:{}", acme_email))
                            .cache(DirCache::new(acme_dir)),
                    ),
            );

            if let Some(tcp_port) = tcp_port {
                let tcp_listener = tcp::listen(format!("0.0.0.0:{}", tcp_port), engine);
                futures::try_join!(main_listener, redirect_listener, tcp_listener)?;
            } else {
                futures::try_join!(main_listener, redirect_listener)?;
            }

            // TODO
        } else {
            let main_listener = main_server.listen(format!("0.0.0.0:{}", &http_port));
            if let Some(tcp_port) = tcp_port {
                let tcp_listener = tcp::listen(format!("0.0.0.0:{}", tcp_port), engine);
                futures::try_join!(main_listener, tcp_listener)?;
            } else {
                main_listener.await?;
            }
        }
    } else {
        if let Some(tcp_port) = tcp_port {
            tcp::listen(format!("0.0.0.0:{}", tcp_port), engine).await?;
        } else {
            anyhow::bail!("Either --tcp-port or --http-port is mandatory");
        };
    }

    Ok(())
}
<commit_msg>Use production as staging does work<commit_after>use crate::common::State;
use crate::http::{http_main_server, http_redirect_server};
use anyhow::Result;
use extendedmind_engine::tcp;
use std::path::PathBuf;
use tide_acme::rustls_acme::caches::DirCache;
use tide_acme::{AcmeConfig, TideRustlsExt};

pub async fn start_server(
    initial_state: State,
    static_root_dir: Option<PathBuf>,
    http_port: Option<u16>,
    https_port: Option<u16>,
    domain: Option<String>,
    acme_email: Option<String>,
    acme_dir: Option<String>,
    tcp_port: Option<u16>,
    skip_compress_mime: Option<Vec<String>>,
    cache_ttl_sec: Option<u64>,
    cache_tti_sec: Option<u64>,
    inline_css_path: Option<Vec<String>>,
    immutable_path: Option<Vec<String>>,
) -> Result<()> {
    let engine = initial_state.engine.clone();

    if let Some(http_port) = http_port {
        let main_server = http_main_server(
            initial_state,
            static_root_dir,
            skip_compress_mime,
            cache_ttl_sec,
            cache_tti_sec,
            inline_css_path,
            immutable_path,
        )
        .unwrap();
        if let Some(https_port) = https_port {
            if domain.is_none() || acme_dir.is_none() || acme_email.is_none() {
                anyhow::bail!(
                    "With --https-port also --domain, --acme-email and --acme-dir are required"
                );
            }
            let domain = domain.unwrap();
            let acme_dir = acme_dir.unwrap();
            let acme_email = acme_email.unwrap();
            let redirect_server =
                http_redirect_server(format!("https://{}:{}", &domain, &https_port).as_str())
                    .unwrap();
            let redirect_listener = redirect_server.listen(format!("0.0.0.0:{}", &http_port));
            let main_listener = main_server.listen(
                tide_rustls::TlsListener::build()
                    .addrs(format!("0.0.0.0:{}", &https_port))
                    .acme(
                        AcmeConfig::new(vec![domain])
                            .contact_push(format!("mailto:{}", acme_email))
                            .cache(DirCache::new(acme_dir))
                            .directory_lets_encrypt(true),
                    ),
            );

            if let Some(tcp_port) = tcp_port {
                let tcp_listener = tcp::listen(format!("0.0.0.0:{}", tcp_port), engine);
                futures::try_join!(main_listener, redirect_listener, tcp_listener)?;
            } else {
                futures::try_join!(main_listener, redirect_listener)?;
            }

            // TODO
        } else {
            let main_listener = main_server.listen(format!("0.0.0.0:{}", &http_port));
            if let Some(tcp_port) = tcp_port {
                let tcp_listener = tcp::listen(format!("0.0.0.0:{}", tcp_port), engine);
                futures::try_join!(main_listener, tcp_listener)?;
            } else {
                main_listener.await?;
            }
        }
    } else {
        if let Some(tcp_port) = tcp_port {
            tcp::listen(format!("0.0.0.0:{}", tcp_port), engine).await?;
        } else {
            anyhow::bail!("Either --tcp-port or --http-port is mandatory");
        };
    }

    Ok(())
}
<|endoftext|>


<commit_before>// TODO: everything
<commit_msg>Too Many Linked Lists, ch2<commit_after>// https://github.com/cis198-2016s/homework/tree/master/hw02
// http://cglab.ca/~abeinges/blah/too-many-lists/book/

use std::mem;

pub struct List {
    head: Link,
}

enum Link {
    Empty,
    More(Box<Node>),
}

struct Node {
    elem: i32,
    next: Link,
}

impl List {
    pub fn new() -> Self {
        List { head: Link::Empty }
    }

    pub fn push(&mut self, elem: i32) {
        let new_node = Box::new(Node {
            elem: elem,
            next: mem::replace(&mut self.head, Link::Empty),
        });
        self.head = Link::More(new_node);
    }

    pub fn pop(&mut self) -> Option<i32> {
        match mem::replace(&mut self.head, Link::Empty) {
            Link::Empty => { None },
            Link::More(boxed_node) => {
                let node = *boxed_node;
                self.head = node.next;
                Some(node.elem)
            },
        }
    }

}

impl Drop for List {
    fn drop(&mut self) {
        let mut cur_link = mem::replace(&mut self.head, Link::Empty);
        // `while let` == "do this thing until this pattern doesn't match"
        while let Link::More(mut boxed_node) = cur_link {
            cur_link = mem::replace(&mut boxed_node.next, Link::Empty);
            // boxed_node goes out of scope and gets dropped here;
            // but its Node's `next` field has been set to Link::Empty
            // so no unbounded recursion occurs.
        }
    }
}

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

    #[test]
    fn basics() {
        let mut list = List::new();

        // Check empty list behaves right
        assert_eq!(list.pop(), None);

        // Populate list
        list.push(1);
        list.push(2);
        list.push(3);

        // Check normal removal
        assert_eq!(list.pop(), Some(3));
        assert_eq!(list.pop(), Some(2));

        // Push some more just to make sure nothing's corrupted
        list.push(4);
        list.push(5);

        // Check normal removal
        assert_eq!(list.pop(), Some(5));
        assert_eq!(list.pop(), Some(4));

        // Check exhaustion
        assert_eq!(list.pop(), Some(1));
        assert_eq!(list.pop(), None);
    }
}
<|endoftext|>