<commit_before>// Copyright (c) 2017-2019 Rene van der Meer
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

// gpio_status.rs
//
// Retrieves the mode and logic level for each of the pins on the 26-pin or
// 40-pin GPIO header, and displays the results in an ASCII table.

use std::fmt;
use std::process::exit;

use rppal::gpio::Gpio;
use rppal::system::{DeviceInfo, Model};

enum PinType {
    Gpio(u8),
    Ground,
    Power3v3,
    Power5v,
}

impl fmt::Display for PinType {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            PinType::Gpio(pin) => write!(f, "GPIO{}", pin),
            PinType::Ground => write!(f, "{:<5}", "GND"),
            PinType::Power3v3 => write!(f, "{:<5}", "3.3 V"),
            PinType::Power5v => write!(f, "{:<5}", "5 V"),
        }
    }
}

const HEADER: [PinType; 40] = [
    PinType::Power3v3, // Physical pin 1
    PinType::Power5v,  // Physical pin 2
    PinType::Gpio(2),  // Physical pin 3
    PinType::Power5v,  // Physical pin 4
    PinType::Gpio(3),  // Physical pin 5
    PinType::Ground,   // Physical pin 6
    PinType::Gpio(4),  // Physical pin 7
    PinType::Gpio(14), // Physical pin 8
    PinType::Ground,   // Physical pin 9
    PinType::Gpio(15), // Physical pin 10
    PinType::Gpio(17), // Physical pin 11
    PinType::Gpio(18), // Physical pin 12
    PinType::Gpio(27), // Physical pin 13
    PinType::Ground,   // Physical pin 14
    PinType::Gpio(22), // Physical pin 15
    PinType::Gpio(23), // Physical pin 16
    PinType::Power3v3, // Physical pin 17
    PinType::Gpio(24), // Physical pin 18
    PinType::Gpio(10), // Physical pin 19
    PinType::Ground,   // Physical pin 20
    PinType::Gpio(9),  // Physical pin 21
    PinType::Gpio(25), // Physical pin 22
    PinType::Gpio(11), // Physical pin 23
    PinType::Gpio(8),  // Physical pin 24
    PinType::Ground,   // Physical pin 25
    PinType::Gpio(7),  // Physical pin 26
    PinType::Gpio(0),  // Physical pin 27
    PinType::Gpio(1),  // Physical pin 28
    PinType::Gpio(5),  // Physical pin 29
    PinType::Ground,   // Physical pin 30
    PinType::Gpio(6),  // Physical pin 31
    PinType::Gpio(12), // Physical pin 32
    PinType::Gpio(13), // Physical pin 33
    PinType::Ground,   // Physical pin 34
    PinType::Gpio(19), // Physical pin 35
    PinType::Gpio(16), // Physical pin 36
    PinType::Gpio(26), // Physical pin 37
    PinType::Gpio(20), // Physical pin 38
    PinType::Ground,   // Physical pin 39
    PinType::Gpio(21), // Physical pin 40
];

const MAX_PINS_SHORT: usize = 26;
const MAX_PINS_LONG: usize = 40;

fn format_pin(
    buf: &mut String,
    pin: usize,
    gpio: impl fmt::Display,
    mode: impl fmt::Display,
    level: impl fmt::Display,
) {
    if pin % 2 != 0 {
        buf.push_str(&format!(
            "| {:>4} | {:<5} | {:>1} | {:>2} |",
            gpio, mode, level, pin
        ));
    } else {
        buf.push_str(&format!(
            " {:>2} | {:>1} | {:<5} | {:>4} |\n",
            pin, level, mode, gpio
        ));
    }
}

fn print_header(max: usize) {
    let gpio = Gpio::new().unwrap_or_else(|e| {
        eprintln!("Error: Can't access GPIO peripheral ({})", e);
        exit(1);
    });

    let mut buf = String::with_capacity(1600);

    buf.push_str("+------+-------+---+---------+---+-------+------+\n");
    buf.push_str("| GPIO | Mode  | L |   Pin   | L | Mode  | GPIO |\n");
    buf.push_str("+------+-------+---+----+----+---+-------+------+\n");

    for (idx, pin_type) in HEADER.iter().take(max).enumerate() {
        match pin_type {
            PinType::Gpio(bcm_gpio) => {
                let pin = gpio.get(*bcm_gpio).unwrap_or_else(|| {
                    eprintln!("Error: Can't access GPIO pin {}", *bcm_gpio);
                    exit(1);
                });

                format_pin(
                    &mut buf,
                    idx + 1,
                    bcm_gpio,
                    format!("{}", pin.mode()).to_uppercase(),
                    pin.read() as u8,
                );
            }
            _ => format_pin(&mut buf, idx + 1, "", pin_type, ""),
        };
    }

    buf.push_str("+------+-------+---+----+----+---+-------+------+\n");

    print!("{}", buf);
}

fn main() {
    match DeviceInfo::new()
        .unwrap_or_else(|e| {
            eprintln!("Error: Can't identify Raspberry Pi model ({})", e);
            exit(1);
        })
        .model()
    {
        Model::RaspberryPiA | Model::RaspberryPiBRev1 | Model::RaspberryPiBRev2 => {
            print_header(MAX_PINS_SHORT)
        }
        Model::RaspberryPiAPlus
        | Model::RaspberryPiBPlus
        | Model::RaspberryPi2B
        | Model::RaspberryPi3APlus
        | Model::RaspberryPi3B
        | Model::RaspberryPi3BPlus
        | Model::RaspberryPiZero
        | Model::RaspberryPiZeroW => print_header(MAX_PINS_LONG),
        model => {
            eprintln!("Error: No GPIO header information available for {}", model);
            exit(1);
        }
    }
}
<commit_msg>Update documentation<commit_after>// Copyright (c) 2017-2019 Rene van der Meer
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

// gpio_status.rs
//
// Retrieves the mode and logic level for each of the pins on the 26-pin or
// 40-pin GPIO header, and displays the results in an ASCII table.

use std::fmt;
use std::process::exit;

use rppal::gpio::Gpio;
use rppal::system::{DeviceInfo, Model};

enum PinType {
    Gpio(u8),
    Ground,
    Power3v3,
    Power5v,
}

impl fmt::Display for PinType {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            PinType::Gpio(pin) => write!(f, "GPIO{}", pin),
            PinType::Ground => write!(f, "{:<5}", "GND"),
            PinType::Power3v3 => write!(f, "{:<5}", "3.3 V"),
            PinType::Power5v => write!(f, "{:<5}", "5 V"),
        }
    }
}

const HEADER: [PinType; 40] = [
    PinType::Power3v3, // Physical pin 1
    PinType::Power5v,  // Physical pin 2
    PinType::Gpio(2),  // Physical pin 3
    PinType::Power5v,  // Physical pin 4
    PinType::Gpio(3),  // Physical pin 5
    PinType::Ground,   // Physical pin 6
    PinType::Gpio(4),  // Physical pin 7
    PinType::Gpio(14), // Physical pin 8
    PinType::Ground,   // Physical pin 9
    PinType::Gpio(15), // Physical pin 10
    PinType::Gpio(17), // Physical pin 11
    PinType::Gpio(18), // Physical pin 12
    PinType::Gpio(27), // Physical pin 13
    PinType::Ground,   // Physical pin 14
    PinType::Gpio(22), // Physical pin 15
    PinType::Gpio(23), // Physical pin 16
    PinType::Power3v3, // Physical pin 17
    PinType::Gpio(24), // Physical pin 18
    PinType::Gpio(10), // Physical pin 19
    PinType::Ground,   // Physical pin 20
    PinType::Gpio(9),  // Physical pin 21
    PinType::Gpio(25), // Physical pin 22
    PinType::Gpio(11), // Physical pin 23
    PinType::Gpio(8),  // Physical pin 24
    PinType::Ground,   // Physical pin 25
    PinType::Gpio(7),  // Physical pin 26
    PinType::Gpio(0),  // Physical pin 27
    PinType::Gpio(1),  // Physical pin 28
    PinType::Gpio(5),  // Physical pin 29
    PinType::Ground,   // Physical pin 30
    PinType::Gpio(6),  // Physical pin 31
    PinType::Gpio(12), // Physical pin 32
    PinType::Gpio(13), // Physical pin 33
    PinType::Ground,   // Physical pin 34
    PinType::Gpio(19), // Physical pin 35
    PinType::Gpio(16), // Physical pin 36
    PinType::Gpio(26), // Physical pin 37
    PinType::Gpio(20), // Physical pin 38
    PinType::Ground,   // Physical pin 39
    PinType::Gpio(21), // Physical pin 40
];

const MAX_PINS_SHORT: usize = 26;
const MAX_PINS_LONG: usize = 40;

fn format_pin(
    buf: &mut String,
    pin: usize,
    gpio: impl fmt::Display,
    mode: impl fmt::Display,
    level: impl fmt::Display,
) {
    if pin % 2 != 0 {
        buf.push_str(&format!(
            "| {:>4} | {:<5} | {:>1} | {:>2} |",
            gpio, mode, level, pin
        ));
    } else {
        buf.push_str(&format!(
            " {:>2} | {:>1} | {:<5} | {:>4} |\n",
            pin, level, mode, gpio
        ));
    }
}

fn print_header(max: usize) {
    let gpio = Gpio::new().unwrap_or_else(|e| {
        eprintln!("Error: Can't access GPIO peripheral ({})", e);
        exit(1);
    });

    let mut buf = String::with_capacity(1600);

    buf.push_str("+------+-------+---+---------+---+-------+------+\n");
    buf.push_str("| GPIO | Mode  | L |   Pin   | L | Mode  | GPIO |\n");
    buf.push_str("+------+-------+---+----+----+---+-------+------+\n");

    for (idx, pin_type) in HEADER.iter().take(max).enumerate() {
        match pin_type {
            PinType::Gpio(bcm_gpio) => {
                // Retrieve a Pin without converting it into an InputPin,
                // OutputPin or IoPin, so we can read the pin's current level
                // and mode without affecting its state.
                let pin = gpio.get(*bcm_gpio).unwrap_or_else(|| {
                    eprintln!("Error: Can't access GPIO pin {}", *bcm_gpio);
                    exit(1);
                });

                format_pin(
                    &mut buf,
                    idx + 1,
                    bcm_gpio,
                    format!("{}", pin.mode()).to_uppercase(),
                    pin.read() as u8,
                );
            }
            _ => format_pin(&mut buf, idx + 1, "", pin_type, ""),
        };
    }

    buf.push_str("+------+-------+---+----+----+---+-------+------+\n");

    print!("{}", buf);
}

fn main() {
    // Identify the Pi's model, so we can print the appropriate GPIO header.
    match DeviceInfo::new()
        .unwrap_or_else(|e| {
            eprintln!("Error: Can't identify Raspberry Pi model ({})", e);
            exit(1);
        })
        .model()
    {
        // The GPIO header on the earlier Pi models overlaps with the first 26 pins
        // of the 40-pin header on the newer models.
        Model::RaspberryPiA | Model::RaspberryPiBRev1 | Model::RaspberryPiBRev2 => {
            print_header(MAX_PINS_SHORT)
        }
        Model::RaspberryPiAPlus
        | Model::RaspberryPiBPlus
        | Model::RaspberryPi2B
        | Model::RaspberryPi3APlus
        | Model::RaspberryPi3B
        | Model::RaspberryPi3BPlus
        | Model::RaspberryPiZero
        | Model::RaspberryPiZeroW => print_header(MAX_PINS_LONG),
        model => {
            eprintln!("Error: No GPIO header information available for {}", model);
            exit(1);
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 Nicholas Bishop
//
// Closest-point method adapted from "Real-Time Collision Detection"
// by Christer Ericson, published by Morgan Kaufmann Publishers,
// Copyright 2005 Elsevier Inc
//
// 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
//
//     http://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 vector::{distance3, dot3, Vec3f};

/// Line segment between two points
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Segment3f {
    pub start: Vec3f,
    pub end: Vec3f
}

impl Segment3f {
    pub fn new(start: &Vec3f, end: &Vec3f) -> Segment3f {
        Segment3f {
            start: *start,
            end: *end
        }
    }

    pub fn length(&self) -> f32 {
        distance3(self.start, self.end)
    }

    /// Find the point on the segment closest to the input point. The
    /// return value contains both the parametric and actual location
    /// of the closest point.
    ///
    /// Adapted from "Real-Time Collision Detection" by Christer
    /// Ericson, published by Morgan Kaufmann Publishers, Copyright
    /// 2005 Elsevier Inc
    pub fn closest_point_to_point(&self, point: &Vec3f) -> (f32, Vec3f) {
        let a = self.start;
        let b = self.end;

        let ab = b - a;
        // Project point onto ab, but deferring divide by dot3(ab, ab)
        let t = dot3(*point - a, ab);
        if t <= 0.0f32 {
            // point projects outside the [a,b] interval, on the a
            // side; clamp to a
            (0.0f32, a)
        } else {
            // Always nonnegative since denom = ||ab|| ∧ 2
            let denom = dot3(ab, ab);
            if t >= denom {
                // point projects outside the [a,b] interval, on the b
                // side; clamp to b
                (1.0f32, b)
            } else {
                // point projects inside the [a,b] interval; must do
                // deferred divide now
                (t / denom, a + ab * t)
            }
        }
    }
}

#[test]
fn test_segment_length() {
    use vector::vec3f;
    let l = Segment3f::new(&vec3f(0, 0, 0),
                           &vec3f(0, 0, 9));
    assert!(l.length() == 9.0);
}

#[test]
fn test_segment_closest_point_to_point() {

}
<commit_msg>Update segment tests<commit_after>// Copyright 2015 Nicholas Bishop
//
// Closest-point method adapted from "Real-Time Collision Detection"
// by Christer Ericson, published by Morgan Kaufmann Publishers,
// Copyright 2005 Elsevier Inc
//
// 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
//
//     http://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 vector::{distance3, dot3, Vec3f};

/// Line segment between two points
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Segment3f {
    pub start: Vec3f,
    pub end: Vec3f
}

impl Segment3f {
    pub fn new(start: &Vec3f, end: &Vec3f) -> Segment3f {
        Segment3f {
            start: *start,
            end: *end
        }
    }

    pub fn length(&self) -> f32 {
        distance3(self.start, self.end)
    }

    /// Find the point on the segment closest to the input point. The
    /// return value contains both the parametric and actual location
    /// of the closest point.
    ///
    /// Adapted from "Real-Time Collision Detection" by Christer
    /// Ericson, published by Morgan Kaufmann Publishers, Copyright
    /// 2005 Elsevier Inc
    pub fn closest_point_to_point(&self, point: &Vec3f) -> (f32, Vec3f) {
        let a = self.start;
        let b = self.end;

        let ab = b - a;
        // Project point onto ab, but deferring divide by dot3(ab, ab)
        let t = dot3(*point - a, ab);
        if t <= 0.0f32 {
            // point projects outside the [a,b] interval, on the a
            // side; clamp to a
            (0.0f32, a)
        } else {
            // Always nonnegative since denom = ||ab|| ∧ 2
            let denom = dot3(ab, ab);
            if t >= denom {
                // point projects outside the [a,b] interval, on the b
                // side; clamp to b
                (1.0f32, b)
            } else {
                // point projects inside the [a,b] interval; must do
                // deferred divide now
                (t / denom, a + ab * t)
            }
        }
    }
}

#[test]
fn test_segment_length() {
    use vector::vec3f;
    let s = Segment3f::new(&vec3f(0, 0, 0),
                           &vec3f(0, 0, 9));
    assert!(s.length() == 9.0);
}

#[test]
fn test_segment_closest_point_to_point() {
    use vector::vec3f;
    let s = Segment3f::new(&vec3f(2, 0, 0),
                           &vec3f(3, 0, 0));
    assert!(s.closest_point_to_point(&vec3f(1, 0, 0)) ==
            (0.0, vec3f(2, 0, 0)));
    assert!(s.closest_point_to_point(&vec3f(4, 0, 0)) ==
            (1.0, vec3f(3, 0, 0)));
    assert!(s.closest_point_to_point(&vec3f(2.5, 1, 0)) ==
            (0.5, vec3f(2.5, 0, 0)));
}
<|endoftext|>


<commit_before>//! This is an implementation of a global allocator on the wasm32 platform when
//! emscripten is not in use. In that situation there's no actual runtime for us
//! to lean on for allocation, so instead we provide our own!
//!
//! The wasm32 instruction set has two instructions for getting the current
//! amount of memory and growing the amount of memory. These instructions are the
//! foundation on which we're able to build an allocator, so we do so! Note that
//! the instructions are also pretty "global" and this is the "global" allocator
//! after all!
//!
//! The current allocator here is the `dlmalloc` crate which we've got included
//! in the rust-lang/rust repository as a submodule. The crate is a port of
//! dlmalloc.c from C to Rust and is basically just so we can have "pure Rust"
//! for now which is currently technically required (can't link with C yet).
//!
//! The crate itself provides a global allocator which on wasm has no
//! synchronization as there are no threads!

use crate::alloc::{GlobalAlloc, Layout, System};

static mut DLMALLOC: dlmalloc::Dlmalloc = dlmalloc::DLMALLOC_INIT;

#[stable(feature = "alloc_system_type", since = "1.28.0")]
unsafe impl GlobalAlloc for System {
    #[inline]
    unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
        let _lock = lock::lock();
        unsafe { DLMALLOC.malloc(layout.size(), layout.align()) }
    }

    #[inline]
    unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
        let _lock = lock::lock();
        unsafe { DLMALLOC.calloc(layout.size(), layout.align()) }
    }

    #[inline]
    unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
        let _lock = lock::lock();
        unsafe { DLMALLOC.free(ptr, layout.size(), layout.align()) }
    }

    #[inline]
    unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
        let _lock = lock::lock();
        unsafe { DLMALLOC.realloc(ptr, layout.size(), layout.align(), new_size) }
    }
}

#[cfg(target_feature = "atomics")]
mod lock {
    use crate::sync::atomic::{AtomicI32, Ordering::SeqCst};

    static LOCKED: AtomicI32 = AtomicI32::new(0);

    pub struct DropLock;

    pub fn lock() -> DropLock {
        loop {
            if LOCKED.swap(1, SeqCst) == 0 {
                return DropLock;
            }
            // Ok so here's where things get a little depressing. At this point
            // in time we need to synchronously acquire a lock, but we're
            // contending with some other thread. Typically we'd execute some
            // form of `i32.atomic.wait` like so:
            //
            //     unsafe {
            //         let r = core::arch::wasm32::i32_atomic_wait(
            //             LOCKED.as_mut_ptr(),
            //             1,  //     expected value
            //             -1, //     timeout
            //         );
            //         debug_assert!(r == 0 || r == 1);
            //     }
            //
            // Unfortunately though in doing so we would cause issues for the
            // main thread. The main thread in a web browser *cannot ever
            // block*, no exceptions. This means that the main thread can't
            // actually execute the `i32.atomic.wait` instruction.
            //
            // As a result if we want to work within the context of browsers we
            // need to figure out some sort of allocation scheme for the main
            // thread where when there's contention on the global malloc lock we
            // do... something.
            //
            // Possible ideas include:
            //
            // 1. Attempt to acquire the global lock. If it fails, fall back to
            //    memory allocation via `memory.grow`. Later just ... somehow
            //    ... inject this raw page back into the main allocator as it
            //    gets sliced up over time. This strategy has the downside of
            //    forcing allocation of a page to happen whenever the main
            //    thread contents with other threads, which is unfortunate.
            //
            // 2. Maintain a form of "two level" allocator scheme where the main
            //    thread has its own allocator. Somehow this allocator would
            //    also be balanced with a global allocator, not only to have
            //    allocations cross between threads but also to ensure that the
            //    two allocators stay "balanced" in terms of free'd memory and
            //    such. This, however, seems significantly complicated.
            //
            // Out of a lack of other ideas, the current strategy implemented
            // here is to simply spin. Typical spin loop algorithms have some
            // form of "hint" here to the CPU that it's what we're doing to
            // ensure that the CPU doesn't get too hot, but wasm doesn't have
            // such an instruction.
            //
            // To be clear, spinning here is not a great solution.
            // Another thread with the lock may take quite a long time to wake
            // up. For example it could be in `memory.grow` or it could be
            // evicted from the CPU for a timeslice like 10ms. For these periods
            // of time our thread will "helpfully" sit here and eat CPU time
            // until it itself is evicted or the lock holder finishes. This
            // means we're just burning and wasting CPU time to no one's
            // benefit.
            //
            // Spinning does have the nice properties, though, of being
            // semantically correct, being fair to all threads for memory
            // allocation, and being simple enough to implement.
            //
            // This will surely (hopefully) be replaced in the future with a
            // real memory allocator that can handle the restriction of the main
            // thread.
            //
            //
            // FIXME: We can also possibly add an optimization here to detect
            // when a thread is the main thread or not and block on all
            // non-main-thread threads. Currently, however, we have no way
            // of knowing which wasm thread is on the browser main thread, but
            // if we could figure out we could at least somewhat mitigate the
            // cost of this spinning.
        }
    }

    impl Drop for DropLock {
        fn drop(&mut self) {
            let r = LOCKED.swap(0, SeqCst);
            debug_assert_eq!(r, 1);

            // Note that due to the above logic we don't actually need to wake
            // anyone up, but if we did it'd likely look something like this:
            //
            //     unsafe {
            //         core::arch::wasm32::atomic_notify(
            //             LOCKED.as_mut_ptr(),
            //             1, //     only one thread
            //         );
            //     }
        }
    }
}

#[cfg(not(target_feature = "atomics"))]
mod lock {
    #[inline]
    pub fn lock() {} // no atomics, no threads, that's easy!
}
<commit_msg>Add some description for (malloc/calloc/free/realloc)<commit_after>//! This is an implementation of a global allocator on the wasm32 platform when
//! emscripten is not in use. In that situation there's no actual runtime for us
//! to lean on for allocation, so instead we provide our own!
//!
//! The wasm32 instruction set has two instructions for getting the current
//! amount of memory and growing the amount of memory. These instructions are the
//! foundation on which we're able to build an allocator, so we do so! Note that
//! the instructions are also pretty "global" and this is the "global" allocator
//! after all!
//!
//! The current allocator here is the `dlmalloc` crate which we've got included
//! in the rust-lang/rust repository as a submodule. The crate is a port of
//! dlmalloc.c from C to Rust and is basically just so we can have "pure Rust"
//! for now which is currently technically required (can't link with C yet).
//!
//! The crate itself provides a global allocator which on wasm has no
//! synchronization as there are no threads!

use crate::alloc::{GlobalAlloc, Layout, System};

static mut DLMALLOC: dlmalloc::Dlmalloc = dlmalloc::DLMALLOC_INIT;

#[stable(feature = "alloc_system_type", since = "1.28.0")]
unsafe impl GlobalAlloc for System {
    #[inline]
    unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
        // SAFETY:  DLMALLOC.malloc() is guranteed to be safe since lock::lock() aqcuire a globl lock
        let _lock = lock::lock();
        unsafe { DLMALLOC.malloc(layout.size(), layout.align()) }
    }

    #[inline]
    unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
        // SAFETY:  DLMALLOC.calloc() is guranteed to be safe since lock::lock() aqcuire a globl lock
        let _lock = lock::lock();
        unsafe { DLMALLOC.calloc(layout.size(), layout.align()) }
    }

    #[inline]
    unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
        // SAFETY:  DLMALLOC.free() is guranteed to be safe since lock::lock() aqcuire a globl lock
        let _lock = lock::lock();
        unsafe { DLMALLOC.free(ptr, layout.size(), layout.align()) }
    }

    #[inline]
    unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
        // SAFETY:  DLMALLOC.realloc() is guranteed to be safe since lock::lock() aqcuire a globl lock
        let _lock = lock::lock();
        unsafe { DLMALLOC.realloc(ptr, layout.size(), layout.align(), new_size) }
    }
}

#[cfg(target_feature = "atomics")]
mod lock {
    use crate::sync::atomic::{AtomicI32, Ordering::SeqCst};

    static LOCKED: AtomicI32 = AtomicI32::new(0);

    pub struct DropLock;

    pub fn lock() -> DropLock {
        loop {
            if LOCKED.swap(1, SeqCst) == 0 {
                return DropLock;
            }
            // Ok so here's where things get a little depressing. At this point
            // in time we need to synchronously acquire a lock, but we're
            // contending with some other thread. Typically we'd execute some
            // form of `i32.atomic.wait` like so:
            //
            //     unsafe {
            //         let r = core::arch::wasm32::i32_atomic_wait(
            //             LOCKED.as_mut_ptr(),
            //             1,  //     expected value
            //             -1, //     timeout
            //         );
            //         debug_assert!(r == 0 || r == 1);
            //     }
            //
            // Unfortunately though in doing so we would cause issues for the
            // main thread. The main thread in a web browser *cannot ever
            // block*, no exceptions. This means that the main thread can't
            // actually execute the `i32.atomic.wait` instruction.
            //
            // As a result if we want to work within the context of browsers we
            // need to figure out some sort of allocation scheme for the main
            // thread where when there's contention on the global malloc lock we
            // do... something.
            //
            // Possible ideas include:
            //
            // 1. Attempt to acquire the global lock. If it fails, fall back to
            //    memory allocation via `memory.grow`. Later just ... somehow
            //    ... inject this raw page back into the main allocator as it
            //    gets sliced up over time. This strategy has the downside of
            //    forcing allocation of a page to happen whenever the main
            //    thread contents with other threads, which is unfortunate.
            //
            // 2. Maintain a form of "two level" allocator scheme where the main
            //    thread has its own allocator. Somehow this allocator would
            //    also be balanced with a global allocator, not only to have
            //    allocations cross between threads but also to ensure that the
            //    two allocators stay "balanced" in terms of free'd memory and
            //    such. This, however, seems significantly complicated.
            //
            // Out of a lack of other ideas, the current strategy implemented
            // here is to simply spin. Typical spin loop algorithms have some
            // form of "hint" here to the CPU that it's what we're doing to
            // ensure that the CPU doesn't get too hot, but wasm doesn't have
            // such an instruction.
            //
            // To be clear, spinning here is not a great solution.
            // Another thread with the lock may take quite a long time to wake
            // up. For example it could be in `memory.grow` or it could be
            // evicted from the CPU for a timeslice like 10ms. For these periods
            // of time our thread will "helpfully" sit here and eat CPU time
            // until it itself is evicted or the lock holder finishes. This
            // means we're just burning and wasting CPU time to no one's
            // benefit.
            //
            // Spinning does have the nice properties, though, of being
            // semantically correct, being fair to all threads for memory
            // allocation, and being simple enough to implement.
            //
            // This will surely (hopefully) be replaced in the future with a
            // real memory allocator that can handle the restriction of the main
            // thread.
            //
            //
            // FIXME: We can also possibly add an optimization here to detect
            // when a thread is the main thread or not and block on all
            // non-main-thread threads. Currently, however, we have no way
            // of knowing which wasm thread is on the browser main thread, but
            // if we could figure out we could at least somewhat mitigate the
            // cost of this spinning.
        }
    }

    impl Drop for DropLock {
        fn drop(&mut self) {
            let r = LOCKED.swap(0, SeqCst);
            debug_assert_eq!(r, 1);

            // Note that due to the above logic we don't actually need to wake
            // anyone up, but if we did it'd likely look something like this:
            //
            //     unsafe {
            //         core::arch::wasm32::atomic_notify(
            //             LOCKED.as_mut_ptr(),
            //             1, //     only one thread
            //         );
            //     }
        }
    }
}

#[cfg(not(target_feature = "atomics"))]
mod lock {
    #[inline]
    pub fn lock() {} // no atomics, no threads, that's easy!
}
<|endoftext|>


<commit_before>use std::io;
use std::marker::PhantomData;

use actix_net::codec::Framed;
use actix_net::service::{NewService, Service};
use futures::future::{ok, Either, FutureResult};
use futures::{Async, AsyncSink, Future, Poll, Sink};
use tokio_io::AsyncWrite;

use error::ResponseError;
use h1::{Codec, Message};
use response::Response;

pub struct SendError<T, R, E>(PhantomData<(T, R, E)>);

impl<T, R, E> Default for SendError<T, R, E>
where
    T: AsyncWrite,
    E: ResponseError,
{
    fn default() -> Self {
        SendError(PhantomData)
    }
}

impl<T, R, E> NewService for SendError<T, R, E>
where
    T: AsyncWrite,
    E: ResponseError,
{
    type Request = Result<R, (E, Framed<T, Codec>)>;
    type Response = R;
    type Error = (E, Framed<T, Codec>);
    type InitError = ();
    type Service = SendError<T, R, E>;
    type Future = FutureResult<Self::Service, Self::InitError>;

    fn new_service(&self) -> Self::Future {
        ok(SendError(PhantomData))
    }
}

impl<T, R, E> Service for SendError<T, R, E>
where
    T: AsyncWrite,
    E: ResponseError,
{
    type Request = Result<R, (E, Framed<T, Codec>)>;
    type Response = R;
    type Error = (E, Framed<T, Codec>);
    type Future = Either<FutureResult<R, (E, Framed<T, Codec>)>, SendErrorFut<T, R, E>>;

    fn poll_ready(&mut self) -> Poll<(), Self::Error> {
        Ok(Async::Ready(()))
    }

    fn call(&mut self, req: Self::Request) -> Self::Future {
        match req {
            Ok(r) => Either::A(ok(r)),
            Err((e, framed)) => Either::B(SendErrorFut {
                framed: Some(framed),
                res: Some(Message::Item(e.error_response())),
                err: Some(e),
                _t: PhantomData,
            }),
        }
    }
}

pub struct SendErrorFut<T, R, E> {
    res: Option<Message<Response>>,
    framed: Option<Framed<T, Codec>>,
    err: Option<E>,
    _t: PhantomData<R>,
}

impl<T, R, E> Future for SendErrorFut<T, R, E>
where
    E: ResponseError,
    T: AsyncWrite,
{
    type Item = R;
    type Error = (E, Framed<T, Codec>);

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        if let Some(res) = self.res.take() {
            match self.framed.as_mut().unwrap().start_send(res) {
                Ok(AsyncSink::Ready) => (),
                Ok(AsyncSink::NotReady(res)) => {
                    self.res = Some(res);
                    return Ok(Async::NotReady);
                }
                Err(_) => {
                    return Err((self.err.take().unwrap(), self.framed.take().unwrap()))
                }
            }
        }
        match self.framed.as_mut().unwrap().poll_complete() {
            Ok(Async::Ready(_)) => {
                Err((self.err.take().unwrap(), self.framed.take().unwrap()))
            }
            Ok(Async::NotReady) => Ok(Async::NotReady),
            Err(_) => Err((self.err.take().unwrap(), self.framed.take().unwrap())),
        }
    }
}

pub struct SendResponse<T>(PhantomData<(T,)>);

impl<T> Default for SendResponse<T>
where
    T: AsyncWrite,
{
    fn default() -> Self {
        SendResponse(PhantomData)
    }
}

impl<T> NewService for SendResponse<T>
where
    T: AsyncWrite,
{
    type Request = (Response, Framed<T, Codec>);
    type Response = Framed<T, Codec>;
    type Error = io::Error;
    type InitError = ();
    type Service = SendResponse<T>;
    type Future = FutureResult<Self::Service, Self::InitError>;

    fn new_service(&self) -> Self::Future {
        ok(SendResponse(PhantomData))
    }
}

impl<T> Service for SendResponse<T>
where
    T: AsyncWrite,
{
    type Request = (Response, Framed<T, Codec>);
    type Response = Framed<T, Codec>;
    type Error = io::Error;
    type Future = SendResponseFut<T>;

    fn poll_ready(&mut self) -> Poll<(), Self::Error> {
        Ok(Async::Ready(()))
    }

    fn call(&mut self, (res, framed): Self::Request) -> Self::Future {
        SendResponseFut {
            res: Some(Message::Item(res)),
            framed: Some(framed),
        }
    }
}

pub struct SendResponseFut<T> {
    res: Option<Message<Response>>,
    framed: Option<Framed<T, Codec>>,
}

impl<T> Future for SendResponseFut<T>
where
    T: AsyncWrite,
{
    type Item = Framed<T, Codec>;
    type Error = io::Error;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        if let Some(res) = self.res.take() {
            match self.framed.as_mut().unwrap().start_send(res)? {
                AsyncSink::Ready => (),
                AsyncSink::NotReady(res) => {
                    self.res = Some(res);
                    return Ok(Async::NotReady);
                }
            }
        }
        match self.framed.as_mut().unwrap().poll_complete()? {
            Async::Ready(_) => Ok(Async::Ready(self.framed.take().unwrap())),
            Async::NotReady => Ok(Async::NotReady),
        }
    }
}
<commit_msg>use error message<commit_after>use std::io;
use std::marker::PhantomData;

use actix_net::codec::Framed;
use actix_net::service::{NewService, Service};
use futures::future::{ok, Either, FutureResult};
use futures::{Async, AsyncSink, Future, Poll, Sink};
use tokio_io::AsyncWrite;

use error::ResponseError;
use h1::{Codec, Message};
use response::Response;

pub struct SendError<T, R, E>(PhantomData<(T, R, E)>);

impl<T, R, E> Default for SendError<T, R, E>
where
    T: AsyncWrite,
    E: ResponseError,
{
    fn default() -> Self {
        SendError(PhantomData)
    }
}

impl<T, R, E> NewService for SendError<T, R, E>
where
    T: AsyncWrite,
    E: ResponseError,
{
    type Request = Result<R, (E, Framed<T, Codec>)>;
    type Response = R;
    type Error = (E, Framed<T, Codec>);
    type InitError = ();
    type Service = SendError<T, R, E>;
    type Future = FutureResult<Self::Service, Self::InitError>;

    fn new_service(&self) -> Self::Future {
        ok(SendError(PhantomData))
    }
}

impl<T, R, E> Service for SendError<T, R, E>
where
    T: AsyncWrite,
    E: ResponseError,
{
    type Request = Result<R, (E, Framed<T, Codec>)>;
    type Response = R;
    type Error = (E, Framed<T, Codec>);
    type Future = Either<FutureResult<R, (E, Framed<T, Codec>)>, SendErrorFut<T, R, E>>;

    fn poll_ready(&mut self) -> Poll<(), Self::Error> {
        Ok(Async::Ready(()))
    }

    fn call(&mut self, req: Self::Request) -> Self::Future {
        match req {
            Ok(r) => Either::A(ok(r)),
            Err((e, framed)) => Either::B(SendErrorFut {
                framed: Some(framed),
                res: Some(Message::Item(e.response_with_message())),
                err: Some(e),
                _t: PhantomData,
            }),
        }
    }
}

pub struct SendErrorFut<T, R, E> {
    res: Option<Message<Response>>,
    framed: Option<Framed<T, Codec>>,
    err: Option<E>,
    _t: PhantomData<R>,
}

impl<T, R, E> Future for SendErrorFut<T, R, E>
where
    E: ResponseError,
    T: AsyncWrite,
{
    type Item = R;
    type Error = (E, Framed<T, Codec>);

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        if let Some(res) = self.res.take() {
            match self.framed.as_mut().unwrap().start_send(res) {
                Ok(AsyncSink::Ready) => (),
                Ok(AsyncSink::NotReady(res)) => {
                    self.res = Some(res);
                    return Ok(Async::NotReady);
                }
                Err(_) => {
                    return Err((self.err.take().unwrap(), self.framed.take().unwrap()))
                }
            }
        }
        match self.framed.as_mut().unwrap().poll_complete() {
            Ok(Async::Ready(_)) => {
                Err((self.err.take().unwrap(), self.framed.take().unwrap()))
            }
            Ok(Async::NotReady) => Ok(Async::NotReady),
            Err(_) => Err((self.err.take().unwrap(), self.framed.take().unwrap())),
        }
    }
}

pub struct SendResponse<T>(PhantomData<(T,)>);

impl<T> Default for SendResponse<T>
where
    T: AsyncWrite,
{
    fn default() -> Self {
        SendResponse(PhantomData)
    }
}

impl<T> NewService for SendResponse<T>
where
    T: AsyncWrite,
{
    type Request = (Response, Framed<T, Codec>);
    type Response = Framed<T, Codec>;
    type Error = io::Error;
    type InitError = ();
    type Service = SendResponse<T>;
    type Future = FutureResult<Self::Service, Self::InitError>;

    fn new_service(&self) -> Self::Future {
        ok(SendResponse(PhantomData))
    }
}

impl<T> Service for SendResponse<T>
where
    T: AsyncWrite,
{
    type Request = (Response, Framed<T, Codec>);
    type Response = Framed<T, Codec>;
    type Error = io::Error;
    type Future = SendResponseFut<T>;

    fn poll_ready(&mut self) -> Poll<(), Self::Error> {
        Ok(Async::Ready(()))
    }

    fn call(&mut self, (res, framed): Self::Request) -> Self::Future {
        SendResponseFut {
            res: Some(Message::Item(res)),
            framed: Some(framed),
        }
    }
}

pub struct SendResponseFut<T> {
    res: Option<Message<Response>>,
    framed: Option<Framed<T, Codec>>,
}

impl<T> Future for SendResponseFut<T>
where
    T: AsyncWrite,
{
    type Item = Framed<T, Codec>;
    type Error = io::Error;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        if let Some(res) = self.res.take() {
            match self.framed.as_mut().unwrap().start_send(res)? {
                AsyncSink::Ready => (),
                AsyncSink::NotReady(res) => {
                    self.res = Some(res);
                    return Ok(Async::NotReady);
                }
            }
        }
        match self.framed.as_mut().unwrap().poll_complete()? {
            Async::Ready(_) => Ok(Async::Ready(self.framed.take().unwrap())),
            Async::NotReady => Ok(Async::NotReady),
        }
    }
}
<|endoftext|>


<commit_before>
use time;
use std::io::Read;
use flate2::read::ZlibDecoder;
use rustc_serialize::json;

use baseconv::{Base, BaseConv};
use signing;

#[derive(Debug, PartialEq)]
pub enum SessionError {
    SessionInvalid,
    SignatureInvalid,
    SessionExpired,
}

pub fn validate(salt: &str, secret: &str, session_id: &str, max_age: Option<usize>) -> Result<String, SessionError> {
    let mut result = String::new();
    let mut parts: Vec<_> = session_id.rsplitn(2, ":").collect();
    let signature = parts[0];
    let data = parts[1];

    let data_signature = signing::base64_hmac(salt, secret, data);
    if data_signature != signature {
        return Err(SessionError::SignatureInvalid);
    }

    parts = data.rsplitn(2, ":").collect();
    let timestamp = parts[0];
    let payload = parts[1];

    if max_age != None {
        let min_secs = time::get_time().sec - max_age.unwrap_or(0) as i64;
        if BaseConv::new(Base::Base62).decode(timestamp) < min_secs {
            return Err(SessionError::SessionExpired);
        }
    }

    if payload.starts_with(".") {
        let b = match String::from_utf8(payload.as_bytes()[1..].to_vec()) {
            Ok(v) => v,
            Err(_) => return Err(SessionError::SessionInvalid),
        };
        let c = signing::b64_decode(&b[..]);
        let mut r = ZlibDecoder::new(&c[..]);
        if let Err(_) = r.read_to_string(&mut result) {
            return Err(SessionError::SessionInvalid);
        }
    } else {
        result = match String::from_utf8(signing::b64_decode(&payload[..])) {
            Ok(v) => v,
            Err(_) => return Err(SessionError::SessionInvalid),
        };
    }

    return Ok(result);
}

pub fn get_user_id(payload: &str) -> Option<usize> {
    let session_json = match json::Json::from_str(payload) {
        Ok(v) => v,
        Err(_) => return None,
    };

    let session_obj = match session_json.as_object() {
        Some(v) => v,
        None => return None,
    };

    match session_obj.get("_auth_user_id") {
        Some(v) => match v.as_u64() {
            Some(v) => Some(v as usize),
            None => None,
        },
        None => None,
    }
}

#[test]
fn test_session() {
    let salt = signing::DEFAULT_SALT;
    let secret = "6utnz%qfm$fs0legp)e@uxjlk-3hyo8sp4dc-y+x@z(!p=l@l9";
    let input = ".eJxVjrsSwiAQRf-F2kQgIWBKZ-y0s2cgbB4aQXk0Ov67ZEyT9u45e-8HmZuyg5NxesDbWUAtOiXvnrA_O2ucRTskVYqjTAG8HFUYM6F74MSIqtGE0w7zGvO-ogfWCE2ErjCvOAPQZCtr1d3Bmuz_O8vO2egnXS5IuV5DeXEG5uPKbh5M2SU5mbOd1LBsBVsMOlOzClF6eCUIMccUE1bgpsD8ikXL6pYy9P0Bm_FM9Q:1Z1WLZ:3jI2GHmVMInuElGvEsK7gVeR5io";
    let output = "{\"django_timezone\":\"Europe/London\",\"_auth_user_hash\":\"bfe71d836b172c07407f329568b18b307375eeb1\",\"_auth_user_backend\":\"django.contrib.auth.backends.ModelBackend\",\"_auth_user_id\":1,\"_language\":\"en-gb\",\"last_request\":\"2015-06-07T08:54:25\"}";

    assert_eq!(validate(salt, secret, input, None).unwrap(), output);

    assert_eq!(validate(salt, secret, input, Some(0)).err(), Some(SessionError::SessionExpired));

    assert_eq!(validate(salt, secret, input, Some(999999)).unwrap(), output);

    let session = validate(salt, secret, input, Some(999999)).unwrap();
    assert_eq!(get_user_id(&session), Some(1));
}
<commit_msg>Fix invalid session panic<commit_after>
use time;
use std::io::Read;
use flate2::read::ZlibDecoder;
use rustc_serialize::json;

use baseconv::{Base, BaseConv};
use signing;

#[derive(Debug, PartialEq)]
pub enum SessionError {
    SessionInvalid,
    SignatureInvalid,
    SessionExpired,
}

pub fn validate(salt: &str, secret: &str, session_id: &str, max_age: Option<usize>) -> Result<String, SessionError> {
    let mut result = String::new();
    let mut parts: Vec<_> = session_id.rsplitn(2, ":").collect();
    if parts.len() != 2 {
        return Err(SessionError::SessionInvalid);
    }
    let signature = parts[0];
    let data = parts[1];

    let data_signature = signing::base64_hmac(salt, secret, data);
    if data_signature != signature {
        return Err(SessionError::SignatureInvalid);
    }

    parts = data.rsplitn(2, ":").collect();
    if parts.len() != 2 {
        return Err(SessionError::SessionInvalid);
    }
    let timestamp = parts[0];
    let payload = parts[1];

    if max_age != None {
        let min_secs = time::get_time().sec - max_age.unwrap_or(0) as i64;
        if BaseConv::new(Base::Base62).decode(timestamp) < min_secs {
            return Err(SessionError::SessionExpired);
        }
    }

    if payload.starts_with(".") {
        let b = match String::from_utf8(payload.as_bytes()[1..].to_vec()) {
            Ok(v) => v,
            Err(_) => return Err(SessionError::SessionInvalid),
        };
        let c = signing::b64_decode(&b[..]);
        let mut r = ZlibDecoder::new(&c[..]);
        if let Err(_) = r.read_to_string(&mut result) {
            return Err(SessionError::SessionInvalid);
        }
    } else {
        result = match String::from_utf8(signing::b64_decode(&payload[..])) {
            Ok(v) => v,
            Err(_) => return Err(SessionError::SessionInvalid),
        };
    }

    return Ok(result);
}

pub fn get_user_id(payload: &str) -> Option<usize> {
    let session_json = match json::Json::from_str(payload) {
        Ok(v) => v,
        Err(_) => return None,
    };

    let session_obj = match session_json.as_object() {
        Some(v) => v,
        None => return None,
    };

    match session_obj.get("_auth_user_id") {
        Some(v) => match v.as_u64() {
            Some(v) => Some(v as usize),
            None => None,
        },
        None => None,
    }
}

#[test]
fn test_session() {
    let salt = signing::DEFAULT_SALT;
    let secret = "6utnz%qfm$fs0legp)e@uxjlk-3hyo8sp4dc-y+x@z(!p=l@l9";
    let input = ".eJxVjrsSwiAQRf-F2kQgIWBKZ-y0s2cgbB4aQXk0Ov67ZEyT9u45e-8HmZuyg5NxesDbWUAtOiXvnrA_O2ucRTskVYqjTAG8HFUYM6F74MSIqtGE0w7zGvO-ogfWCE2ErjCvOAPQZCtr1d3Bmuz_O8vO2egnXS5IuV5DeXEG5uPKbh5M2SU5mbOd1LBsBVsMOlOzClF6eCUIMccUE1bgpsD8ikXL6pYy9P0Bm_FM9Q:1Z1WLZ:3jI2GHmVMInuElGvEsK7gVeR5io";
    let output = "{\"django_timezone\":\"Europe/London\",\"_auth_user_hash\":\"bfe71d836b172c07407f329568b18b307375eeb1\",\"_auth_user_backend\":\"django.contrib.auth.backends.ModelBackend\",\"_auth_user_id\":1,\"_language\":\"en-gb\",\"last_request\":\"2015-06-07T08:54:25\"}";

    assert_eq!(validate(salt, secret, input, None).unwrap(), output);

    assert_eq!(validate(salt, secret, input, Some(0)).err(), Some(SessionError::SessionExpired));

    assert_eq!(validate(salt, secret, input, Some(999999)).unwrap(), output);

    let session = validate(salt, secret, input, Some(999999)).unwrap();
    assert_eq!(get_user_id(&session), Some(1));
}
<|endoftext|>


<commit_before>//! FIXME: write short doc here
use std::{str::FromStr, sync::Arc};

use ra_cfg::CfgOptions;
use ra_db::{CrateName, Env, FileSet, SourceRoot, VfsPath};
use test_utils::{extract_offset, extract_range, Fixture, CURSOR_MARKER};

use crate::{
    Analysis, AnalysisChange, AnalysisHost, CrateGraph, Edition, FileId, FilePosition, FileRange,
};

#[derive(Debug)]
enum MockFileData {
    Plain { path: String, content: String },
    Fixture(Fixture),
}

impl MockFileData {
    fn new(path: String, content: String) -> Self {
        // `Self::Plain` causes a false warning: 'variant is never constructed: `Plain` '
        // see https://github.com/rust-lang/rust/issues/69018
        MockFileData::Plain { path, content }
    }

    fn path(&self) -> &str {
        match self {
            MockFileData::Plain { path, .. } => path.as_str(),
            MockFileData::Fixture(f) => f.path.as_str(),
        }
    }

    fn content(&self) -> &str {
        match self {
            MockFileData::Plain { content, .. } => content,
            MockFileData::Fixture(f) => f.text.as_str(),
        }
    }

    fn cfg_options(&self) -> CfgOptions {
        match self {
            MockFileData::Fixture(f) => {
                let mut cfg = CfgOptions::default();
                f.cfg_atoms.iter().for_each(|it| cfg.insert_atom(it.into()));
                f.cfg_key_values.iter().for_each(|(k, v)| cfg.insert_key_value(k.into(), v.into()));
                cfg
            }
            _ => CfgOptions::default(),
        }
    }

    fn edition(&self) -> Edition {
        match self {
            MockFileData::Fixture(f) => {
                f.edition.as_ref().map_or(Edition::Edition2018, |v| Edition::from_str(&v).unwrap())
            }
            _ => Edition::Edition2018,
        }
    }

    fn env(&self) -> Env {
        match self {
            MockFileData::Fixture(f) => Env::from(f.env.iter()),
            _ => Env::default(),
        }
    }
}

impl From<Fixture> for MockFileData {
    fn from(fixture: Fixture) -> Self {
        Self::Fixture(fixture)
    }
}

/// Mock analysis is used in test to bootstrap an AnalysisHost/Analysis
/// from a set of in-memory files.
#[derive(Debug, Default)]
pub struct MockAnalysis {
    files: Vec<MockFileData>,
}

impl MockAnalysis {
    pub fn new() -> MockAnalysis {
        MockAnalysis::default()
    }
    /// Creates `MockAnalysis` using a fixture data in the following format:
    ///
    /// ```not_rust
    /// //- /main.rs
    /// mod foo;
    /// fn main() {}
    ///
    /// //- /foo.rs
    /// struct Baz;
    /// ```
    pub fn with_files(fixture: &str) -> MockAnalysis {
        let mut res = MockAnalysis::new();
        for entry in Fixture::parse(fixture) {
            res.add_file_fixture(entry);
        }
        res
    }

    /// Same as `with_files`, but requires that a single file contains a `<|>` marker,
    /// whose position is also returned.
    pub fn with_files_and_position(fixture: &str) -> (MockAnalysis, FilePosition) {
        let mut position = None;
        let mut res = MockAnalysis::new();
        for mut entry in Fixture::parse(fixture) {
            if entry.text.contains(CURSOR_MARKER) {
                assert!(position.is_none(), "only one marker (<|>) per fixture is allowed");
                let (offset, text) = extract_offset(&entry.text);
                entry.text = text;
                let file_id = res.add_file_fixture(entry);
                position = Some(FilePosition { file_id, offset });
            } else {
                res.add_file_fixture(entry);
            }
        }
        let position = position.expect("expected a marker (<|>)");
        (res, position)
    }

    fn add_file_fixture(&mut self, fixture: Fixture) -> FileId {
        let file_id = self.next_id();
        self.files.push(MockFileData::from(fixture));
        file_id
    }

    fn add_file_with_range(&mut self, path: &str, text: &str) -> FileRange {
        let (range, text) = extract_range(text);
        let file_id = self.next_id();
        self.files.push(MockFileData::new(path.to_string(), text));
        FileRange { file_id, range }
    }
    pub fn id_of(&self, path: &str) -> FileId {
        let (idx, _) = self
            .files
            .iter()
            .enumerate()
            .find(|(_, data)| path == data.path())
            .expect("no file in this mock");
        FileId(idx as u32 + 1)
    }
    pub fn analysis_host(self) -> AnalysisHost {
        let mut host = AnalysisHost::default();
        let mut change = AnalysisChange::new();
        let mut file_set = FileSet::default();
        let mut crate_graph = CrateGraph::default();
        let mut root_crate = None;
        for (i, data) in self.files.into_iter().enumerate() {
            let path = data.path();
            assert!(path.starts_with('/'));
            let cfg_options = data.cfg_options();
            let file_id = FileId(i as u32 + 1);
            let edition = data.edition();
            let env = data.env();
            if path == "/lib.rs" || path == "/main.rs" {
                root_crate = Some(crate_graph.add_crate_root(
                    file_id,
                    edition,
                    None,
                    cfg_options,
                    env,
                    Default::default(),
                ));
            } else if path.ends_with("/lib.rs") {
                let base = &path[..path.len() - "/lib.rs".len()];
                let crate_name = &base[base.rfind('/').unwrap() + '/'.len_utf8()..];
                let other_crate = crate_graph.add_crate_root(
                    file_id,
                    edition,
                    Some(CrateName::new(crate_name).unwrap()),
                    cfg_options,
                    env,
                    Default::default(),
                );
                if let Some(root_crate) = root_crate {
                    crate_graph
                        .add_dep(root_crate, CrateName::new(crate_name).unwrap(), other_crate)
                        .unwrap();
                }
            }
            let path = VfsPath::new_virtual_path(path.to_string());
            file_set.insert(file_id, path);
            change.change_file(file_id, Some(Arc::new(data.content().to_owned())));
        }
        change.set_crate_graph(crate_graph);
        change.set_roots(vec![SourceRoot::new_local(file_set)]);
        host.apply_change(change);
        host
    }
    pub fn analysis(self) -> Analysis {
        self.analysis_host().analysis()
    }

    fn next_id(&self) -> FileId {
        FileId((self.files.len() + 1) as u32)
    }
}

/// Creates analysis from a multi-file fixture, returns positions marked with <|>.
pub fn analysis_and_position(ra_fixture: &str) -> (Analysis, FilePosition) {
    let (mock, position) = MockAnalysis::with_files_and_position(ra_fixture);
    (mock.analysis(), position)
}

/// Creates analysis for a single file.
pub fn single_file(ra_fixture: &str) -> (Analysis, FileId) {
    let mock = MockAnalysis::with_files(ra_fixture);
    let file_id = mock.id_of("/main.rs");
    (mock.analysis(), file_id)
}

/// Creates analysis for a single file, returns range marked with a pair of <|>.
pub fn single_file_with_range(ra_fixture: &str) -> (Analysis, FileRange) {
    let mut mock = MockAnalysis::new();
    let pos = mock.add_file_with_range("/main.rs", ra_fixture);
    (mock.analysis(), pos)
}
<commit_msg>Simplify<commit_after>//! FIXME: write short doc here
use std::{str::FromStr, sync::Arc};

use ra_cfg::CfgOptions;
use ra_db::{CrateName, Env, FileSet, SourceRoot, VfsPath};
use test_utils::{extract_offset, extract_range, Fixture, CURSOR_MARKER};

use crate::{
    Analysis, AnalysisChange, AnalysisHost, CrateGraph, Edition, FileId, FilePosition, FileRange,
};

#[derive(Debug)]
enum MockFileData {
    Plain { path: String, content: String },
    Fixture(Fixture),
}

impl MockFileData {
    fn new(path: String, content: String) -> Self {
        // `Self::Plain` causes a false warning: 'variant is never constructed: `Plain` '
        // see https://github.com/rust-lang/rust/issues/69018
        MockFileData::Plain { path, content }
    }

    fn path(&self) -> &str {
        match self {
            MockFileData::Plain { path, .. } => path.as_str(),
            MockFileData::Fixture(f) => f.path.as_str(),
        }
    }

    fn content(&self) -> &str {
        match self {
            MockFileData::Plain { content, .. } => content,
            MockFileData::Fixture(f) => f.text.as_str(),
        }
    }

    fn cfg_options(&self) -> CfgOptions {
        match self {
            MockFileData::Fixture(f) => {
                let mut cfg = CfgOptions::default();
                f.cfg_atoms.iter().for_each(|it| cfg.insert_atom(it.into()));
                f.cfg_key_values.iter().for_each(|(k, v)| cfg.insert_key_value(k.into(), v.into()));
                cfg
            }
            _ => CfgOptions::default(),
        }
    }

    fn edition(&self) -> Edition {
        match self {
            MockFileData::Fixture(f) => {
                f.edition.as_ref().map_or(Edition::Edition2018, |v| Edition::from_str(&v).unwrap())
            }
            _ => Edition::Edition2018,
        }
    }

    fn env(&self) -> Env {
        match self {
            MockFileData::Fixture(f) => Env::from(f.env.iter()),
            _ => Env::default(),
        }
    }
}

impl From<Fixture> for MockFileData {
    fn from(fixture: Fixture) -> Self {
        Self::Fixture(fixture)
    }
}

/// Mock analysis is used in test to bootstrap an AnalysisHost/Analysis
/// from a set of in-memory files.
#[derive(Debug, Default)]
pub struct MockAnalysis {
    files: Vec<MockFileData>,
}

impl MockAnalysis {
    /// Creates `MockAnalysis` using a fixture data in the following format:
    ///
    /// ```not_rust
    /// //- /main.rs
    /// mod foo;
    /// fn main() {}
    ///
    /// //- /foo.rs
    /// struct Baz;
    /// ```
    pub fn with_files(fixture: &str) -> MockAnalysis {
        let mut res = MockAnalysis::default();
        for entry in Fixture::parse(fixture) {
            res.add_file_fixture(entry);
        }
        res
    }

    /// Same as `with_files`, but requires that a single file contains a `<|>` marker,
    /// whose position is also returned.
    pub fn with_files_and_position(fixture: &str) -> (MockAnalysis, FilePosition) {
        let mut position = None;
        let mut res = MockAnalysis::default();
        for mut entry in Fixture::parse(fixture) {
            if entry.text.contains(CURSOR_MARKER) {
                assert!(position.is_none(), "only one marker (<|>) per fixture is allowed");
                let (offset, text) = extract_offset(&entry.text);
                entry.text = text;
                let file_id = res.add_file_fixture(entry);
                position = Some(FilePosition { file_id, offset });
            } else {
                res.add_file_fixture(entry);
            }
        }
        let position = position.expect("expected a marker (<|>)");
        (res, position)
    }

    fn add_file_fixture(&mut self, fixture: Fixture) -> FileId {
        let file_id = self.next_id();
        self.files.push(MockFileData::from(fixture));
        file_id
    }

    fn add_file_with_range(&mut self, path: &str, text: &str) -> FileRange {
        let (range, text) = extract_range(text);
        let file_id = self.next_id();
        self.files.push(MockFileData::new(path.to_string(), text));
        FileRange { file_id, range }
    }
    pub fn id_of(&self, path: &str) -> FileId {
        let (idx, _) = self
            .files
            .iter()
            .enumerate()
            .find(|(_, data)| path == data.path())
            .expect("no file in this mock");
        FileId(idx as u32 + 1)
    }
    pub fn analysis_host(self) -> AnalysisHost {
        let mut host = AnalysisHost::default();
        let mut change = AnalysisChange::new();
        let mut file_set = FileSet::default();
        let mut crate_graph = CrateGraph::default();
        let mut root_crate = None;
        for (i, data) in self.files.into_iter().enumerate() {
            let path = data.path();
            assert!(path.starts_with('/'));
            let cfg_options = data.cfg_options();
            let file_id = FileId(i as u32 + 1);
            let edition = data.edition();
            let env = data.env();
            if path == "/lib.rs" || path == "/main.rs" {
                root_crate = Some(crate_graph.add_crate_root(
                    file_id,
                    edition,
                    None,
                    cfg_options,
                    env,
                    Default::default(),
                ));
            } else if path.ends_with("/lib.rs") {
                let base = &path[..path.len() - "/lib.rs".len()];
                let crate_name = &base[base.rfind('/').unwrap() + '/'.len_utf8()..];
                let other_crate = crate_graph.add_crate_root(
                    file_id,
                    edition,
                    Some(CrateName::new(crate_name).unwrap()),
                    cfg_options,
                    env,
                    Default::default(),
                );
                if let Some(root_crate) = root_crate {
                    crate_graph
                        .add_dep(root_crate, CrateName::new(crate_name).unwrap(), other_crate)
                        .unwrap();
                }
            }
            let path = VfsPath::new_virtual_path(path.to_string());
            file_set.insert(file_id, path);
            change.change_file(file_id, Some(Arc::new(data.content().to_owned())));
        }
        change.set_crate_graph(crate_graph);
        change.set_roots(vec![SourceRoot::new_local(file_set)]);
        host.apply_change(change);
        host
    }
    pub fn analysis(self) -> Analysis {
        self.analysis_host().analysis()
    }

    fn next_id(&self) -> FileId {
        FileId((self.files.len() + 1) as u32)
    }
}

/// Creates analysis from a multi-file fixture, returns positions marked with <|>.
pub fn analysis_and_position(ra_fixture: &str) -> (Analysis, FilePosition) {
    let (mock, position) = MockAnalysis::with_files_and_position(ra_fixture);
    (mock.analysis(), position)
}

/// Creates analysis for a single file.
pub fn single_file(ra_fixture: &str) -> (Analysis, FileId) {
    let mock = MockAnalysis::with_files(ra_fixture);
    let file_id = mock.id_of("/main.rs");
    (mock.analysis(), file_id)
}

/// Creates analysis for a single file, returns range marked with a pair of <|>.
pub fn single_file_with_range(ra_fixture: &str) -> (Analysis, FileRange) {
    let mut mock = MockAnalysis::default();
    let pos = mock.add_file_with_range("/main.rs", ra_fixture);
    (mock.analysis(), pos)
}
<|endoftext|>


<commit_before>//! Config used by the language server.
//!
//! We currently get this config from `initialize` LSP request, which is not the
//! best way to do it, but was the simplest thing we could implement.
//!
//! Of particular interest is the `feature_flags` hash map: while other fields
//! configure the server itself, feature flags are passed into analysis, and
//! tweak things like automatic insertion of `()` in completions.

use std::{ffi::OsString, path::PathBuf};

use crate::diagnostics::DiagnosticsConfig;
use flycheck::FlycheckConfig;
use lsp_types::ClientCapabilities;
use ra_db::AbsPathBuf;
use ra_ide::{AssistConfig, CompletionConfig, HoverConfig, InlayHintsConfig};
use ra_project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest};
use serde::Deserialize;

#[derive(Debug, Clone)]
pub struct Config {
    pub client_caps: ClientCapsConfig,

    pub publish_diagnostics: bool,
    pub diagnostics: DiagnosticsConfig,
    pub lru_capacity: Option<usize>,
    pub proc_macro_srv: Option<(PathBuf, Vec<OsString>)>,
    pub files: FilesConfig,
    pub notifications: NotificationsConfig,

    pub cargo: CargoConfig,
    pub rustfmt: RustfmtConfig,
    pub flycheck: Option<FlycheckConfig>,

    pub inlay_hints: InlayHintsConfig,
    pub completion: CompletionConfig,
    pub assist: AssistConfig,
    pub call_info_full: bool,
    pub lens: LensConfig,
    pub hover: HoverConfig,

    pub with_sysroot: bool,
    pub linked_projects: Vec<LinkedProject>,
    pub root_path: AbsPathBuf,
}

#[derive(Debug, Clone, Eq, PartialEq)]
pub enum LinkedProject {
    ProjectManifest(ProjectManifest),
    InlineJsonProject(ProjectJson),
}

impl From<ProjectManifest> for LinkedProject {
    fn from(v: ProjectManifest) -> Self {
        LinkedProject::ProjectManifest(v)
    }
}

impl From<ProjectJson> for LinkedProject {
    fn from(v: ProjectJson) -> Self {
        LinkedProject::InlineJsonProject(v)
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct LensConfig {
    pub run: bool,
    pub debug: bool,
    pub impementations: bool,
}

impl Default for LensConfig {
    fn default() -> Self {
        Self { run: true, debug: true, impementations: true }
    }
}

impl LensConfig {
    pub const NO_LENS: LensConfig = Self { run: false, debug: false, impementations: false };

    pub fn any(&self) -> bool {
        self.impementations || self.runnable()
    }

    pub fn none(&self) -> bool {
        !self.any()
    }

    pub fn runnable(&self) -> bool {
        self.run || self.debug
    }
}

#[derive(Debug, Clone)]
pub struct FilesConfig {
    pub watcher: FilesWatcher,
    pub exclude: Vec<String>,
}

#[derive(Debug, Clone)]
pub enum FilesWatcher {
    Client,
    Notify,
}

#[derive(Debug, Clone)]
pub struct NotificationsConfig {
    pub cargo_toml_not_found: bool,
}

#[derive(Debug, Clone)]
pub enum RustfmtConfig {
    Rustfmt {
        extra_args: Vec<String>,
    },
    #[allow(unused)]
    CustomCommand {
        command: String,
        args: Vec<String>,
    },
}

#[derive(Debug, Clone, Default)]
pub struct ClientCapsConfig {
    pub location_link: bool,
    pub line_folding_only: bool,
    pub hierarchical_symbols: bool,
    pub code_action_literals: bool,
    pub work_done_progress: bool,
    pub code_action_group: bool,
    pub resolve_code_action: bool,
    pub hover_actions: bool,
    pub status_notification: bool,
}

impl Config {
    pub fn new(root_path: AbsPathBuf) -> Self {
        Config {
            client_caps: ClientCapsConfig::default(),

            with_sysroot: true,
            publish_diagnostics: true,
            diagnostics: DiagnosticsConfig::default(),
            lru_capacity: None,
            proc_macro_srv: None,
            files: FilesConfig { watcher: FilesWatcher::Notify, exclude: Vec::new() },
            notifications: NotificationsConfig { cargo_toml_not_found: true },

            cargo: CargoConfig::default(),
            rustfmt: RustfmtConfig::Rustfmt { extra_args: Vec::new() },
            flycheck: Some(FlycheckConfig::CargoCommand {
                command: "check".to_string(),
                all_targets: true,
                all_features: false,
                extra_args: Vec::new(),
                features: Vec::new(),
            }),

            inlay_hints: InlayHintsConfig {
                type_hints: true,
                parameter_hints: true,
                chaining_hints: true,
                max_length: None,
            },
            completion: CompletionConfig {
                enable_postfix_completions: true,
                add_call_parenthesis: true,
                add_call_argument_snippets: true,
                ..CompletionConfig::default()
            },
            assist: AssistConfig::default(),
            call_info_full: true,
            lens: LensConfig::default(),
            hover: HoverConfig::default(),
            linked_projects: Vec::new(),
            root_path,
        }
    }

    #[rustfmt::skip]
    pub fn update(&mut self, value: &serde_json::Value) {
        log::info!("Config::update({:#})", value);

        let client_caps = self.client_caps.clone();
        *self = Config::new(self.root_path.clone());
        self.client_caps = client_caps;

        set(value, "/withSysroot", &mut self.with_sysroot);
        set(value, "/diagnostics/enable", &mut self.publish_diagnostics);
        set(value, "/diagnostics/warningsAsInfo", &mut self.diagnostics.warnings_as_info);
        set(value, "/diagnostics/warningsAsHint", &mut self.diagnostics.warnings_as_hint);
        set(value, "/lruCapacity", &mut self.lru_capacity);
        self.files.watcher = match get(value, "/files/watcher") {
            Some("client") => FilesWatcher::Client,
            Some("notify") | _ => FilesWatcher::Notify
        };
        set(value, "/notifications/cargoTomlNotFound", &mut self.notifications.cargo_toml_not_found);

        set(value, "/cargo/noDefaultFeatures", &mut self.cargo.no_default_features);
        set(value, "/cargo/allFeatures", &mut self.cargo.all_features);
        set(value, "/cargo/features", &mut self.cargo.features);
        set(value, "/cargo/loadOutDirsFromCheck", &mut self.cargo.load_out_dirs_from_check);
        set(value, "/cargo/target", &mut self.cargo.target);

        match get(value, "/procMacro/enable") {
            Some(true) => {
                if let Ok(path) = std::env::current_exe() {
                    self.proc_macro_srv = Some((path, vec!["proc-macro".into()]));
                }
            }
            _ => self.proc_macro_srv = None,
        }

        match get::<Vec<String>>(value, "/rustfmt/overrideCommand") {
            Some(mut args) if !args.is_empty() => {
                let command = args.remove(0);
                self.rustfmt = RustfmtConfig::CustomCommand {
                    command,
                    args,
                }
            }
            _ => {
                if let RustfmtConfig::Rustfmt { extra_args } = &mut self.rustfmt {
                    set(value, "/rustfmt/extraArgs", extra_args);
                }
            }
        };

        if let Some(false) = get(value, "/checkOnSave/enable") {
            // check is disabled
            self.flycheck = None;
        } else {
            // check is enabled
            match get::<Vec<String>>(value, "/checkOnSave/overrideCommand") {
                // first see if the user has completely overridden the command
                Some(mut args) if !args.is_empty() => {
                    let command = args.remove(0);
                    self.flycheck = Some(FlycheckConfig::CustomCommand {
                        command,
                        args,
                    });
                }
                // otherwise configure command customizations
                _ => {
                    if let Some(FlycheckConfig::CargoCommand { command, extra_args, all_targets, all_features, features })
                        = &mut self.flycheck
                    {
                        set(value, "/checkOnSave/extraArgs", extra_args);
                        set(value, "/checkOnSave/command", command);
                        set(value, "/checkOnSave/allTargets", all_targets);
                        *all_features = get(value, "/checkOnSave/allFeatures").unwrap_or(self.cargo.all_features);
                        *features = get(value, "/checkOnSave/features").unwrap_or(self.cargo.features.clone());
                    }
                }
            };
        }

        set(value, "/inlayHints/typeHints", &mut self.inlay_hints.type_hints);
        set(value, "/inlayHints/parameterHints", &mut self.inlay_hints.parameter_hints);
        set(value, "/inlayHints/chainingHints", &mut self.inlay_hints.chaining_hints);
        set(value, "/inlayHints/maxLength", &mut self.inlay_hints.max_length);
        set(value, "/completion/postfix/enable", &mut self.completion.enable_postfix_completions);
        set(value, "/completion/addCallParenthesis", &mut self.completion.add_call_parenthesis);
        set(value, "/completion/addCallArgumentSnippets", &mut self.completion.add_call_argument_snippets);
        set(value, "/callInfo/full", &mut self.call_info_full);

        let mut lens_enabled = true;
        set(value, "/lens/enable", &mut lens_enabled);
        if lens_enabled {
            set(value, "/lens/run", &mut self.lens.run);
            set(value, "/lens/debug", &mut self.lens.debug);
            set(value, "/lens/implementations", &mut self.lens.impementations);
        } else {
            self.lens = LensConfig::NO_LENS;
        }

        if let Some(linked_projects) = get::<Vec<ManifestOrProjectJson>>(value, "/linkedProjects") {
            if !linked_projects.is_empty() {
                self.linked_projects.clear();
                for linked_project in linked_projects {
                    let linked_project = match linked_project {
                        ManifestOrProjectJson::Manifest(it) => {
                            let path = self.root_path.join(it);
                            match ProjectManifest::from_manifest_file(path) {
                                Ok(it) => it.into(),
                                Err(_) => continue,
                            }
                        }
                        ManifestOrProjectJson::ProjectJson(it) => ProjectJson::new(&self.root_path, it).into(),
                    };
                    self.linked_projects.push(linked_project);
                }
            }
        }

        let mut use_hover_actions = false;
        set(value, "/hoverActions/enable", &mut use_hover_actions);
        if use_hover_actions {
            set(value, "/hoverActions/implementations", &mut self.hover.implementations);
            set(value, "/hoverActions/run", &mut self.hover.run);
            set(value, "/hoverActions/debug", &mut self.hover.debug);
            set(value, "/hoverActions/gotoTypeDef", &mut self.hover.goto_type_def);
        } else {
            self.hover = HoverConfig::NO_ACTIONS;
        }

        log::info!("Config::update() = {:#?}", self);

        fn get<'a, T: Deserialize<'a>>(value: &'a serde_json::Value, pointer: &str) -> Option<T> {
            value.pointer(pointer).and_then(|it| T::deserialize(it).ok())
        }

        fn set<'a, T: Deserialize<'a>>(value: &'a serde_json::Value, pointer: &str, slot: &mut T) {
            if let Some(new_value) = get(value, pointer) {
                *slot = new_value
            }
        }
    }

    pub fn update_caps(&mut self, caps: &ClientCapabilities) {
        if let Some(doc_caps) = caps.text_document.as_ref() {
            if let Some(value) = doc_caps.definition.as_ref().and_then(|it| it.link_support) {
                self.client_caps.location_link = value;
            }
            if let Some(value) = doc_caps.folding_range.as_ref().and_then(|it| it.line_folding_only)
            {
                self.client_caps.line_folding_only = value
            }
            if let Some(value) = doc_caps
                .document_symbol
                .as_ref()
                .and_then(|it| it.hierarchical_document_symbol_support)
            {
                self.client_caps.hierarchical_symbols = value
            }
            if let Some(value) =
                doc_caps.code_action.as_ref().map(|it| it.code_action_literal_support.is_some())
            {
                self.client_caps.code_action_literals = value;
            }

            self.completion.allow_snippets(false);
            if let Some(completion) = &doc_caps.completion {
                if let Some(completion_item) = &completion.completion_item {
                    if let Some(value) = completion_item.snippet_support {
                        self.completion.allow_snippets(value);
                    }
                }
            }
        }

        if let Some(window_caps) = caps.window.as_ref() {
            if let Some(value) = window_caps.work_done_progress {
                self.client_caps.work_done_progress = value;
            }
        }

        self.assist.allow_snippets(false);
        if let Some(experimental) = &caps.experimental {
            let get_bool =
                |index: &str| experimental.get(index).and_then(|it| it.as_bool()) == Some(true);

            let snippet_text_edit = get_bool("snippetTextEdit");
            self.assist.allow_snippets(snippet_text_edit);

            self.client_caps.code_action_group = get_bool("codeActionGroup");
            self.client_caps.resolve_code_action = get_bool("resolveCodeAction");
            self.client_caps.hover_actions = get_bool("hoverActions");
            self.client_caps.status_notification = get_bool("statusNotification");
        }
    }
}

#[derive(Deserialize)]
#[serde(untagged)]
enum ManifestOrProjectJson {
    Manifest(PathBuf),
    ProjectJson(ProjectJsonData),
}
<commit_msg>Fix workspace reloading<commit_after>//! Config used by the language server.
//!
//! We currently get this config from `initialize` LSP request, which is not the
//! best way to do it, but was the simplest thing we could implement.
//!
//! Of particular interest is the `feature_flags` hash map: while other fields
//! configure the server itself, feature flags are passed into analysis, and
//! tweak things like automatic insertion of `()` in completions.

use std::{ffi::OsString, path::PathBuf};

use flycheck::FlycheckConfig;
use lsp_types::ClientCapabilities;
use ra_db::AbsPathBuf;
use ra_ide::{AssistConfig, CompletionConfig, HoverConfig, InlayHintsConfig};
use ra_project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest};
use serde::Deserialize;

use crate::diagnostics::DiagnosticsConfig;

#[derive(Debug, Clone)]
pub struct Config {
    pub client_caps: ClientCapsConfig,

    pub publish_diagnostics: bool,
    pub diagnostics: DiagnosticsConfig,
    pub lru_capacity: Option<usize>,
    pub proc_macro_srv: Option<(PathBuf, Vec<OsString>)>,
    pub files: FilesConfig,
    pub notifications: NotificationsConfig,

    pub cargo: CargoConfig,
    pub rustfmt: RustfmtConfig,
    pub flycheck: Option<FlycheckConfig>,

    pub inlay_hints: InlayHintsConfig,
    pub completion: CompletionConfig,
    pub assist: AssistConfig,
    pub call_info_full: bool,
    pub lens: LensConfig,
    pub hover: HoverConfig,

    pub with_sysroot: bool,
    pub linked_projects: Vec<LinkedProject>,
    pub root_path: AbsPathBuf,
}

#[derive(Debug, Clone, Eq, PartialEq)]
pub enum LinkedProject {
    ProjectManifest(ProjectManifest),
    InlineJsonProject(ProjectJson),
}

impl From<ProjectManifest> for LinkedProject {
    fn from(v: ProjectManifest) -> Self {
        LinkedProject::ProjectManifest(v)
    }
}

impl From<ProjectJson> for LinkedProject {
    fn from(v: ProjectJson) -> Self {
        LinkedProject::InlineJsonProject(v)
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct LensConfig {
    pub run: bool,
    pub debug: bool,
    pub impementations: bool,
}

impl Default for LensConfig {
    fn default() -> Self {
        Self { run: true, debug: true, impementations: true }
    }
}

impl LensConfig {
    pub const NO_LENS: LensConfig = Self { run: false, debug: false, impementations: false };

    pub fn any(&self) -> bool {
        self.impementations || self.runnable()
    }

    pub fn none(&self) -> bool {
        !self.any()
    }

    pub fn runnable(&self) -> bool {
        self.run || self.debug
    }
}

#[derive(Debug, Clone)]
pub struct FilesConfig {
    pub watcher: FilesWatcher,
    pub exclude: Vec<String>,
}

#[derive(Debug, Clone)]
pub enum FilesWatcher {
    Client,
    Notify,
}

#[derive(Debug, Clone)]
pub struct NotificationsConfig {
    pub cargo_toml_not_found: bool,
}

#[derive(Debug, Clone)]
pub enum RustfmtConfig {
    Rustfmt {
        extra_args: Vec<String>,
    },
    #[allow(unused)]
    CustomCommand {
        command: String,
        args: Vec<String>,
    },
}

#[derive(Debug, Clone, Default)]
pub struct ClientCapsConfig {
    pub location_link: bool,
    pub line_folding_only: bool,
    pub hierarchical_symbols: bool,
    pub code_action_literals: bool,
    pub work_done_progress: bool,
    pub code_action_group: bool,
    pub resolve_code_action: bool,
    pub hover_actions: bool,
    pub status_notification: bool,
}

impl Config {
    pub fn new(root_path: AbsPathBuf) -> Self {
        Config {
            client_caps: ClientCapsConfig::default(),

            with_sysroot: true,
            publish_diagnostics: true,
            diagnostics: DiagnosticsConfig::default(),
            lru_capacity: None,
            proc_macro_srv: None,
            files: FilesConfig { watcher: FilesWatcher::Notify, exclude: Vec::new() },
            notifications: NotificationsConfig { cargo_toml_not_found: true },

            cargo: CargoConfig::default(),
            rustfmt: RustfmtConfig::Rustfmt { extra_args: Vec::new() },
            flycheck: Some(FlycheckConfig::CargoCommand {
                command: "check".to_string(),
                all_targets: true,
                all_features: false,
                extra_args: Vec::new(),
                features: Vec::new(),
            }),

            inlay_hints: InlayHintsConfig {
                type_hints: true,
                parameter_hints: true,
                chaining_hints: true,
                max_length: None,
            },
            completion: CompletionConfig {
                enable_postfix_completions: true,
                add_call_parenthesis: true,
                add_call_argument_snippets: true,
                ..CompletionConfig::default()
            },
            assist: AssistConfig::default(),
            call_info_full: true,
            lens: LensConfig::default(),
            hover: HoverConfig::default(),
            linked_projects: Vec::new(),
            root_path,
        }
    }

    #[rustfmt::skip]
    pub fn update(&mut self, value: &serde_json::Value) {
        log::info!("Config::update({:#})", value);

        let client_caps = self.client_caps.clone();
        let linked_projects = self.linked_projects.clone();
        *self = Config::new(self.root_path.clone());
        self.client_caps = client_caps;
        self.linked_projects = linked_projects;

        set(value, "/withSysroot", &mut self.with_sysroot);
        set(value, "/diagnostics/enable", &mut self.publish_diagnostics);
        set(value, "/diagnostics/warningsAsInfo", &mut self.diagnostics.warnings_as_info);
        set(value, "/diagnostics/warningsAsHint", &mut self.diagnostics.warnings_as_hint);
        set(value, "/lruCapacity", &mut self.lru_capacity);
        self.files.watcher = match get(value, "/files/watcher") {
            Some("client") => FilesWatcher::Client,
            Some("notify") | _ => FilesWatcher::Notify
        };
        set(value, "/notifications/cargoTomlNotFound", &mut self.notifications.cargo_toml_not_found);

        set(value, "/cargo/noDefaultFeatures", &mut self.cargo.no_default_features);
        set(value, "/cargo/allFeatures", &mut self.cargo.all_features);
        set(value, "/cargo/features", &mut self.cargo.features);
        set(value, "/cargo/loadOutDirsFromCheck", &mut self.cargo.load_out_dirs_from_check);
        set(value, "/cargo/target", &mut self.cargo.target);

        match get(value, "/procMacro/enable") {
            Some(true) => {
                if let Ok(path) = std::env::current_exe() {
                    self.proc_macro_srv = Some((path, vec!["proc-macro".into()]));
                }
            }
            _ => self.proc_macro_srv = None,
        }

        match get::<Vec<String>>(value, "/rustfmt/overrideCommand") {
            Some(mut args) if !args.is_empty() => {
                let command = args.remove(0);
                self.rustfmt = RustfmtConfig::CustomCommand {
                    command,
                    args,
                }
            }
            _ => {
                if let RustfmtConfig::Rustfmt { extra_args } = &mut self.rustfmt {
                    set(value, "/rustfmt/extraArgs", extra_args);
                }
            }
        };

        if let Some(false) = get(value, "/checkOnSave/enable") {
            // check is disabled
            self.flycheck = None;
        } else {
            // check is enabled
            match get::<Vec<String>>(value, "/checkOnSave/overrideCommand") {
                // first see if the user has completely overridden the command
                Some(mut args) if !args.is_empty() => {
                    let command = args.remove(0);
                    self.flycheck = Some(FlycheckConfig::CustomCommand {
                        command,
                        args,
                    });
                }
                // otherwise configure command customizations
                _ => {
                    if let Some(FlycheckConfig::CargoCommand { command, extra_args, all_targets, all_features, features })
                        = &mut self.flycheck
                    {
                        set(value, "/checkOnSave/extraArgs", extra_args);
                        set(value, "/checkOnSave/command", command);
                        set(value, "/checkOnSave/allTargets", all_targets);
                        *all_features = get(value, "/checkOnSave/allFeatures").unwrap_or(self.cargo.all_features);
                        *features = get(value, "/checkOnSave/features").unwrap_or(self.cargo.features.clone());
                    }
                }
            };
        }

        set(value, "/inlayHints/typeHints", &mut self.inlay_hints.type_hints);
        set(value, "/inlayHints/parameterHints", &mut self.inlay_hints.parameter_hints);
        set(value, "/inlayHints/chainingHints", &mut self.inlay_hints.chaining_hints);
        set(value, "/inlayHints/maxLength", &mut self.inlay_hints.max_length);
        set(value, "/completion/postfix/enable", &mut self.completion.enable_postfix_completions);
        set(value, "/completion/addCallParenthesis", &mut self.completion.add_call_parenthesis);
        set(value, "/completion/addCallArgumentSnippets", &mut self.completion.add_call_argument_snippets);
        set(value, "/callInfo/full", &mut self.call_info_full);

        let mut lens_enabled = true;
        set(value, "/lens/enable", &mut lens_enabled);
        if lens_enabled {
            set(value, "/lens/run", &mut self.lens.run);
            set(value, "/lens/debug", &mut self.lens.debug);
            set(value, "/lens/implementations", &mut self.lens.impementations);
        } else {
            self.lens = LensConfig::NO_LENS;
        }

        if let Some(linked_projects) = get::<Vec<ManifestOrProjectJson>>(value, "/linkedProjects") {
            if !linked_projects.is_empty() {
                self.linked_projects.clear();
                for linked_project in linked_projects {
                    let linked_project = match linked_project {
                        ManifestOrProjectJson::Manifest(it) => {
                            let path = self.root_path.join(it);
                            match ProjectManifest::from_manifest_file(path) {
                                Ok(it) => it.into(),
                                Err(_) => continue,
                            }
                        }
                        ManifestOrProjectJson::ProjectJson(it) => ProjectJson::new(&self.root_path, it).into(),
                    };
                    self.linked_projects.push(linked_project);
                }
            }
        }

        let mut use_hover_actions = false;
        set(value, "/hoverActions/enable", &mut use_hover_actions);
        if use_hover_actions {
            set(value, "/hoverActions/implementations", &mut self.hover.implementations);
            set(value, "/hoverActions/run", &mut self.hover.run);
            set(value, "/hoverActions/debug", &mut self.hover.debug);
            set(value, "/hoverActions/gotoTypeDef", &mut self.hover.goto_type_def);
        } else {
            self.hover = HoverConfig::NO_ACTIONS;
        }

        log::info!("Config::update() = {:#?}", self);

        fn get<'a, T: Deserialize<'a>>(value: &'a serde_json::Value, pointer: &str) -> Option<T> {
            value.pointer(pointer).and_then(|it| T::deserialize(it).ok())
        }

        fn set<'a, T: Deserialize<'a>>(value: &'a serde_json::Value, pointer: &str, slot: &mut T) {
            if let Some(new_value) = get(value, pointer) {
                *slot = new_value
            }
        }
    }

    pub fn update_caps(&mut self, caps: &ClientCapabilities) {
        if let Some(doc_caps) = caps.text_document.as_ref() {
            if let Some(value) = doc_caps.definition.as_ref().and_then(|it| it.link_support) {
                self.client_caps.location_link = value;
            }
            if let Some(value) = doc_caps.folding_range.as_ref().and_then(|it| it.line_folding_only)
            {
                self.client_caps.line_folding_only = value
            }
            if let Some(value) = doc_caps
                .document_symbol
                .as_ref()
                .and_then(|it| it.hierarchical_document_symbol_support)
            {
                self.client_caps.hierarchical_symbols = value
            }
            if let Some(value) =
                doc_caps.code_action.as_ref().map(|it| it.code_action_literal_support.is_some())
            {
                self.client_caps.code_action_literals = value;
            }

            self.completion.allow_snippets(false);
            if let Some(completion) = &doc_caps.completion {
                if let Some(completion_item) = &completion.completion_item {
                    if let Some(value) = completion_item.snippet_support {
                        self.completion.allow_snippets(value);
                    }
                }
            }
        }

        if let Some(window_caps) = caps.window.as_ref() {
            if let Some(value) = window_caps.work_done_progress {
                self.client_caps.work_done_progress = value;
            }
        }

        self.assist.allow_snippets(false);
        if let Some(experimental) = &caps.experimental {
            let get_bool =
                |index: &str| experimental.get(index).and_then(|it| it.as_bool()) == Some(true);

            let snippet_text_edit = get_bool("snippetTextEdit");
            self.assist.allow_snippets(snippet_text_edit);

            self.client_caps.code_action_group = get_bool("codeActionGroup");
            self.client_caps.resolve_code_action = get_bool("resolveCodeAction");
            self.client_caps.hover_actions = get_bool("hoverActions");
            self.client_caps.status_notification = get_bool("statusNotification");
        }
    }
}

#[derive(Deserialize)]
#[serde(untagged)]
enum ManifestOrProjectJson {
    Manifest(PathBuf),
    ProjectJson(ProjectJsonData),
}
<|endoftext|>