<commit_before>// Copyright (c) 2017-2018 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.

//! Raspberry Pi system-related tools.
//!
//! Use [`DeviceInfo`] to identify what Raspberry Pi model and SoC the software is
//! running on.
//!
//! [`DeviceInfo`]: struct.DeviceInfo.html

use std::fmt;
use std::fs;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::result;

use quick_error::quick_error;

const PERIPHERAL_BASE_RPI: u32 = 0x2000_0000;
const PERIPHERAL_BASE_RPI2: u32 = 0x3f00_0000;
const GPIO_OFFSET: u32 = 0x20_0000;

quick_error! {
/// Errors that can occur when trying to identify the Raspberry Pi hardware.
    #[derive(Debug)]
    pub enum Error {
/// Unknown model.
///
/// `DeviceInfo` was unable to identify the Raspberry Pi model based on the
/// contents of `/proc/cpuinfo`, `/sys/firmware/devicetree/base/compatible`
/// and `/sys/firmware/devicetree/base/model`.
        UnknownModel { description("unknown Raspberry Pi model") }
    }
}

/// Result type returned from methods that can have `system::Error`s.
pub type Result<T> = result::Result<T, Error>;

/// Identifiable Raspberry Pi models.
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Model {
    RaspberryPiA,
    RaspberryPiAPlus,
    RaspberryPiBRev1,
    RaspberryPiBRev2,
    RaspberryPiBPlus,
    RaspberryPi2B,
    RaspberryPi3B,
    RaspberryPi3BPlus,
    RaspberryPi3APlus,
    RaspberryPiComputeModule,
    RaspberryPiComputeModule3,
    RaspberryPiZero,
    RaspberryPiZeroW,
}

impl fmt::Display for Model {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            Model::RaspberryPiA => write!(f, "Raspberry Pi A"),
            Model::RaspberryPiAPlus => write!(f, "Raspberry Pi A+"),
            Model::RaspberryPiBRev1 => write!(f, "Raspberry Pi B Rev 1"),
            Model::RaspberryPiBRev2 => write!(f, "Raspberry Pi B Rev 2"),
            Model::RaspberryPiBPlus => write!(f, "Raspberry Pi B+"),
            Model::RaspberryPi2B => write!(f, "Raspberry Pi 2 B"),
            Model::RaspberryPi3B => write!(f, "Raspberry Pi 3 B"),
            Model::RaspberryPi3BPlus => write!(f, "Raspberry Pi 3 B+"),
            Model::RaspberryPi3APlus => write!(f, "Raspberry Pi 3 A+"),
            Model::RaspberryPiComputeModule => write!(f, "Raspberry Pi Compute Module"),
            Model::RaspberryPiComputeModule3 => write!(f, "Raspberry Pi Compute Module 3"),
            Model::RaspberryPiZero => write!(f, "Raspberry Pi Zero"),
            Model::RaspberryPiZeroW => write!(f, "Raspberry Pi Zero W"),
        }
    }
}

/// Identifiable Raspberry Pi SoCs.
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum SoC {
    Bcm2835,
    Bcm2836,
    Bcm2837A1,
    Bcm2837B0,
}

impl fmt::Display for SoC {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            SoC::Bcm2835 => write!(f, "BCM2835"),
            SoC::Bcm2836 => write!(f, "BCM2836"),
            SoC::Bcm2837A1 => write!(f, "BCM2837A1"),
            SoC::Bcm2837B0 => write!(f, "BCM2837B0"),
        }
    }
}

// Identify Pi model based on /proc/cpuinfo
fn parse_proc_cpuinfo() -> Result<Model> {
    let proc_cpuinfo = BufReader::new(match File::open("/proc/cpuinfo") {
        Ok(file) => file,
        Err(_) => return Err(Error::UnknownModel),
    });

    let mut hardware: String = String::new();
    let mut revision: String = String::new();
    for line_result in proc_cpuinfo.lines() {
        if let Ok(line) = line_result {
            if line.starts_with("Hardware\t: ") {
                hardware = String::from(&line[11..]);
            } else if line.starts_with("Revision\t: ") {
                revision = String::from(&line[11..]).to_lowercase();
            }
        }
    }

    // Return an error if we don't recognize the SoC. This check is
    // done to prevent accidentally identifying a non-Pi SBC as a Pi
    // solely based on the revision field.
    match &hardware[..] {
        "BCM2708" | "BCM2835" | "BCM2709" | "BCM2836" | "BCM2710" | "BCM2837" | "BCM2837A1"
        | "BCM2837B0" => {}
        _ => return Err(Error::UnknownModel),
    }

    let model = if (revision.len() == 4) || (revision.len() == 8) {
        // Older revisions are 4 characters long, or 8 if they've been over-volted
        match &revision[revision.len() - 4..] {
            "0007" | "0008" | "0009" | "0015" => Model::RaspberryPiA,
            "Beta" | "0002" | "0003" => Model::RaspberryPiBRev1,
            "0004" | "0005" | "0006" | "000d" | "000e" | "000f" => Model::RaspberryPiBRev2,
            "0012" => Model::RaspberryPiAPlus,
            "0010" | "0013" => Model::RaspberryPiBPlus,
            "0011" | "0014" => Model::RaspberryPiComputeModule,
            _ => return Err(Error::UnknownModel),
        }
    } else if revision.len() >= 6 {
        // Newer revisions consist of at least 6 characters
        match &revision[..] {
            "900021" => Model::RaspberryPiAPlus,
            "900032" => Model::RaspberryPiBPlus,
            "a01040" | "a01041" | "a21041" | "a22042" => Model::RaspberryPi2B,
            "a02082" | "a22082" | "a32082" | "a52082" => Model::RaspberryPi3B,
            "900092" | "920092" | "900093" | "920093" => Model::RaspberryPiZero,
            "a020a0" => Model::RaspberryPiComputeModule3,
            "9000c1" => Model::RaspberryPiZeroW,
            "a020d3" => Model::RaspberryPi3BPlus,
            "9020e0" => Model::RaspberryPi3APlus,
            _ => return Err(Error::UnknownModel),
        }
    } else {
        return Err(Error::UnknownModel);
    };

    Ok(model)
}

// Identify Pi model based on /sys/firmware/devicetree/base/compatible
fn parse_base_compatible() -> Result<Model> {
    let base_compatible = match fs::read_to_string("/sys/firmware/devicetree/base/compatible") {
        Ok(buffer) => buffer,
        Err(_) => return Err(Error::UnknownModel),
    };

    // Based on /arch/arm/boot/dts/ and /Documentation/devicetree/bindings/arm/bcm/
    for comp_id in base_compatible.split('\0') {
        let model = match comp_id {
            "raspberrypi,model-b-i2c0" => Model::RaspberryPiBRev1,
            "raspberrypi,model-b" => Model::RaspberryPiBRev1,
            "raspberrypi,model-a" => Model::RaspberryPiA,
            "raspberrypi,model-b-rev2" => Model::RaspberryPiBRev2,
            "raspberrypi,model-a-plus" => Model::RaspberryPiAPlus,
            "raspberrypi,model-b-plus" => Model::RaspberryPiBPlus,
            "raspberrypi,2-model-b" => Model::RaspberryPi2B,
            "raspberrypi,compute-module" => Model::RaspberryPiComputeModule,
            "raspberrypi,3-model-b" => Model::RaspberryPi3B,
            "raspberrypi,model-zero" => Model::RaspberryPiZero,
            "raspberrypi,3-compute-module" => Model::RaspberryPiComputeModule3,
            "raspberrypi,model-zero-w" => Model::RaspberryPiZeroW,
            "raspberrypi,3-model-b-plus" => Model::RaspberryPi3BPlus,
            "raspberrypi,3-model-a-plus" => Model::RaspberryPi3APlus,
            _ => continue,
        };

        return Ok(model);
    }

    Err(Error::UnknownModel)
}

// Identify Pi model based on /sys/firmware/devicetree/base/model
fn parse_base_model() -> Result<Model> {
    let mut base_model = match fs::read_to_string("/sys/firmware/devicetree/base/model") {
        Ok(mut buffer) => {
            if let Some(idx) = buffer.find('\0') {
                buffer.truncate(idx);
                buffer
            } else {
                buffer
            }
        }
        Err(_) => return Err(Error::UnknownModel),
    };

    // Check if this is a Pi B rev 2 before we remove the revision part, assuming the
    // PCB Revision numbers on https://elinux.org/RPi_HardwareHistory are correct, and
    // the installed distro appends the revision to the model name.
    match &base_model[..] {
        "Raspberry Pi Model B Rev 2.0" => return Ok(Model::RaspberryPiBRev2),
        "Raspberry Pi Model B rev2 Rev 2.0" => return Ok(Model::RaspberryPiBRev2),
        _ => (),
    }

    if let Some(idx) = base_model.find(" Rev ") {
        base_model.truncate(idx);
    }

    // Based on /arch/arm/boot/dts/ and /Documentation/devicetree/bindings/arm/bcm/
    let model = match &base_model[..] {
        "Raspberry Pi Model B (no P5)" => Model::RaspberryPiBRev1,
        "Raspberry Pi Model B" => Model::RaspberryPiBRev1,
        "Raspberry Pi Model A" => Model::RaspberryPiA,
        "Raspberry Pi Model B rev2" => Model::RaspberryPiBRev2,
        "Raspberry Pi Model A+" => Model::RaspberryPiAPlus,
        "Raspberry Pi Model A Plus" => Model::RaspberryPiAPlus,
        "Raspberry Pi Model B+" => Model::RaspberryPiBPlus,
        "Raspberry Pi Model B Plus" => Model::RaspberryPiBPlus,
        "Raspberry Pi 2 Model B" => Model::RaspberryPi2B,
        "Raspberry Pi Compute Module" => Model::RaspberryPiComputeModule,
        "Raspberry Pi 3 Model B" => Model::RaspberryPi3B,
        "Raspberry Pi Zero" => Model::RaspberryPiZero,
        "Raspberry Pi Compute Module 3" => Model::RaspberryPiComputeModule3,
        "Raspberry Pi Zero W" => Model::RaspberryPiZeroW,
        "Raspberry Pi 3 Model B+" => Model::RaspberryPi3BPlus,
        "Raspberry Pi 3 Model B Plus" => Model::RaspberryPi3BPlus,
        "Raspberry Pi 3 Model A Plus" => Model::RaspberryPi3APlus,
        _ => return Err(Error::UnknownModel),
    };

    Ok(model)
}

/// Retrieves Raspberry Pi device information.
#[derive(Debug, PartialEq, Copy, Clone)]
pub struct DeviceInfo {
    model: Model,
    soc: SoC,
    peripheral_base: u32,
    gpio_offset: u32,
}

impl DeviceInfo {
    /// Constructs a new `DeviceInfo`.
    ///
    /// `new` automatically identifies the Raspberry Pi model and SoC based on
    /// the contents of `/proc/cpuinfo`, `/sys/firmware/devicetree/base/compatible`
    /// and `/sys/firmware/devicetree/base/model`.
    pub fn new() -> Result<DeviceInfo> {
        // Parse order from most-detailed to least-detailed info
        let model = parse_proc_cpuinfo()
            .or_else(|_| parse_base_compatible().or_else(|_| parse_base_model()))?;

        // Set SoC and memory offsets based on model
        match model {
            Model::RaspberryPiA
            | Model::RaspberryPiAPlus
            | Model::RaspberryPiBRev1
            | Model::RaspberryPiBRev2
            | Model::RaspberryPiBPlus
            | Model::RaspberryPiComputeModule
            | Model::RaspberryPiZero
            | Model::RaspberryPiZeroW => Ok(DeviceInfo {
                model,
                soc: SoC::Bcm2835,
                peripheral_base: PERIPHERAL_BASE_RPI,
                gpio_offset: GPIO_OFFSET,
            }),
            Model::RaspberryPi2B => Ok(DeviceInfo {
                model,
                soc: SoC::Bcm2836,
                peripheral_base: PERIPHERAL_BASE_RPI2,
                gpio_offset: GPIO_OFFSET,
            }),
            Model::RaspberryPi3B | Model::RaspberryPiComputeModule3 => Ok(DeviceInfo {
                model,
                soc: SoC::Bcm2837A1,
                peripheral_base: PERIPHERAL_BASE_RPI2,
                gpio_offset: GPIO_OFFSET,
            }),
            Model::RaspberryPi3BPlus | Model::RaspberryPi3APlus => Ok(DeviceInfo {
                model,
                soc: SoC::Bcm2837B0,
                peripheral_base: PERIPHERAL_BASE_RPI2,
                gpio_offset: GPIO_OFFSET,
            }),
        }
    }

    /// Returns the Raspberry Pi's model.
    pub fn model(&self) -> Model {
        self.model
    }

    /// Returns the Raspberry Pi's SoC.
    pub fn soc(&self) -> SoC {
        self.soc
    }

    /// Returns the base memory address for the BCM283x peripherals.
    pub fn peripheral_base(&self) -> u32 {
        self.peripheral_base
    }

    /// Returns the offset from the base memory address for the GPIO section.
    pub fn gpio_offset(&self) -> u32 {
        self.gpio_offset
    }
}
<commit_msg>Fix casing.<commit_after>// Copyright (c) 2017-2018 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.

//! Raspberry Pi system-related tools.
//!
//! Use [`DeviceInfo`] to identify what Raspberry Pi model and SoC the software is
//! running on.
//!
//! [`DeviceInfo`]: struct.DeviceInfo.html

use std::fmt;
use std::fs;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::result;

use quick_error::quick_error;

const PERIPHERAL_BASE_RPI: u32 = 0x2000_0000;
const PERIPHERAL_BASE_RPI2: u32 = 0x3f00_0000;
const GPIO_OFFSET: u32 = 0x20_0000;

quick_error! {
/// Errors that can occur when trying to identify the Raspberry Pi hardware.
    #[derive(Debug)]
    pub enum Error {
/// Unknown model.
///
/// `DeviceInfo` was unable to identify the Raspberry Pi model based on the
/// contents of `/proc/cpuinfo`, `/sys/firmware/devicetree/base/compatible`
/// and `/sys/firmware/devicetree/base/model`.
        UnknownModel { description("unknown Raspberry Pi model") }
    }
}

/// Result type returned from methods that can have `system::Error`s.
pub type Result<T> = result::Result<T, Error>;

/// Identifiable Raspberry Pi models.
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Model {
    RaspberryPiA,
    RaspberryPiAPlus,
    RaspberryPiBRev1,
    RaspberryPiBRev2,
    RaspberryPiBPlus,
    RaspberryPi2B,
    RaspberryPi3B,
    RaspberryPi3BPlus,
    RaspberryPi3APlus,
    RaspberryPiComputeModule,
    RaspberryPiComputeModule3,
    RaspberryPiZero,
    RaspberryPiZeroW,
}

impl fmt::Display for Model {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            Model::RaspberryPiA => write!(f, "Raspberry Pi A"),
            Model::RaspberryPiAPlus => write!(f, "Raspberry Pi A+"),
            Model::RaspberryPiBRev1 => write!(f, "Raspberry Pi B Rev 1"),
            Model::RaspberryPiBRev2 => write!(f, "Raspberry Pi B Rev 2"),
            Model::RaspberryPiBPlus => write!(f, "Raspberry Pi B+"),
            Model::RaspberryPi2B => write!(f, "Raspberry Pi 2 B"),
            Model::RaspberryPi3B => write!(f, "Raspberry Pi 3 B"),
            Model::RaspberryPi3BPlus => write!(f, "Raspberry Pi 3 B+"),
            Model::RaspberryPi3APlus => write!(f, "Raspberry Pi 3 A+"),
            Model::RaspberryPiComputeModule => write!(f, "Raspberry Pi Compute Module"),
            Model::RaspberryPiComputeModule3 => write!(f, "Raspberry Pi Compute Module 3"),
            Model::RaspberryPiZero => write!(f, "Raspberry Pi Zero"),
            Model::RaspberryPiZeroW => write!(f, "Raspberry Pi Zero W"),
        }
    }
}

/// Identifiable Raspberry Pi SoCs.
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum SoC {
    Bcm2835,
    Bcm2836,
    Bcm2837A1,
    Bcm2837B0,
}

impl fmt::Display for SoC {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            SoC::Bcm2835 => write!(f, "BCM2835"),
            SoC::Bcm2836 => write!(f, "BCM2836"),
            SoC::Bcm2837A1 => write!(f, "BCM2837A1"),
            SoC::Bcm2837B0 => write!(f, "BCM2837B0"),
        }
    }
}

// Identify Pi model based on /proc/cpuinfo
fn parse_proc_cpuinfo() -> Result<Model> {
    let proc_cpuinfo = BufReader::new(match File::open("/proc/cpuinfo") {
        Ok(file) => file,
        Err(_) => return Err(Error::UnknownModel),
    });

    let mut hardware: String = String::new();
    let mut revision: String = String::new();
    for line_result in proc_cpuinfo.lines() {
        if let Ok(line) = line_result {
            if line.starts_with("Hardware\t: ") {
                hardware = String::from(&line[11..]);
            } else if line.starts_with("Revision\t: ") {
                revision = String::from(&line[11..]).to_lowercase();
            }
        }
    }

    // Return an error if we don't recognize the SoC. This check is
    // done to prevent accidentally identifying a non-Pi SBC as a Pi
    // solely based on the revision field.
    match &hardware[..] {
        "BCM2708" | "BCM2835" | "BCM2709" | "BCM2836" | "BCM2710" | "BCM2837" | "BCM2837A1"
        | "BCM2837B0" => {}
        _ => return Err(Error::UnknownModel),
    }

    let model = if (revision.len() == 4) || (revision.len() == 8) {
        // Older revisions are 4 characters long, or 8 if they've been over-volted
        match &revision[revision.len() - 4..] {
            "0007" | "0008" | "0009" | "0015" => Model::RaspberryPiA,
            "beta" | "0002" | "0003" => Model::RaspberryPiBRev1,
            "0004" | "0005" | "0006" | "000d" | "000e" | "000f" => Model::RaspberryPiBRev2,
            "0012" => Model::RaspberryPiAPlus,
            "0010" | "0013" => Model::RaspberryPiBPlus,
            "0011" | "0014" => Model::RaspberryPiComputeModule,
            _ => return Err(Error::UnknownModel),
        }
    } else if revision.len() >= 6 {
        // Newer revisions consist of at least 6 characters
        match &revision[..] {
            "900021" => Model::RaspberryPiAPlus,
            "900032" => Model::RaspberryPiBPlus,
            "a01040" | "a01041" | "a21041" | "a22042" => Model::RaspberryPi2B,
            "a02082" | "a22082" | "a32082" | "a52082" => Model::RaspberryPi3B,
            "900092" | "920092" | "900093" | "920093" => Model::RaspberryPiZero,
            "a020a0" => Model::RaspberryPiComputeModule3,
            "9000c1" => Model::RaspberryPiZeroW,
            "a020d3" => Model::RaspberryPi3BPlus,
            "9020e0" => Model::RaspberryPi3APlus,
            _ => return Err(Error::UnknownModel),
        }
    } else {
        return Err(Error::UnknownModel);
    };

    Ok(model)
}

// Identify Pi model based on /sys/firmware/devicetree/base/compatible
fn parse_base_compatible() -> Result<Model> {
    let base_compatible = match fs::read_to_string("/sys/firmware/devicetree/base/compatible") {
        Ok(buffer) => buffer,
        Err(_) => return Err(Error::UnknownModel),
    };

    // Based on /arch/arm/boot/dts/ and /Documentation/devicetree/bindings/arm/bcm/
    for comp_id in base_compatible.split('\0') {
        let model = match comp_id {
            "raspberrypi,model-b-i2c0" => Model::RaspberryPiBRev1,
            "raspberrypi,model-b" => Model::RaspberryPiBRev1,
            "raspberrypi,model-a" => Model::RaspberryPiA,
            "raspberrypi,model-b-rev2" => Model::RaspberryPiBRev2,
            "raspberrypi,model-a-plus" => Model::RaspberryPiAPlus,
            "raspberrypi,model-b-plus" => Model::RaspberryPiBPlus,
            "raspberrypi,2-model-b" => Model::RaspberryPi2B,
            "raspberrypi,compute-module" => Model::RaspberryPiComputeModule,
            "raspberrypi,3-model-b" => Model::RaspberryPi3B,
            "raspberrypi,model-zero" => Model::RaspberryPiZero,
            "raspberrypi,3-compute-module" => Model::RaspberryPiComputeModule3,
            "raspberrypi,model-zero-w" => Model::RaspberryPiZeroW,
            "raspberrypi,3-model-b-plus" => Model::RaspberryPi3BPlus,
            "raspberrypi,3-model-a-plus" => Model::RaspberryPi3APlus,
            _ => continue,
        };

        return Ok(model);
    }

    Err(Error::UnknownModel)
}

// Identify Pi model based on /sys/firmware/devicetree/base/model
fn parse_base_model() -> Result<Model> {
    let mut base_model = match fs::read_to_string("/sys/firmware/devicetree/base/model") {
        Ok(mut buffer) => {
            if let Some(idx) = buffer.find('\0') {
                buffer.truncate(idx);
                buffer
            } else {
                buffer
            }
        }
        Err(_) => return Err(Error::UnknownModel),
    };

    // Check if this is a Pi B rev 2 before we remove the revision part, assuming the
    // PCB Revision numbers on https://elinux.org/RPi_HardwareHistory are correct, and
    // the installed distro appends the revision to the model name.
    match &base_model[..] {
        "Raspberry Pi Model B Rev 2.0" => return Ok(Model::RaspberryPiBRev2),
        "Raspberry Pi Model B rev2 Rev 2.0" => return Ok(Model::RaspberryPiBRev2),
        _ => (),
    }

    if let Some(idx) = base_model.find(" Rev ") {
        base_model.truncate(idx);
    }

    // Based on /arch/arm/boot/dts/ and /Documentation/devicetree/bindings/arm/bcm/
    let model = match &base_model[..] {
        "Raspberry Pi Model B (no P5)" => Model::RaspberryPiBRev1,
        "Raspberry Pi Model B" => Model::RaspberryPiBRev1,
        "Raspberry Pi Model A" => Model::RaspberryPiA,
        "Raspberry Pi Model B rev2" => Model::RaspberryPiBRev2,
        "Raspberry Pi Model A+" => Model::RaspberryPiAPlus,
        "Raspberry Pi Model A Plus" => Model::RaspberryPiAPlus,
        "Raspberry Pi Model B+" => Model::RaspberryPiBPlus,
        "Raspberry Pi Model B Plus" => Model::RaspberryPiBPlus,
        "Raspberry Pi 2 Model B" => Model::RaspberryPi2B,
        "Raspberry Pi Compute Module" => Model::RaspberryPiComputeModule,
        "Raspberry Pi 3 Model B" => Model::RaspberryPi3B,
        "Raspberry Pi Zero" => Model::RaspberryPiZero,
        "Raspberry Pi Compute Module 3" => Model::RaspberryPiComputeModule3,
        "Raspberry Pi Zero W" => Model::RaspberryPiZeroW,
        "Raspberry Pi 3 Model B+" => Model::RaspberryPi3BPlus,
        "Raspberry Pi 3 Model B Plus" => Model::RaspberryPi3BPlus,
        "Raspberry Pi 3 Model A Plus" => Model::RaspberryPi3APlus,
        _ => return Err(Error::UnknownModel),
    };

    Ok(model)
}

/// Retrieves Raspberry Pi device information.
#[derive(Debug, PartialEq, Copy, Clone)]
pub struct DeviceInfo {
    model: Model,
    soc: SoC,
    peripheral_base: u32,
    gpio_offset: u32,
}

impl DeviceInfo {
    /// Constructs a new `DeviceInfo`.
    ///
    /// `new` automatically identifies the Raspberry Pi model and SoC based on
    /// the contents of `/proc/cpuinfo`, `/sys/firmware/devicetree/base/compatible`
    /// and `/sys/firmware/devicetree/base/model`.
    pub fn new() -> Result<DeviceInfo> {
        // Parse order from most-detailed to least-detailed info
        let model = parse_proc_cpuinfo()
            .or_else(|_| parse_base_compatible().or_else(|_| parse_base_model()))?;

        // Set SoC and memory offsets based on model
        match model {
            Model::RaspberryPiA
            | Model::RaspberryPiAPlus
            | Model::RaspberryPiBRev1
            | Model::RaspberryPiBRev2
            | Model::RaspberryPiBPlus
            | Model::RaspberryPiComputeModule
            | Model::RaspberryPiZero
            | Model::RaspberryPiZeroW => Ok(DeviceInfo {
                model,
                soc: SoC::Bcm2835,
                peripheral_base: PERIPHERAL_BASE_RPI,
                gpio_offset: GPIO_OFFSET,
            }),
            Model::RaspberryPi2B => Ok(DeviceInfo {
                model,
                soc: SoC::Bcm2836,
                peripheral_base: PERIPHERAL_BASE_RPI2,
                gpio_offset: GPIO_OFFSET,
            }),
            Model::RaspberryPi3B | Model::RaspberryPiComputeModule3 => Ok(DeviceInfo {
                model,
                soc: SoC::Bcm2837A1,
                peripheral_base: PERIPHERAL_BASE_RPI2,
                gpio_offset: GPIO_OFFSET,
            }),
            Model::RaspberryPi3BPlus | Model::RaspberryPi3APlus => Ok(DeviceInfo {
                model,
                soc: SoC::Bcm2837B0,
                peripheral_base: PERIPHERAL_BASE_RPI2,
                gpio_offset: GPIO_OFFSET,
            }),
        }
    }

    /// Returns the Raspberry Pi's model.
    pub fn model(&self) -> Model {
        self.model
    }

    /// Returns the Raspberry Pi's SoC.
    pub fn soc(&self) -> SoC {
        self.soc
    }

    /// Returns the base memory address for the BCM283x peripherals.
    pub fn peripheral_base(&self) -> u32 {
        self.peripheral_base
    }

    /// Returns the offset from the base memory address for the GPIO section.
    pub fn gpio_offset(&self) -> u32 {
        self.gpio_offset
    }
}
<|endoftext|>


<commit_before>/// Macro to implement a [system of quantities](http://jcgm.bipm.org/vim/en/1.3.html).
#[macro_export]
macro_rules! system {
    (
        $(#[$quantities_attr:meta])* quantities: $quantities:ident {
            $($name:ident: $unit:ident, $symbol:ident;)+
        }
        $(#[$units_attr:meta])* units: $units:ident {
            $($module:ident::$quantity:ident,)+
        }
    ) =>
    {
        $(#[macro_use]
        pub mod $module;)+

        system! {
            $(#[$quantities_attr])*
            quantities: $quantities {
                $($name: $unit, $symbol;)+
            }
            $(#[$units_attr])*
            units: $units {
                $($quantity,)+
            }
        }
    };
    (
        $(#[$quantities_attr:meta])* quantities: $quantities:ident {
            $($name:ident: $unit:ident, $symbol:ident;)+
        }
        $(#[$units_attr:meta])* units: $units:ident {
            $($quantity:ident,)+
        }
    ) =>
    {
        mod system { pub use super::*; }

        /// Property of a phenomenon, body or substance, where the property has a magnitude that
        /// can be expressed as a number and a reference.
        ///
        /// * http://jcgm.bipm.org/vim/en/1.1.html
        #[derive(Copy, Clone, Debug)]
        pub struct Quantity<D, U, V>
            where D: Dimension,
                U: Units<D, V>,
        {
            dimension: $crate::stdlib::marker::PhantomData<D>,
            units: $crate::stdlib::marker::PhantomData<U>,
            value: V,
        }

        /// Marker trait to express the dependence of a [quantity][quantity] on the
        /// [base quantities][base] of a [system of quantities][quantities] as a product of powers
        /// of factors corresponding to the base quantities, omitting any numerical factor.
        ///
        /// * http://jcgm.bipm.org/vim/en/1.7.html
        ///
        /// [quantity]: http://jcgm.bipm.org/vim/en/1.1.html
        /// [base]: http://jcgm.bipm.org/vim/en/1.4.html
        /// [quantities]: http://jcgm.bipm.org/vim/en/1.3.html
        pub trait Dimension {}

        /// Trait to identify a [system of units][units] based on a set of [base units][base] of a
        /// [system of quantities][quantities].
        ///
        /// [units]: http://jcgm.bipm.org/vim/en/1.13.html
        /// [base]: http://jcgm.bipm.org/vim/en/1.10.html
        /// [quantities]: http://jcgm.bipm.org/vim/en/1.3.html
        pub trait Units<D, V>
            where D: Dimension,
        {
            /// Conversion factor for the given base units to the base units for the system of
            /// quantities.
            fn conversion() -> V;
        }

        /// Trait to identify [measurement units][measurement] of individual
        /// [quantities][quantity].
        ///
        /// [measurement]: http://jcgm.bipm.org/vim/en/1.9.html
        /// [quantity]: http://jcgm.bipm.org/vim/en/1.1.html
        pub trait Unit<V> {
            /// Conversion factor for the given unit to the base unit for the quantity.
            fn conversion() -> V;
        }

        $(#[$quantities_attr])*
        #[derive(Clone, Copy, Debug)]
        pub struct $quantities<$($symbol),+>
            where $($symbol: $crate::typenum::Integer,)+ {
            $($name: $crate::stdlib::marker::PhantomData<$symbol>),+
        }

        impl<$($symbol),+> Dimension for $quantities<$($symbol),+>
            where $($symbol: $crate::typenum::Integer,)+ {
        }

        /// Marker struct to identify the [base units][base] of the
        /// [system of quantities][quantities] to be used in the internal representation of a
        /// [quantity][quantity] value.
        ///
        /// [base]: http://jcgm.bipm.org/vim/en/1.10.html
        /// [quantities]: http://jcgm.bipm.org/vim/en/1.3.html
        /// [quantity]: http://jcgm.bipm.org/vim/en/1.1.html
        #[derive(Clone, Copy, Debug)]
        pub struct BaseUnits<$($symbol,)+ V>
            where $($symbol: system::$name::Unit<V>,)+ {
            $($name: $crate::stdlib::marker::PhantomData<$symbol>,)+
            value: $crate::stdlib::marker::PhantomData<V>,
        }

        $(#[$units_attr])*
        pub type $units<V> = BaseUnits<$(system::$name::$unit),+, V>;

        #[doc(hidden)]
        macro_rules! impl_units {
            ($V:ty) => {
                #[allow(non_camel_case_types)]
                impl<$($name,)+ $($symbol),+> Units<$quantities<$($name),+>, $V>
                    for BaseUnits<$($symbol,)+ $V>
                    where $($name: $crate::typenum::Integer,)+
                          $($symbol: system::$name::Unit<$V>,)+
                {
                    #[inline(always)]
                    fn conversion() -> $V {
                        #[cfg(not(feature = "std"))]
                        #[allow(unused_imports)]
                        use $crate::stdlib::num::*;

                        1.0 $(* <$symbol as Unit<$V>>::conversion().powi($name::to_i32()))+
                    }
                }
            };
        }
        #[cfg(feature = "f32")]
        impl_units!(f32);
        #[cfg(feature = "f64")]
        impl_units!(f64);

        /// Macro to implement [quantity](si/struct.Quantity.html) type aliases for a specific
        /// [system of units][units] and value storage type.
        ///
        /// [units]: http://jcgm.bipm.org/vim/en/1.13.html
        #[macro_export]
        macro_rules! $quantities {
            ($path:path, $V:ty) => {
                quantities!($path, $V, $units; $($quantity),+);
            };
            ($path:path, $V:ty, $U:tt) => {
                quantities!($path, $V; $($name),+; $U; $($quantity),+);
            };
        }
    };
}

/// Macro to implement a [quantity][quantity] and associated [measurement units][measurement].
///
/// [quantity]: http://jcgm.bipm.org/vim/en/1.1.html
/// [measurement]: http://jcgm.bipm.org/vim/en/1.9.html
#[macro_export]
macro_rules! quantity {
    (
        $(#[$quantity_attr:meta])* quantity: $quantity:ident;
        $(#[$dim_attr:meta])* dimension: $system:ident<$($dimension:ident),+>;
        units {
            $($unit:ident: $conversion:expr;)+
        }
    ) =>
    {
        $(#[$dim_attr])*
        #[allow(dead_code)]
        pub type Dimension = super::$system<$($crate::typenum::$dimension),+>;

        $(#[$quantity_attr])*
        #[allow(dead_code)]
        pub type $quantity<U, V> = super::Quantity<Dimension, U, V>;

        /// Marker trait to identify measurement units for the quantity. See
        /// [Unit](../trait.Unit.html).
        pub trait Unit<V>: super::Unit<V> {}

        $(/// Measurement unit.
        #[allow(non_camel_case_types)]
        #[derive(Clone, Copy, Debug)]
        pub struct $unit;)+

        #[doc(hidden)]
        macro_rules! impl_quantity {
            ($V:ty) => {
                impl<U> $quantity<U, $V>
                    where U: super::Units<Dimension, $V>,
                {
                    /// Create a new quantity from the given value and measurement unit.
                    #[inline(always)]
                    pub fn new<N>(v: $V, _unit: N) -> Self
                        where N: Unit<$V>,
                    {
                        $quantity {
                            dimension: $crate::stdlib::marker::PhantomData,
                            units: $crate::stdlib::marker::PhantomData,
                            value: v * (<N as super::Unit<$V>>::conversion()
                                    / <U as super::Units<Dimension, $V>>::conversion()),
                        }
                    }
                }

                $(impl Unit<$V> for $unit {}

                impl super::Unit<$V> for $unit {
                    #[inline(always)]
                    fn conversion() -> $V {
                        $conversion
                    }
                })+
            };
        }
        #[cfg(feature = "f32")]
        impl_quantity!(f32);
        #[cfg(feature = "f64")]
        impl_quantity!(f64);

        #[doc(hidden)]
        #[macro_export]
        macro_rules! $quantity {
            ($U:ty, $V:ty) => {
                $(#[$quantity_attr])*
                #[allow(dead_code)]
                pub type $quantity =
                    system::Quantity<system::$system<$($crate::typenum::$dimension),+>, $U, $V>;
            }
        }
    };
}

#[doc(hidden)]
#[macro_export]
macro_rules! quantities {
    ($path:path, $V:ty, $U:ident; $($quantity:ident),+) => {
        use $path as system;

        $($quantity!(system::$U<$V>, $V);)+
    };
    ($path:path, $V:ty; $($name:ident),+; ($($U:ident),+); $($quantity:ident),+) => {
        use $path as system;

        type Units = system::BaseUnits<$(system::$name::$U,)+ $V>;

        $($quantity!(Units, $V);)+
    };
}
<commit_msg>Implement `Quantity` specific accessor methods.<commit_after>/// Macro to implement a [system of quantities](http://jcgm.bipm.org/vim/en/1.3.html).
#[macro_export]
macro_rules! system {
    (
        $(#[$quantities_attr:meta])* quantities: $quantities:ident {
            $($name:ident: $unit:ident, $symbol:ident;)+
        }
        $(#[$units_attr:meta])* units: $units:ident {
            $($module:ident::$quantity:ident,)+
        }
    ) =>
    {
        $(#[macro_use]
        pub mod $module;)+

        system! {
            $(#[$quantities_attr])*
            quantities: $quantities {
                $($name: $unit, $symbol;)+
            }
            $(#[$units_attr])*
            units: $units {
                $($quantity,)+
            }
        }
    };
    (
        $(#[$quantities_attr:meta])* quantities: $quantities:ident {
            $($name:ident: $unit:ident, $symbol:ident;)+
        }
        $(#[$units_attr:meta])* units: $units:ident {
            $($quantity:ident,)+
        }
    ) =>
    {
        mod system { pub use super::*; }

        /// Property of a phenomenon, body or substance, where the property has a magnitude that
        /// can be expressed as a number and a reference.
        ///
        /// * http://jcgm.bipm.org/vim/en/1.1.html
        #[derive(Copy, Clone, Debug)]
        pub struct Quantity<D, U, V>
            where D: Dimension,
                U: Units<D, V>,
        {
            dimension: $crate::stdlib::marker::PhantomData<D>,
            units: $crate::stdlib::marker::PhantomData<U>,
            value: V,
        }

        /// Marker trait to express the dependence of a [quantity][quantity] on the
        /// [base quantities][base] of a [system of quantities][quantities] as a product of powers
        /// of factors corresponding to the base quantities, omitting any numerical factor.
        ///
        /// * http://jcgm.bipm.org/vim/en/1.7.html
        ///
        /// [quantity]: http://jcgm.bipm.org/vim/en/1.1.html
        /// [base]: http://jcgm.bipm.org/vim/en/1.4.html
        /// [quantities]: http://jcgm.bipm.org/vim/en/1.3.html
        pub trait Dimension {}

        /// Trait to identify a [system of units][units] based on a set of [base units][base] of a
        /// [system of quantities][quantities].
        ///
        /// [units]: http://jcgm.bipm.org/vim/en/1.13.html
        /// [base]: http://jcgm.bipm.org/vim/en/1.10.html
        /// [quantities]: http://jcgm.bipm.org/vim/en/1.3.html
        pub trait Units<D, V>
            where D: Dimension,
        {
            /// Conversion factor for the given base units to the base units for the system of
            /// quantities.
            fn conversion() -> V;
        }

        /// Trait to identify [measurement units][measurement] of individual
        /// [quantities][quantity].
        ///
        /// [measurement]: http://jcgm.bipm.org/vim/en/1.9.html
        /// [quantity]: http://jcgm.bipm.org/vim/en/1.1.html
        pub trait Unit<V> {
            /// Conversion factor for the given unit to the base unit for the quantity.
            fn conversion() -> V;
        }

        $(#[$quantities_attr])*
        #[derive(Clone, Copy, Debug)]
        pub struct $quantities<$($symbol),+>
            where $($symbol: $crate::typenum::Integer,)+ {
            $($name: $crate::stdlib::marker::PhantomData<$symbol>),+
        }

        impl<$($symbol),+> Dimension for $quantities<$($symbol),+>
            where $($symbol: $crate::typenum::Integer,)+ {
        }

        /// Marker struct to identify the [base units][base] of the
        /// [system of quantities][quantities] to be used in the internal representation of a
        /// [quantity][quantity] value.
        ///
        /// [base]: http://jcgm.bipm.org/vim/en/1.10.html
        /// [quantities]: http://jcgm.bipm.org/vim/en/1.3.html
        /// [quantity]: http://jcgm.bipm.org/vim/en/1.1.html
        #[derive(Clone, Copy, Debug)]
        pub struct BaseUnits<$($symbol,)+ V>
            where $($symbol: system::$name::Unit<V>,)+ {
            $($name: $crate::stdlib::marker::PhantomData<$symbol>,)+
            value: $crate::stdlib::marker::PhantomData<V>,
        }

        $(#[$units_attr])*
        pub type $units<V> = BaseUnits<$(system::$name::$unit),+, V>;

        #[doc(hidden)]
        macro_rules! impl_units {
            ($V:ty) => {
                #[allow(non_camel_case_types)]
                impl<$($name,)+ $($symbol),+> Units<$quantities<$($name),+>, $V>
                    for BaseUnits<$($symbol,)+ $V>
                    where $($name: $crate::typenum::Integer,)+
                          $($symbol: system::$name::Unit<$V>,)+
                {
                    #[inline(always)]
                    fn conversion() -> $V {
                        #[cfg(not(feature = "std"))]
                        #[allow(unused_imports)]
                        use $crate::stdlib::num::*;

                        1.0 $(* <$symbol as Unit<$V>>::conversion().powi($name::to_i32()))+
                    }
                }
            };
        }
        #[cfg(feature = "f32")]
        impl_units!(f32);
        #[cfg(feature = "f64")]
        impl_units!(f64);

        /// Macro to implement [quantity](si/struct.Quantity.html) type aliases for a specific
        /// [system of units][units] and value storage type.
        ///
        /// [units]: http://jcgm.bipm.org/vim/en/1.13.html
        #[macro_export]
        macro_rules! $quantities {
            ($path:path, $V:ty) => {
                quantities!($path, $V, $units; $($quantity),+);
            };
            ($path:path, $V:ty, $U:tt) => {
                quantities!($path, $V; $($name),+; $U; $($quantity),+);
            };
        }
    };
}

/// Macro to implement a [quantity][quantity] and associated [measurement units][measurement].
///
/// [quantity]: http://jcgm.bipm.org/vim/en/1.1.html
/// [measurement]: http://jcgm.bipm.org/vim/en/1.9.html
#[macro_export]
macro_rules! quantity {
    (
        $(#[$quantity_attr:meta])* quantity: $quantity:ident;
        $(#[$dim_attr:meta])* dimension: $system:ident<$($dimension:ident),+>;
        units {
            $($unit:ident: $conversion:expr;)+
        }
    ) =>
    {
        $(#[$dim_attr])*
        #[allow(dead_code)]
        pub type Dimension = super::$system<$($crate::typenum::$dimension),+>;

        $(#[$quantity_attr])*
        #[allow(dead_code)]
        pub type $quantity<U, V> = super::Quantity<Dimension, U, V>;

        /// Marker trait to identify measurement units for the quantity. See
        /// [Unit](../trait.Unit.html).
        pub trait Unit<V>: super::Unit<V> {}

        $(/// Measurement unit.
        #[allow(non_camel_case_types)]
        #[derive(Clone, Copy, Debug)]
        pub struct $unit;)+

        #[doc(hidden)]
        macro_rules! impl_quantity {
            ($V:ty) => {
                impl<U> $quantity<U, $V>
                    where U: super::Units<Dimension, $V>,
                {
                    /// Create a new quantity from the given value and measurement unit.
                    #[inline(always)]
                    pub fn new<N>(v: $V, _unit: N) -> Self
                        where N: Unit<$V>,
                    {
                        $quantity {
                            dimension: $crate::stdlib::marker::PhantomData,
                            units: $crate::stdlib::marker::PhantomData,
                            value: v * (<N as super::Unit<$V>>::conversion()
                                    / <U as super::Units<Dimension, $V>>::conversion()),
                        }
                    }

                    /// Retrieve the value of the quantity in the given measurement unit.
                    #[inline(always)]
                    pub fn get<N>(self, _unit: N) -> $V
                        where N: Unit<$V>,
                    {
                        self.value * <U as super::Units<Dimension, $V>>::conversion()
                            / <N as super::Unit<$V>>::conversion()
                    }
                }

                $(impl Unit<$V> for $unit {}

                impl super::Unit<$V> for $unit {
                    #[inline(always)]
                    fn conversion() -> $V {
                        $conversion
                    }
                })+
            };
        }
        #[cfg(feature = "f32")]
        impl_quantity!(f32);
        #[cfg(feature = "f64")]
        impl_quantity!(f64);

        #[doc(hidden)]
        #[macro_export]
        macro_rules! $quantity {
            ($U:ty, $V:ty) => {
                $(#[$quantity_attr])*
                #[allow(dead_code)]
                pub type $quantity =
                    system::Quantity<system::$system<$($crate::typenum::$dimension),+>, $U, $V>;
            }
        }
    };
}

#[doc(hidden)]
#[macro_export]
macro_rules! quantities {
    ($path:path, $V:ty, $U:ident; $($quantity:ident),+) => {
        use $path as system;

        $($quantity!(system::$U<$V>, $V);)+
    };
    ($path:path, $V:ty; $($name:ident),+; ($($U:ident),+); $($quantity:ident),+) => {
        use $path as system;

        type Units = system::BaseUnits<$(system::$name::$U,)+ $V>;

        $($quantity!(Units, $V);)+
    };
}
<|endoftext|>


<commit_before>use crate::Result;
use parking_lot::Mutex;
use std::{sync::Arc, thread};

pub type JoinHandle = thread::JoinHandle<Result<()>>;

#[derive(Clone)]
pub struct ThreadHandle(Arc<Mutex<Option<JoinHandle>>>);

impl Default for ThreadHandle {
    fn default() -> Self {
        Self(Arc::new(Mutex::new(None)))
    }
}

impl ThreadHandle {
    pub fn new(handle: JoinHandle) -> Self {
        Self(Arc::new(Mutex::new(Some(handle))))
    }

    pub(crate) fn register(&self, handle: JoinHandle) {
        *self.0.lock() = Some(handle);
    }

    pub(crate) fn wait(&self, context: &'static str) -> Result<()> {
        if let Some(handle) = self.0.lock().take() {
            handle.join().expect(context)?
        }
        Ok(())
    }
}
<commit_msg>thread: make sure to release lock asap<commit_after>use crate::Result;
use parking_lot::Mutex;
use std::{sync::Arc, thread};

pub type JoinHandle = thread::JoinHandle<Result<()>>;

#[derive(Clone)]
pub struct ThreadHandle(Arc<Mutex<Option<JoinHandle>>>);

impl Default for ThreadHandle {
    fn default() -> Self {
        Self(Arc::new(Mutex::new(None)))
    }
}

impl ThreadHandle {
    pub fn new(handle: JoinHandle) -> Self {
        Self(Arc::new(Mutex::new(Some(handle))))
    }

    pub(crate) fn register(&self, handle: JoinHandle) {
        *self.0.lock() = Some(handle);
    }

    fn take(&self) -> Option<JoinHandle> {
        self.0.lock().take()
    }

    pub(crate) fn wait(&self, context: &'static str) -> Result<()> {
        if let Some(handle) = self.take() {
            handle.join().expect(context)?
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before>use nom::{be_u8,be_u16};
use enum_primitive::FromPrimitive;

enum_from_primitive! {
/// Named curves, as defined in [RFC4492](https://tools.ietf.org/html/rfc4492), [RFC7027](https://tools.ietf.org/html/rfc7027), [RFC7919](https://tools.ietf.org/html/rfc7919) and
/// [IANA Supported Groups
/// Registry](https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8)
#[derive(Debug,PartialEq)]
#[repr(u16)]
pub enum NamedGroup {
    Sect163k1 = 1,
    Sect163r1 = 2,
    Sect163r2 = 3,
    Sect193r1 = 4,
    Sect193r2 = 5,
    Sect233k1 = 6,
    Sect233r1 = 7,
    Sect239k1 = 8,
    Sect283k1 = 9,
    Sect283r1 = 10,
    Sect409k1 = 11,
    Sect409r1 = 12,
    Sect571k1 = 13,
    Sect571r1 = 14,
    Secp160k1 = 15,
    Secp160r1 = 16,
    Secp160r2 = 17,
    Secp192k1 = 18,
    Secp192r1 = 19,
    Secp224k1 = 20,
    Secp224r1 = 21,
    Secp256k1 = 22,
    Secp256r1 = 23,
    Secp384r1 = 24,
    Secp521r1 = 25,
    BrainpoolP256r1 = 26,
    BrainpoolP384r1 = 27,
    BrainpoolP512r1 = 28,
    EcdhX25519 = 29,
    EcdhX448 = 30,
    Ffdhe2048 = 0x100,
    Ffdhe3072 = 0x101,
    Ffdhe4096 = 0x102,
    Ffdhe6144 = 0x103,
    Ffdhe8192 = 0x104,
    ArbitraryExplicitPrimeCurves = 0xFF01,
    ArbitraryExplicitChar2Curves = 0xFF02,
}
}

impl NamedGroup {
    /// Return key size of curve in bits, or None if unknown
    pub fn key_bits(self: &NamedGroup) -> Option<u16> {
        match *self {
            NamedGroup::Sect163k1 => Some(163),
            NamedGroup::Sect163r1 => Some(163),
            NamedGroup::Sect163r2 => Some(163),
            NamedGroup::Sect193r1 => Some(193),
            NamedGroup::Sect193r2 => Some(193),
            NamedGroup::Sect233k1 => Some(233),
            NamedGroup::Sect233r1 => Some(233),
            NamedGroup::Sect239k1 => Some(239),
            NamedGroup::Sect283k1 => Some(283),
            NamedGroup::Sect283r1 => Some(283),
            NamedGroup::Sect409k1 => Some(409),
            NamedGroup::Sect409r1 => Some(409),
            NamedGroup::Sect571k1 => Some(571),
            NamedGroup::Sect571r1 => Some(571),
            NamedGroup::Secp160k1 => Some(160),
            NamedGroup::Secp160r1 => Some(160),
            NamedGroup::Secp160r2 => Some(160),
            NamedGroup::Secp192k1 => Some(192),
            NamedGroup::Secp192r1 => Some(192),
            NamedGroup::Secp224k1 => Some(224),
            NamedGroup::Secp224r1 => Some(224),
            NamedGroup::Secp256k1 => Some(256),
            NamedGroup::Secp256r1 => Some(256),
            NamedGroup::Secp384r1 => Some(384),
            NamedGroup::Secp521r1 => Some(521),
            NamedGroup::BrainpoolP256r1 => Some(256),
            NamedGroup::BrainpoolP384r1 => Some(384),
            NamedGroup::BrainpoolP512r1 => Some(521),
            NamedGroup::EcdhX25519 => Some(253),
            _                     => None,
        }
    }
}

pub fn named_curve_of_u16(id: u16) -> Option<NamedGroup> {
    NamedGroup::from_u16(id)
}

#[derive(Debug,PartialEq)]
pub struct ECCurve<'a> {
    pub a: &'a[u8],
    pub b: &'a[u8],
}

enum_from_primitive! {
#[derive(Debug,PartialEq)]
#[repr(u8)]
pub enum ECCurveType {
    ExplicitPrime = 1,
    ExplicitChar2 = 2,
    NamedGroup = 3,
}
}

#[derive(Debug,PartialEq)]
pub struct ECPoint<'a> {
    pub point: &'a[u8],
}

#[derive(Debug,PartialEq)]
pub struct ExplicitPrimeContent<'a> {
    pub prime_p: &'a[u8],
    pub curve: ECCurve<'a>,
    pub base: ECPoint<'a>,
    pub order: &'a[u8],
    pub cofactor: &'a[u8],
}

#[derive(Debug,PartialEq)]
pub enum ECParametersContent<'a> {
    ExplicitPrime(ExplicitPrimeContent<'a>),
    // TODO ExplicitChar2 is defined in [RFC4492] section 5.4
    ExplicitChar2(&'a[u8]),
    NamedGroup(u16),
}

#[derive(Debug,PartialEq)]
pub struct ECParameters<'a> {
    pub curve_type: u8,
    pub params_content: ECParametersContent<'a>,
}

#[derive(Debug,PartialEq)]
pub struct ServerECDHParams<'a> {
    pub curve_params: ECParameters<'a>,
    pub public: ECPoint<'a>,
}

named!(pub parse_ec_point<ECPoint>,
       map!(length_bytes!(be_u8),|d| { ECPoint{ point:d } })
);

named!(parse_ec_curve<ECCurve>,
    do_parse!(
        a: length_bytes!(be_u8) >>
        b: length_bytes!(be_u8) >>
        ( ECCurve{a:a,b:b} )
    )
);

named!(parse_ec_explicit_prime_content<ECParametersContent>,
    do_parse!(
        p:        length_bytes!(be_u8) >>
        curve:    parse_ec_curve >>
        base:     parse_ec_point >>
        order:    length_bytes!(be_u8) >>
        cofactor: length_bytes!(be_u8) >>
        (
            ECParametersContent::ExplicitPrime(
                ExplicitPrimeContent{
                    prime_p:  p,
                    curve:    curve,
                    base:     base,
                    order:    order,
                    cofactor: cofactor,
                }
            )
        )
    )
);

named!(parse_ec_named_curve_content<ECParametersContent>,
    map!(be_u16,|c|{ECParametersContent::NamedGroup(c)})
);

named!(pub parse_ec_parameters<ECParameters>,
    do_parse!(
        curve_type: be_u8  >>
        d: switch!(value!(curve_type),
            1 => call!(parse_ec_explicit_prime_content) |
            3 => call!(parse_ec_named_curve_content)
        ) >>
        (
            ECParameters{
                curve_type: curve_type,
                params_content: d,
            }
        )
    )
);

named!(pub parse_ecdh_params<ServerECDHParams>,
    do_parse!(
        c: parse_ec_parameters >>
        p: parse_ec_point >>
        ( ServerECDHParams{curve_params:c,public:p} )
    )
);
<commit_msg>Remove useless function<commit_after>use nom::{be_u8,be_u16};

enum_from_primitive! {
/// Named curves, as defined in [RFC4492](https://tools.ietf.org/html/rfc4492), [RFC7027](https://tools.ietf.org/html/rfc7027), [RFC7919](https://tools.ietf.org/html/rfc7919) and
/// [IANA Supported Groups
/// Registry](https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8)
#[derive(Debug,PartialEq)]
#[repr(u16)]
pub enum NamedGroup {
    Sect163k1 = 1,
    Sect163r1 = 2,
    Sect163r2 = 3,
    Sect193r1 = 4,
    Sect193r2 = 5,
    Sect233k1 = 6,
    Sect233r1 = 7,
    Sect239k1 = 8,
    Sect283k1 = 9,
    Sect283r1 = 10,
    Sect409k1 = 11,
    Sect409r1 = 12,
    Sect571k1 = 13,
    Sect571r1 = 14,
    Secp160k1 = 15,
    Secp160r1 = 16,
    Secp160r2 = 17,
    Secp192k1 = 18,
    Secp192r1 = 19,
    Secp224k1 = 20,
    Secp224r1 = 21,
    Secp256k1 = 22,
    Secp256r1 = 23,
    Secp384r1 = 24,
    Secp521r1 = 25,
    BrainpoolP256r1 = 26,
    BrainpoolP384r1 = 27,
    BrainpoolP512r1 = 28,
    EcdhX25519 = 29,
    EcdhX448 = 30,
    Ffdhe2048 = 0x100,
    Ffdhe3072 = 0x101,
    Ffdhe4096 = 0x102,
    Ffdhe6144 = 0x103,
    Ffdhe8192 = 0x104,
    ArbitraryExplicitPrimeCurves = 0xFF01,
    ArbitraryExplicitChar2Curves = 0xFF02,
}
}

impl NamedGroup {
    /// Return key size of curve in bits, or None if unknown
    pub fn key_bits(self: &NamedGroup) -> Option<u16> {
        match *self {
            NamedGroup::Sect163k1 => Some(163),
            NamedGroup::Sect163r1 => Some(163),
            NamedGroup::Sect163r2 => Some(163),
            NamedGroup::Sect193r1 => Some(193),
            NamedGroup::Sect193r2 => Some(193),
            NamedGroup::Sect233k1 => Some(233),
            NamedGroup::Sect233r1 => Some(233),
            NamedGroup::Sect239k1 => Some(239),
            NamedGroup::Sect283k1 => Some(283),
            NamedGroup::Sect283r1 => Some(283),
            NamedGroup::Sect409k1 => Some(409),
            NamedGroup::Sect409r1 => Some(409),
            NamedGroup::Sect571k1 => Some(571),
            NamedGroup::Sect571r1 => Some(571),
            NamedGroup::Secp160k1 => Some(160),
            NamedGroup::Secp160r1 => Some(160),
            NamedGroup::Secp160r2 => Some(160),
            NamedGroup::Secp192k1 => Some(192),
            NamedGroup::Secp192r1 => Some(192),
            NamedGroup::Secp224k1 => Some(224),
            NamedGroup::Secp224r1 => Some(224),
            NamedGroup::Secp256k1 => Some(256),
            NamedGroup::Secp256r1 => Some(256),
            NamedGroup::Secp384r1 => Some(384),
            NamedGroup::Secp521r1 => Some(521),
            NamedGroup::BrainpoolP256r1 => Some(256),
            NamedGroup::BrainpoolP384r1 => Some(384),
            NamedGroup::BrainpoolP512r1 => Some(521),
            NamedGroup::EcdhX25519 => Some(253),
            _                     => None,
        }
    }
}

#[derive(Debug,PartialEq)]
pub struct ECCurve<'a> {
    pub a: &'a[u8],
    pub b: &'a[u8],
}

enum_from_primitive! {
#[derive(Debug,PartialEq)]
#[repr(u8)]
pub enum ECCurveType {
    ExplicitPrime = 1,
    ExplicitChar2 = 2,
    NamedGroup = 3,
}
}

#[derive(Debug,PartialEq)]
pub struct ECPoint<'a> {
    pub point: &'a[u8],
}

#[derive(Debug,PartialEq)]
pub struct ExplicitPrimeContent<'a> {
    pub prime_p: &'a[u8],
    pub curve: ECCurve<'a>,
    pub base: ECPoint<'a>,
    pub order: &'a[u8],
    pub cofactor: &'a[u8],
}

#[derive(Debug,PartialEq)]
pub enum ECParametersContent<'a> {
    ExplicitPrime(ExplicitPrimeContent<'a>),
    // TODO ExplicitChar2 is defined in [RFC4492] section 5.4
    ExplicitChar2(&'a[u8]),
    NamedGroup(u16),
}

#[derive(Debug,PartialEq)]
pub struct ECParameters<'a> {
    pub curve_type: u8,
    pub params_content: ECParametersContent<'a>,
}

#[derive(Debug,PartialEq)]
pub struct ServerECDHParams<'a> {
    pub curve_params: ECParameters<'a>,
    pub public: ECPoint<'a>,
}

named!(pub parse_ec_point<ECPoint>,
       map!(length_bytes!(be_u8),|d| { ECPoint{ point:d } })
);

named!(parse_ec_curve<ECCurve>,
    do_parse!(
        a: length_bytes!(be_u8) >>
        b: length_bytes!(be_u8) >>
        ( ECCurve{a:a,b:b} )
    )
);

named!(parse_ec_explicit_prime_content<ECParametersContent>,
    do_parse!(
        p:        length_bytes!(be_u8) >>
        curve:    parse_ec_curve >>
        base:     parse_ec_point >>
        order:    length_bytes!(be_u8) >>
        cofactor: length_bytes!(be_u8) >>
        (
            ECParametersContent::ExplicitPrime(
                ExplicitPrimeContent{
                    prime_p:  p,
                    curve:    curve,
                    base:     base,
                    order:    order,
                    cofactor: cofactor,
                }
            )
        )
    )
);

named!(parse_ec_named_curve_content<ECParametersContent>,
    map!(be_u16,|c|{ECParametersContent::NamedGroup(c)})
);

named!(pub parse_ec_parameters<ECParameters>,
    do_parse!(
        curve_type: be_u8  >>
        d: switch!(value!(curve_type),
            1 => call!(parse_ec_explicit_prime_content) |
            3 => call!(parse_ec_named_curve_content)
        ) >>
        (
            ECParameters{
                curve_type: curve_type,
                params_content: d,
            }
        )
    )
);

named!(pub parse_ecdh_params<ServerECDHParams>,
    do_parse!(
        c: parse_ec_parameters >>
        p: parse_ec_point >>
        ( ServerECDHParams{curve_params:c,public:p} )
    )
);
<|endoftext|>


<commit_before>/// Trait implemented by all types that can be stored directly in the GC heap:
/// the `Storage` types associated with any `IntoHeap` type.
///
pub unsafe trait InHeap<'a>: Sized {
    type Out: IntoHeap<'a, In=Self>;

    unsafe fn mark(ptr: *mut Self);

    /// Extract the value from the heap. This is for macro-generated code to
    /// call; it is impossible for ordinary users to call this safely, because
    /// `self` must be a direct, unwrapped reference to a value stored in the
    /// GC heap, which ordinary users cannot obtain.
    ///
    /// This turns any raw pointers in the `Storage` value into safe
    /// references, so while it's a dangerous function, the result of a correct
    /// call can be safely handed out to user code.
    ///
    unsafe fn from_heap(&self) -> Self::Out;
}

/// Non-inlined function that serves as an entry point to marking. This is used
/// for marking root set entries.
pub unsafe fn mark_entry_point<'a, T: InHeap<'a>>(addr: *mut ()) {
    InHeap::mark(addr as *mut T);
}

/// `IntoHeap` types are safe to use and can be stored in a field of a GC struct.
///
/// "Safe to use" means they don't expose raw pointers to GC memory, and they
/// obey Rust's safety and aliasing rules. If a `IntoHeap` value contains a
/// pointer to a GC allocation, then that allocation (and everything reachable
/// from it) is protected from GC.
///
/// "Stored in a field of a GC struct" - This includes primitive types like `i32`,
/// but also GC structs themselves (because they can be nested).
///
/// This trait is unsafe to implement for several reasons, ranging from:
///
/// *   The common-sense: API users aren't supposed to know or care about this.
///     It is only public so that the public macros can see it. Use the macros.
///
/// *   ...To the obvious: `Storage` objects are full of pointers and if
///     `into_heap` puts garbage into them, GC will crash.
///
/// *   ...To the subtle: `from_heap` receives a non-mut reference to a heap
///     value. But there may exist gc-references to that value, in which case
///     `from_heap` (or other code it calls) could modify the value while this
///     direct, non-mut reference exists, which could lead to crashes (due to
///     changing enums if nothing else) - all without using any unsafe code.
///
pub unsafe trait IntoHeap<'a>: Sized {
    /// The type of the value when it is physically stored in the heap.
    type In: InHeap<'a, Out=Self>;

    /// Convert the value to the form it should have in the heap.
    /// This is for macro-generated code to call.
    fn into_heap(self) -> Self::In;
}

pub fn heap_type_id<'a, T: InHeap<'a>>() -> usize {
    mark_entry_point::<T> as *const () as usize
}

macro_rules! gc_trivial_impl {
    ($t:ty) => {
        unsafe impl<'a> InHeap<'a> for $t {
            type Out = $t;
            unsafe fn mark(_ptr: *mut $t) {}
            unsafe fn from_heap(&self) -> $t { self.clone() }
        }

        unsafe impl<'a> IntoHeap<'a> for $t {
            type In = $t;
            fn into_heap(self) -> $t { self }
        }
    }
}

gc_trivial_impl!(bool);
gc_trivial_impl!(char);
gc_trivial_impl!(i8);
gc_trivial_impl!(u8);
gc_trivial_impl!(i16);
gc_trivial_impl!(u16);
gc_trivial_impl!(i32);
gc_trivial_impl!(u32);
gc_trivial_impl!(i64);
gc_trivial_impl!(u64);
gc_trivial_impl!(isize);
gc_trivial_impl!(usize);
gc_trivial_impl!(f32);
gc_trivial_impl!(f64);

use std::rc::Rc;
gc_trivial_impl!(Rc<String>);

/*
// 'static types are heap-safe because ref types are never 'static.
// Unfortunately I can't make the compiler understand this: the rules
// to prevent conflicting trait impls make this conflict with almost
// everything.
unsafe impl<'a, T: Clone + 'static> InHeap<'a> for T { ...trivial... }
unsafe impl<'a, T: Clone + 'static> IntoHeap<'a> for T { ...trivial... }
*/
<commit_msg>traits: Comments.<commit_after>/// `InHeap` types can be stored directly in the GC heap.
///
/// Application code does not need to use this trait. It is only public so that
/// the public macros can use it. Use the friendly macros!
///
/// GC types come in pairs: an `InHeap` type, which is stored physically inside
/// the heap, and may be packed with pointer fields and unsafe methods; and an
/// `IntoHeap` type, which is safe, is used in application code, and lives on
/// the stack.
///
/// Users never get a direct `&` reference to any value stored in the heap. All
/// access is through safe `IntoHeap` types, like `GCRef`.
///
/// *   For primitive types, like `i32`, the two types are the same.
///
/// *   `GCRef<'a, T>` is an `IntoHeap` type; the corresponding `InHeap` type
///     is the pointer type `*mut T`.
///
/// *   For macro-generated structs and enums, the user specifies the names of
///     both types. Both types have identical fields or variants, but fields of
///     the `InHeap` type are `InHeap`, and fields of the `IntoHeap` type are
///     `IntoHeap`.
///
/// Unsafe to implement: `from_heap` receives a non-mut reference to a heap
/// value. There may exist gc-references to that value, in which case
/// `from_heap` (or other code it calls) could *without using any unsafe code*
/// modify the value while this direct, non-mut reference exists. This breaks
/// Rust's aliasing rules and could cause crashes due to changing enums, if
/// nothing else.
///
pub unsafe trait InHeap<'a>: Sized {
    type Out: IntoHeap<'a, In=Self>;

    unsafe fn mark(ptr: *mut Self);

    /// Extract the value from the heap. This turns any raw pointers in the
    /// `InHeap` value into safe references, so while it's an unsafe function,
    /// the result of a correct call can be safely handed out to user code.
    ///
    /// Unsafe to call: It is impossible for ordinary users to call this
    /// safely, because `self` must be a direct, unwrapped reference to a value
    /// stored in the GC heap, which ordinary users cannot obtain.
    ///
    unsafe fn from_heap(&self) -> Self::Out;
}

/// Non-inlined function that serves as an entry point to marking. This is used
/// for marking root set entries.
pub unsafe fn mark_entry_point<'a, T: InHeap<'a>>(addr: *mut ()) {
    InHeap::mark(addr as *mut T);
}

/// `IntoHeap` types live on the stack, in application code. They are the
/// safe, user-friendly analogues to pointer-filled `InHeap` types. When you
/// use `gc_ref_type!` to declare a new GC struct or enum, `IntoHeap` types are
/// the types you use for fields.
///
/// Application code does not need to use this trait. It is only public so that
/// the public macros can use it. Use the friendly macros!
///
/// "Safe to use" means they don't expose pointers or references to GC memory;
/// and they obey Rust's safety and aliasing rules. If an `IntoHeap` value
/// contains a pointer to a GC allocation, then that allocation (and everything
/// reachable from it) is protected from GC.
///
/// "Stored in a field of a GC struct" - This includes primitive types like
/// `i32`, but also GC structs and enums (they can nest).
///
/// Unsafe to implement: `InHeap` objects are full of pointers; if `into_heap`
/// puts garbage into them, GC will crash.
///
pub unsafe trait IntoHeap<'a>: Sized {
    /// The type of the value when it is physically stored in the heap.
    type In: InHeap<'a, Out=Self>;

    /// Convert the value to the form it should have in the heap.
    /// This is for macro-generated code to call.
    fn into_heap(self) -> Self::In;
}

pub fn heap_type_id<'a, T: InHeap<'a>>() -> usize {
    mark_entry_point::<T> as *const () as usize
}

macro_rules! gc_trivial_impl {
    ($t:ty) => {
        unsafe impl<'a> InHeap<'a> for $t {
            type Out = $t;
            unsafe fn mark(_ptr: *mut $t) {}
            unsafe fn from_heap(&self) -> $t { self.clone() }
        }

        unsafe impl<'a> IntoHeap<'a> for $t {
            type In = $t;
            fn into_heap(self) -> $t { self }
        }
    }
}

gc_trivial_impl!(bool);
gc_trivial_impl!(char);
gc_trivial_impl!(i8);
gc_trivial_impl!(u8);
gc_trivial_impl!(i16);
gc_trivial_impl!(u16);
gc_trivial_impl!(i32);
gc_trivial_impl!(u32);
gc_trivial_impl!(i64);
gc_trivial_impl!(u64);
gc_trivial_impl!(isize);
gc_trivial_impl!(usize);
gc_trivial_impl!(f32);
gc_trivial_impl!(f64);

use std::rc::Rc;
gc_trivial_impl!(Rc<String>);

/*
// 'static types are heap-safe because ref types are never 'static.
// Unfortunately I can't make the compiler understand this: the rules
// to prevent conflicting trait impls make this conflict with almost
// everything.
unsafe impl<'a, T: Clone + 'static> InHeap<'a> for T { ...trivial... }
unsafe impl<'a, T: Clone + 'static> IntoHeap<'a> for T { ...trivial... }
*/
<|endoftext|>


<commit_before>extern crate serialize;

use serialize::{Decodable,json};
use std::io::fs::File;
use std::io::process::{Command,ProcessOutput};
use std::io::{fs,IoResult,Truncate,UserRWX,Write};
use std::str;

#[deriving(Decodable,Show)]
struct Example {
    id: StrBuf,
    title: StrBuf,
}

fn read(file: &Path) -> IoResult<~str> {
    let mut file = try!(File::open(file));

    Ok(try!(file.read_to_str()))
}

fn write(file: &Path, contents: &str) -> IoResult<()> {
    let mut file = try!(File::open_mode(file, Truncate, Write));

    Ok(try!(file.write_str(contents)))
}

fn update(example: &Example) -> bool {
    print!("{}... ", example.id);

    let src_dir = Path::new(format!("src/{}", example.id));
    let out_dir = Path::new(format!("output/examples/{}", example.id));

    let sources: Vec<Path> = match fs::walk_dir(&src_dir) {
        Err(_) => {
            println!("couldn't read {}", src_dir.display());
            return false
        } Ok(contents) => contents.filter(|path| match path.extension_str() {
            None => false,
            Some(extension) => extension == "rs",
        }).collect(),
    };

    let mut template = match read(&src_dir.join("input.md")) {
        Err(_) => {
            println!("couldn't read input.md");
            return false
        } Ok(contents) => format!("\\# {}\n\n{}", example.title, contents),
    };

    // insert source code in markdown
    for source in sources.iter() {
        let path = source.as_str().unwrap().split('/').skip(2).collect::<Vec<&str>>().connect("/");
        let code = match read(source) {
            Err(_) => {
                println!("couldn't read {}", path);
                return false;
            }, Ok(contents) => format!("``` rust\n// {}\n{}```", path, contents),
        };

        template = template.replace(format!("\\{{}\\}", path), code);
    }

    // insert program output in markdown
    for source in sources.iter() {
        let filename = source.filename_str().unwrap();
        let token = format!("\\{{}\\}", source.with_extension("out").filename_str().unwrap());

        if template.contains(token) {
            match compile_run(source) {
                Err(action) => {
                    println!("couldn't {} {}", action, filename);
                    return false;
                }, Ok(output) => {
                    let output = format!("```\n$ rustc {} && ./{}\n{}```",
                                         filename,
                                         filename.split('.').nth(0).unwrap(),
                                         output);

                    template = template.replace(token, output);
                }
            }
        }
    }

    if !out_dir.exists() {
        fs::mkdir(&out_dir, UserRWX).unwrap();
    }

    match write(&out_dir.join("README.md"), template) {
        Err(_) => {
            println!("couldn't write README.md");
            false
        } Ok(_) => {
            println!("DONE");
            true
        }
    }
}

fn compile_run(path: &Path) -> Result<~str, &'static str> {
    match Command::new("rustc").args([path.as_str().unwrap(), "-o", "executable"]).output() {
        Err(_) => return Err("compile"),
        Ok(out) => {
            if !out.status.success() {
                return Ok(str::from_utf8_owned(out.error.as_slice().to_owned()).unwrap());
            }
        }
    }

    match Command::new("./executable").output() {
        Err(_) => Err("run"),
        Ok(out) => {
            fs::unlink(&Path::new("./executable")).unwrap();
            let ProcessOutput { status: _, output: out, error: err } = out;
            let stdout = str::from_utf8_owned(out.as_slice().to_owned()).unwrap();
            let stderr = str::from_utf8_owned(err.as_slice().to_owned()).unwrap();

            Ok(vec!(stdout, stderr).concat())
        }
    }
}

fn main() {
    let src_dir = Path::new("src");

    let examples: Vec<Example> = match read(&src_dir.join("order.json")) {
        Err(err) => fail!("couldn't read order.json: {}", err),
        Ok(string) => match json::from_str(string) {
            Err(err) => fail!("order.json is invalid json: {}", err),
            Ok(json) => match Decodable::decode(&mut json::Decoder::new(json)) {
                Err(err) => fail!("error decoding order.json: {}", err),
                Ok(data) => data,
            }
        }
    };

    let mut indent = false;
    let summary = examples.move_iter().map(|example| {
        if example.id.as_slice() == "staging" {
            indent = true;
        }

        let chapter = if update(&example) {
            format!("* [{}](examples/{}/README.md)", example.title, example.id)
        } else {
            format!("* {}", example.title)
        };

        if indent &&
            example.id.as_slice() != "staging" &&
            example.id.as_slice() != "todo" {
            format!("  {}", chapter)
        } else {
            chapter
        }
    }).collect::<Vec<~str>>().connect("\n");

    if write(&Path::new("output/SUMMARY.md"), summary).is_err() {
        fail!("failed to write SUMMARY.md");
    }
}
<commit_msg>update.rs: fix removal of ~str, and renaming of StrBuf to String<commit_after>extern crate serialize;

use serialize::{Decodable,json};
use std::io::fs::File;
use std::io::process::{Command,ProcessOutput};
use std::io::{fs,IoResult,Truncate,UserRWX,Write};
use std::str;

#[deriving(Decodable,Show)]
struct Example {
    id: String,
    title: String,
}

fn read(file: &Path) -> IoResult<String> {
    let mut file = try!(File::open(file));

    Ok(try!(file.read_to_str()))
}

fn write(file: &Path, contents: &str) -> IoResult<()> {
    let mut file = try!(File::open_mode(file, Truncate, Write));

    Ok(try!(file.write_str(contents)))
}

fn update(example: &Example) -> bool {
    print!("{}... ", example.id);

    let src_dir = Path::new(format!("src/{}", example.id));
    let out_dir = Path::new(format!("output/examples/{}", example.id));

    let sources: Vec<Path> = match fs::walk_dir(&src_dir) {
        Err(_) => {
            println!("couldn't read {}", src_dir.display());
            return false
        } Ok(contents) => contents.filter(|path| match path.extension_str() {
            None => false,
            Some(extension) => extension == "rs",
        }).collect(),
    };

    let mut template = match read(&src_dir.join("input.md")) {
        Err(_) => {
            println!("couldn't read input.md");
            return false
        } Ok(contents) => format!("\\# {}\n\n{}", example.title, contents),
    };

    // insert source code in markdown
    for source in sources.iter() {
        let path = source.as_str().unwrap().split('/').skip(2).collect::<Vec<&str>>().connect("/");
        let code = match read(source) {
            Err(_) => {
                println!("couldn't read {}", path);
                return false;
            }, Ok(contents) => format!("``` rust\n// {}\n{}```", path, contents),
        };

        template = template.replace(format!("\\{{}\\}", path).as_slice(),
                                    code.as_slice());
    }

    // insert program output in markdown
    for source in sources.iter() {
        let filename = source.filename_str().unwrap();
        let token = format!("\\{{}\\}", source.with_extension("out").filename_str().unwrap());

        if template.as_slice().contains(token.as_slice()) {
            match compile_run(source) {
                Err(action) => {
                    println!("couldn't {} {}", action, filename);
                    return false;
                }, Ok(output) => {
                    let output = format!("```\n$ rustc {} && ./{}\n{}```",
                                         filename,
                                         filename.split('.').nth(0).unwrap(),
                                         output);

                    template = template.replace(token.as_slice(),
                                                output.as_slice());
                }
            }
        }
    }

    if !out_dir.exists() {
        fs::mkdir(&out_dir, UserRWX).unwrap();
    }

    match write(&out_dir.join("README.md"), template.as_slice()) {
        Err(_) => {
            println!("couldn't write README.md");
            false
        } Ok(_) => {
            println!("DONE");
            true
        }
    }
}

fn compile_run(path: &Path) -> Result<String, &'static str> {
    match Command::new("rustc").args([path.as_str().unwrap(), "-o", "executable"]).output() {
        Err(_) => return Err("compile"),
        Ok(out) => {
            if !out.status.success() {
                return Ok(str::from_utf8_owned(out.error).unwrap());
            }
        }
    }

    match Command::new("./executable").output() {
        Err(_) => Err("run"),
        Ok(out) => {
            fs::unlink(&Path::new("./executable")).unwrap();
            let ProcessOutput { status: _, output: out, error: err } = out;
            let stdout = str::from_utf8_owned(out).unwrap();
            let stderr = str::from_utf8_owned(err).unwrap();

            Ok(vec!(stdout, stderr).concat())
        }
    }
}

fn main() {
    let src_dir = Path::new("src");

    let examples: Vec<Example> = match read(&src_dir.join("order.json")) {
        Err(err) => fail!("couldn't read order.json: {}", err),
        Ok(string) => match json::from_str(string.as_slice()) {
            Err(err) => fail!("order.json is invalid json: {}", err),
            Ok(json) => match Decodable::decode(&mut json::Decoder::new(json)) {
                Err(err) => fail!("error decoding order.json: {}", err),
                Ok(data) => data,
            }
        }
    };

    let mut indent = false;
    let summary = examples.move_iter().map(|example| {
        if example.id.as_slice() == "staging" {
            indent = true;
        }

        let chapter = if update(&example) {
            format!("* [{}](examples/{}/README.md)", example.title, example.id)
        } else {
            format!("* {}", example.title)
        };

        if indent &&
            example.id.as_slice() != "staging" &&
            example.id.as_slice() != "todo" {
            format!("  {}", chapter)
        } else {
            chapter
        }
    }).collect::<Vec<String>>().connect("\n");

    if write(&Path::new("output/SUMMARY.md"), summary.as_slice()).is_err() {
        fail!("failed to write SUMMARY.md");
    }
}
<|endoftext|>


<commit_before>use rustc_hash::{FxHashSet};
use ra_editor::find_node_at_offset;
use ra_syntax::{
    algo::visit::{visitor, Visitor},
    SourceFileNode, AstNode,
    ast::{self, LoopBodyOwner},
    SyntaxKind::*,
};

use crate::{
    db::RootDatabase,
    input::{SourceRootId},
    completion::CompletionItem,
    descriptors::module::{ModuleId, ModuleScope, ModuleTree},
    descriptors::function::FnScopes,
    descriptors::DescriptorDatabase,
    Cancelable
};

pub(super) fn completions(
    acc: &mut Vec<CompletionItem>,
    db: &RootDatabase,
    source_root_id: SourceRootId,
    module_tree: &ModuleTree,
    module_id: ModuleId,
    file: &SourceFileNode,
    name_ref: ast::NameRef,
) -> Cancelable<()> {
    let kind = match classify_name_ref(name_ref) {
        Some(it) => it,
        None => return Ok(()),
    };

    match kind {
        NameRefKind::LocalRef => {
            let module_scope = db.module_scope(source_root_id, module_id)?;
            if let Some(fn_def) = complete_local_name(acc, &module_scope, name_ref) {
                complete_expr_keywords(&file, fn_def, name_ref, acc);
                complete_expr_snippets(acc);
            }
        }
        NameRefKind::CratePath(path) => {
            complete_path(acc, db, source_root_id, module_tree, module_id, path)?
        }
        NameRefKind::BareIdentInMod => {
            let name_range = name_ref.syntax().range();
            let top_node = name_ref
                .syntax()
                .ancestors()
                .take_while(|it| it.range() == name_range)
                .last()
                .unwrap();
            match top_node.parent().map(|it| it.kind()) {
                Some(SOURCE_FILE) | Some(ITEM_LIST) => complete_mod_item_snippets(acc),
                _ => (),
            }
        }
    }
    Ok(())
}

enum NameRefKind<'a> {
    /// NameRef is a part of single-segment path, for example, a refernece to a
    /// local variable.
    LocalRef,
    /// NameRef is the last segment in crate:: path
    CratePath(Vec<ast::NameRef<'a>>),
    /// NameRef is bare identifier at the module's root.
    /// Used for keyword completion
    BareIdentInMod,
}

fn classify_name_ref(name_ref: ast::NameRef) -> Option<NameRefKind> {
    let name_range = name_ref.syntax().range();
    let top_node = name_ref
        .syntax()
        .ancestors()
        .take_while(|it| it.range() == name_range)
        .last()
        .unwrap();
    match top_node.parent().map(|it| it.kind()) {
        Some(SOURCE_FILE) | Some(ITEM_LIST) => return Some(NameRefKind::BareIdentInMod),
        _ => (),
    }

    let parent = name_ref.syntax().parent()?;
    if let Some(segment) = ast::PathSegment::cast(parent) {
        let path = segment.parent_path();
        if path.qualifier().is_none() {
            return Some(NameRefKind::LocalRef);
        }
        if let Some(crate_path) = crate_path(path) {
            return Some(NameRefKind::CratePath(crate_path));
        }
    }
    None
}

fn crate_path(mut path: ast::Path) -> Option<Vec<ast::NameRef>> {
    let mut res = Vec::new();
    loop {
        let segment = path.segment()?;
        match segment.kind()? {
            ast::PathSegmentKind::Name(name) => res.push(name),
            ast::PathSegmentKind::CrateKw => break,
            ast::PathSegmentKind::SelfKw | ast::PathSegmentKind::SuperKw => return None,
        }
        path = path.qualifier()?;
    }
    res.reverse();
    Some(res)
}

fn complete_local_name<'a>(
    acc: &mut Vec<CompletionItem>,
    module_scope: &ModuleScope,
    name_ref: ast::NameRef<'a>,
) -> Option<ast::FnDef<'a>> {
    let enclosing_fn = name_ref
        .syntax()
        .ancestors()
        .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
        .find_map(ast::FnDef::cast);
    if let Some(fn_def) = enclosing_fn {
        let scopes = FnScopes::new(fn_def);
        complete_fn(name_ref, &scopes, acc);
    }

    acc.extend(
        module_scope
            .entries()
            .iter()
            .filter(|entry| {
                // Don't expose this item
                !entry.ptr().range().is_subrange(&name_ref.syntax().range())
            })
            .map(|entry| CompletionItem {
                label: entry.name().to_string(),
                lookup: None,
                snippet: None,
            }),
    );
    enclosing_fn
}

fn complete_fn(name_ref: ast::NameRef, scopes: &FnScopes, acc: &mut Vec<CompletionItem>) {
    let mut shadowed = FxHashSet::default();
    acc.extend(
        scopes
            .scope_chain(name_ref.syntax())
            .flat_map(|scope| scopes.entries(scope).iter())
            .filter(|entry| shadowed.insert(entry.name()))
            .map(|entry| CompletionItem {
                label: entry.name().to_string(),
                lookup: None,
                snippet: None,
            }),
    );
    if scopes.self_param.is_some() {
        acc.push(CompletionItem {
            label: "self".to_string(),
            lookup: None,
            snippet: None,
        })
    }
}

fn complete_path(
    acc: &mut Vec<CompletionItem>,
    db: &RootDatabase,
    source_root_id: SourceRootId,
    module_tree: &ModuleTree,
    module_id: ModuleId,
    crate_path: Vec<ast::NameRef>,
) -> Cancelable<()> {
    let target_module_id = match find_target_module(module_tree, module_id, crate_path) {
        None => return Ok(()),
        Some(it) => it,
    };
    let module_scope = db.module_scope(source_root_id, target_module_id)?;
    let completions = module_scope.entries().iter().map(|entry| CompletionItem {
        label: entry.name().to_string(),
        lookup: None,
        snippet: None,
    });
    acc.extend(completions);
    Ok(())
}

fn find_target_module(
    module_tree: &ModuleTree,
    module_id: ModuleId,
    mut crate_path: Vec<ast::NameRef>,
) -> Option<ModuleId> {
    crate_path.pop();
    let mut target_module = module_id.root(&module_tree);
    for name in crate_path {
        target_module = target_module.child(module_tree, name.text().as_str())?;
    }
    Some(target_module)
}

fn complete_mod_item_snippets(acc: &mut Vec<CompletionItem>) {
    acc.push(CompletionItem {
        label: "tfn".to_string(),
        lookup: None,
        snippet: Some("#[test]\nfn $1() {\n    $0\n}".to_string()),
    });
    acc.push(CompletionItem {
        label: "pub(crate)".to_string(),
        lookup: None,
        snippet: Some("pub(crate) $0".to_string()),
    })
}

fn complete_expr_keywords(
    file: &SourceFileNode,
    fn_def: ast::FnDef,
    name_ref: ast::NameRef,
    acc: &mut Vec<CompletionItem>,
) {
    acc.push(keyword("if", "if $0 {}"));
    acc.push(keyword("match", "match $0 {}"));
    acc.push(keyword("while", "while $0 {}"));
    acc.push(keyword("loop", "loop {$0}"));

    if let Some(off) = name_ref.syntax().range().start().checked_sub(2.into()) {
        if let Some(if_expr) = find_node_at_offset::<ast::IfExpr>(file.syntax(), off) {
            if if_expr.syntax().range().end() < name_ref.syntax().range().start() {
                acc.push(keyword("else", "else {$0}"));
                acc.push(keyword("else if", "else if $0 {}"));
            }
        }
    }
    if is_in_loop_body(name_ref) {
        acc.push(keyword("continue", "continue"));
        acc.push(keyword("break", "break"));
    }
    acc.extend(complete_return(fn_def, name_ref));
}

fn is_in_loop_body(name_ref: ast::NameRef) -> bool {
    for node in name_ref.syntax().ancestors() {
        if node.kind() == FN_DEF || node.kind() == LAMBDA_EXPR {
            break;
        }
        let loop_body = visitor()
            .visit::<ast::ForExpr, _>(LoopBodyOwner::loop_body)
            .visit::<ast::WhileExpr, _>(LoopBodyOwner::loop_body)
            .visit::<ast::LoopExpr, _>(LoopBodyOwner::loop_body)
            .accept(node);
        if let Some(Some(body)) = loop_body {
            if name_ref
                .syntax()
                .range()
                .is_subrange(&body.syntax().range())
            {
                return true;
            }
        }
    }
    false
}

fn complete_return(fn_def: ast::FnDef, name_ref: ast::NameRef) -> Option<CompletionItem> {
    // let is_last_in_block = name_ref.syntax().ancestors().filter_map(ast::Expr::cast)
    //     .next()
    //     .and_then(|it| it.syntax().parent())
    //     .and_then(ast::Block::cast)
    //     .is_some();

    // if is_last_in_block {
    //     return None;
    // }

    let is_stmt = match name_ref
        .syntax()
        .ancestors()
        .filter_map(ast::ExprStmt::cast)
        .next()
    {
        None => false,
        Some(expr_stmt) => expr_stmt.syntax().range() == name_ref.syntax().range(),
    };
    let snip = match (is_stmt, fn_def.ret_type().is_some()) {
        (true, true) => "return $0;",
        (true, false) => "return;",
        (false, true) => "return $0",
        (false, false) => "return",
    };
    Some(keyword("return", snip))
}

fn keyword(kw: &str, snip: &str) -> CompletionItem {
    CompletionItem {
        label: kw.to_string(),
        lookup: None,
        snippet: Some(snip.to_string()),
    }
}

fn complete_expr_snippets(acc: &mut Vec<CompletionItem>) {
    acc.push(CompletionItem {
        label: "pd".to_string(),
        lookup: None,
        snippet: Some("eprintln!(\"$0 = {:?}\", $0);".to_string()),
    });
    acc.push(CompletionItem {
        label: "ppd".to_string(),
        lookup: None,
        snippet: Some("eprintln!(\"$0 = {:#?}\", $0);".to_string()),
    });
}
<commit_msg>Do more work in classify<commit_after>use rustc_hash::{FxHashSet};
use ra_editor::find_node_at_offset;
use ra_syntax::{
    algo::visit::{visitor, Visitor},
    SourceFileNode, AstNode,
    ast::{self, LoopBodyOwner},
    SyntaxKind::*,
};

use crate::{
    db::RootDatabase,
    input::{SourceRootId},
    completion::CompletionItem,
    descriptors::module::{ModuleId, ModuleTree},
    descriptors::function::FnScopes,
    descriptors::DescriptorDatabase,
    Cancelable
};

pub(super) fn completions(
    acc: &mut Vec<CompletionItem>,
    db: &RootDatabase,
    source_root_id: SourceRootId,
    module_tree: &ModuleTree,
    module_id: ModuleId,
    file: &SourceFileNode,
    name_ref: ast::NameRef,
) -> Cancelable<()> {
    let kind = match classify_name_ref(name_ref) {
        Some(it) => it,
        None => return Ok(()),
    };

    match kind {
        NameRefKind::LocalRef { enclosing_fn } => {
            if let Some(fn_def) = enclosing_fn {
                let scopes = FnScopes::new(fn_def);
                complete_fn(name_ref, &scopes, acc);
                complete_expr_keywords(&file, fn_def, name_ref, acc);
                complete_expr_snippets(acc);
            }

            let module_scope = db.module_scope(source_root_id, module_id)?;
            acc.extend(
                module_scope
                    .entries()
                    .iter()
                    .filter(|entry| {
                        // Don't expose this item
                        !entry.ptr().range().is_subrange(&name_ref.syntax().range())
                    })
                    .map(|entry| CompletionItem {
                        label: entry.name().to_string(),
                        lookup: None,
                        snippet: None,
                    }),
            );
        }
        NameRefKind::CratePath(path) => {
            complete_path(acc, db, source_root_id, module_tree, module_id, path)?
        }
        NameRefKind::BareIdentInMod => {
            let name_range = name_ref.syntax().range();
            let top_node = name_ref
                .syntax()
                .ancestors()
                .take_while(|it| it.range() == name_range)
                .last()
                .unwrap();
            match top_node.parent().map(|it| it.kind()) {
                Some(SOURCE_FILE) | Some(ITEM_LIST) => complete_mod_item_snippets(acc),
                _ => (),
            }
        }
    }
    Ok(())
}

enum NameRefKind<'a> {
    /// NameRef is a part of single-segment path, for example, a refernece to a
    /// local variable.
    LocalRef {
        enclosing_fn: Option<ast::FnDef<'a>>,
    },
    /// NameRef is the last segment in crate:: path
    CratePath(Vec<ast::NameRef<'a>>),
    /// NameRef is bare identifier at the module's root.
    /// Used for keyword completion
    BareIdentInMod,
}

fn classify_name_ref(name_ref: ast::NameRef) -> Option<NameRefKind> {
    let name_range = name_ref.syntax().range();
    let top_node = name_ref
        .syntax()
        .ancestors()
        .take_while(|it| it.range() == name_range)
        .last()
        .unwrap();
    match top_node.parent().map(|it| it.kind()) {
        Some(SOURCE_FILE) | Some(ITEM_LIST) => return Some(NameRefKind::BareIdentInMod),
        _ => (),
    }

    let parent = name_ref.syntax().parent()?;
    if let Some(segment) = ast::PathSegment::cast(parent) {
        let path = segment.parent_path();
        if path.qualifier().is_none() {
            let enclosing_fn = name_ref
                .syntax()
                .ancestors()
                .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
                .find_map(ast::FnDef::cast);
            return Some(NameRefKind::LocalRef { enclosing_fn });
        }
        if let Some(crate_path) = crate_path(path) {
            return Some(NameRefKind::CratePath(crate_path));
        }
    }
    None
}

fn crate_path(mut path: ast::Path) -> Option<Vec<ast::NameRef>> {
    let mut res = Vec::new();
    loop {
        let segment = path.segment()?;
        match segment.kind()? {
            ast::PathSegmentKind::Name(name) => res.push(name),
            ast::PathSegmentKind::CrateKw => break,
            ast::PathSegmentKind::SelfKw | ast::PathSegmentKind::SuperKw => return None,
        }
        path = path.qualifier()?;
    }
    res.reverse();
    Some(res)
}

fn complete_fn(name_ref: ast::NameRef, scopes: &FnScopes, acc: &mut Vec<CompletionItem>) {
    let mut shadowed = FxHashSet::default();
    acc.extend(
        scopes
            .scope_chain(name_ref.syntax())
            .flat_map(|scope| scopes.entries(scope).iter())
            .filter(|entry| shadowed.insert(entry.name()))
            .map(|entry| CompletionItem {
                label: entry.name().to_string(),
                lookup: None,
                snippet: None,
            }),
    );
    if scopes.self_param.is_some() {
        acc.push(CompletionItem {
            label: "self".to_string(),
            lookup: None,
            snippet: None,
        })
    }
}

fn complete_path(
    acc: &mut Vec<CompletionItem>,
    db: &RootDatabase,
    source_root_id: SourceRootId,
    module_tree: &ModuleTree,
    module_id: ModuleId,
    crate_path: Vec<ast::NameRef>,
) -> Cancelable<()> {
    let target_module_id = match find_target_module(module_tree, module_id, crate_path) {
        None => return Ok(()),
        Some(it) => it,
    };
    let module_scope = db.module_scope(source_root_id, target_module_id)?;
    let completions = module_scope.entries().iter().map(|entry| CompletionItem {
        label: entry.name().to_string(),
        lookup: None,
        snippet: None,
    });
    acc.extend(completions);
    Ok(())
}

fn find_target_module(
    module_tree: &ModuleTree,
    module_id: ModuleId,
    mut crate_path: Vec<ast::NameRef>,
) -> Option<ModuleId> {
    crate_path.pop();
    let mut target_module = module_id.root(&module_tree);
    for name in crate_path {
        target_module = target_module.child(module_tree, name.text().as_str())?;
    }
    Some(target_module)
}

fn complete_mod_item_snippets(acc: &mut Vec<CompletionItem>) {
    acc.push(CompletionItem {
        label: "tfn".to_string(),
        lookup: None,
        snippet: Some("#[test]\nfn $1() {\n    $0\n}".to_string()),
    });
    acc.push(CompletionItem {
        label: "pub(crate)".to_string(),
        lookup: None,
        snippet: Some("pub(crate) $0".to_string()),
    })
}

fn complete_expr_keywords(
    file: &SourceFileNode,
    fn_def: ast::FnDef,
    name_ref: ast::NameRef,
    acc: &mut Vec<CompletionItem>,
) {
    acc.push(keyword("if", "if $0 {}"));
    acc.push(keyword("match", "match $0 {}"));
    acc.push(keyword("while", "while $0 {}"));
    acc.push(keyword("loop", "loop {$0}"));

    if let Some(off) = name_ref.syntax().range().start().checked_sub(2.into()) {
        if let Some(if_expr) = find_node_at_offset::<ast::IfExpr>(file.syntax(), off) {
            if if_expr.syntax().range().end() < name_ref.syntax().range().start() {
                acc.push(keyword("else", "else {$0}"));
                acc.push(keyword("else if", "else if $0 {}"));
            }
        }
    }
    if is_in_loop_body(name_ref) {
        acc.push(keyword("continue", "continue"));
        acc.push(keyword("break", "break"));
    }
    acc.extend(complete_return(fn_def, name_ref));
}

fn is_in_loop_body(name_ref: ast::NameRef) -> bool {
    for node in name_ref.syntax().ancestors() {
        if node.kind() == FN_DEF || node.kind() == LAMBDA_EXPR {
            break;
        }
        let loop_body = visitor()
            .visit::<ast::ForExpr, _>(LoopBodyOwner::loop_body)
            .visit::<ast::WhileExpr, _>(LoopBodyOwner::loop_body)
            .visit::<ast::LoopExpr, _>(LoopBodyOwner::loop_body)
            .accept(node);
        if let Some(Some(body)) = loop_body {
            if name_ref
                .syntax()
                .range()
                .is_subrange(&body.syntax().range())
            {
                return true;
            }
        }
    }
    false
}

fn complete_return(fn_def: ast::FnDef, name_ref: ast::NameRef) -> Option<CompletionItem> {
    // let is_last_in_block = name_ref.syntax().ancestors().filter_map(ast::Expr::cast)
    //     .next()
    //     .and_then(|it| it.syntax().parent())
    //     .and_then(ast::Block::cast)
    //     .is_some();

    // if is_last_in_block {
    //     return None;
    // }

    let is_stmt = match name_ref
        .syntax()
        .ancestors()
        .filter_map(ast::ExprStmt::cast)
        .next()
    {
        None => false,
        Some(expr_stmt) => expr_stmt.syntax().range() == name_ref.syntax().range(),
    };
    let snip = match (is_stmt, fn_def.ret_type().is_some()) {
        (true, true) => "return $0;",
        (true, false) => "return;",
        (false, true) => "return $0",
        (false, false) => "return",
    };
    Some(keyword("return", snip))
}

fn keyword(kw: &str, snip: &str) -> CompletionItem {
    CompletionItem {
        label: kw.to_string(),
        lookup: None,
        snippet: Some(snip.to_string()),
    }
}

fn complete_expr_snippets(acc: &mut Vec<CompletionItem>) {
    acc.push(CompletionItem {
        label: "pd".to_string(),
        lookup: None,
        snippet: Some("eprintln!(\"$0 = {:?}\", $0);".to_string()),
    });
    acc.push(CompletionItem {
        label: "ppd".to_string(),
        lookup: None,
        snippet: Some("eprintln!(\"$0 = {:#?}\", $0);".to_string()),
    });
}
<|endoftext|>