<commit_before>// Copyright 2021 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

//! Provides parts of crosvm as a library to communicate with running crosvm instances.
//!
//! This crate is a programmatic alternative to invoking crosvm with subcommands that produce the
//! result on stdout.
//!
//! Downstream projects rely on this library maintaining a stable API surface.
//! Do not make changes to this library without consulting the crosvm externalization team.
//! Email: crosvm-dev@chromium.org
//! For more information see:
//! <https://google.github.io/crosvm/running_crosvm/programmatic_interaction.html#usage>

use std::convert::{TryFrom, TryInto};
use std::ffi::CStr;
use std::panic::catch_unwind;
use std::path::{Path, PathBuf};

use libc::{c_char, ssize_t};

use vm_control::{
    client::*, BalloonControlCommand, BalloonStats, DiskControlCommand, UsbControlAttachedDevice,
    UsbControlResult, VmRequest, VmResponse,
};

fn validate_socket_path(socket_path: *const c_char) -> Option<PathBuf> {
    if !socket_path.is_null() {
        let socket_path = unsafe { CStr::from_ptr(socket_path) };
        Some(PathBuf::from(socket_path.to_str().ok()?))
    } else {
        None
    }
}

/// Stops the crosvm instance whose control socket is listening on `socket_path`.
///
/// The function returns true on success or false if an error occured.
#[no_mangle]
pub extern "C" fn crosvm_client_stop_vm(socket_path: *const c_char) -> bool {
    catch_unwind(|| {
        if let Some(socket_path) = validate_socket_path(socket_path) {
            vms_request(&VmRequest::Exit, &socket_path).is_ok()
        } else {
            false
        }
    })
    .unwrap_or(false)
}

/// Suspends the crosvm instance whose control socket is listening on `socket_path`.
///
/// The function returns true on success or false if an error occured.
#[no_mangle]
pub extern "C" fn crosvm_client_suspend_vm(socket_path: *const c_char) -> bool {
    catch_unwind(|| {
        if let Some(socket_path) = validate_socket_path(socket_path) {
            vms_request(&VmRequest::Suspend, &socket_path).is_ok()
        } else {
            false
        }
    })
    .unwrap_or(false)
}

/// Resumes the crosvm instance whose control socket is listening on `socket_path`.
///
/// The function returns true on success or false if an error occured.
#[no_mangle]
pub extern "C" fn crosvm_client_resume_vm(socket_path: *const c_char) -> bool {
    catch_unwind(|| {
        if let Some(socket_path) = validate_socket_path(socket_path) {
            vms_request(&VmRequest::Resume, &socket_path).is_ok()
        } else {
            false
        }
    })
    .unwrap_or(false)
}

/// Creates an RT vCPU for the crosvm instance whose control socket is listening on `socket_path`.
///
/// The function returns true on success or false if an error occured.
#[no_mangle]
pub extern "C" fn crosvm_client_make_rt_vm(socket_path: *const c_char) -> bool {
    catch_unwind(|| {
        if let Some(socket_path) = validate_socket_path(socket_path) {
            vms_request(&VmRequest::MakeRT, &socket_path).is_ok()
        } else {
            false
        }
    })
    .unwrap_or(false)
}

/// Adjusts the balloon size of the crosvm instance whose control socket is
/// listening on `socket_path`.
///
/// The function returns true on success or false if an error occured.
#[no_mangle]
pub extern "C" fn crosvm_client_balloon_vms(socket_path: *const c_char, num_bytes: u64) -> bool {
    catch_unwind(|| {
        if let Some(socket_path) = validate_socket_path(socket_path) {
            let command = BalloonControlCommand::Adjust { num_bytes };
            vms_request(&VmRequest::BalloonCommand(command), &socket_path).is_ok()
        } else {
            false
        }
    })
    .unwrap_or(false)
}

/// Represents an individual attached USB device.
#[repr(C)]
pub struct UsbDeviceEntry {
    /// Internal port index used for identifying this individual device.
    port: u8,
    /// USB vendor ID
    vendor_id: u16,
    /// USB product ID
    product_id: u16,
}

impl From<&UsbControlAttachedDevice> for UsbDeviceEntry {
    fn from(other: &UsbControlAttachedDevice) -> Self {
        Self {
            port: other.port,
            vendor_id: other.vendor_id,
            product_id: other.product_id,
        }
    }
}

/// Returns all USB devices passed through the crosvm instance whose control socket is listening on `socket_path`.
///
/// The function returns the amount of entries written.
/// # Arguments
///
/// * `socket_path` - Path to the crosvm control socket
/// * `entries` - Pointer to an array of `UsbDeviceEntry` where the details about the attached
///               devices will be written to
/// * `entries_length` - Amount of entries in the array specified by `entries`
///
/// Crosvm supports passing through up to 255 devices, so pasing an array with 255 entries will
/// guarantee to return all entries.
#[no_mangle]
pub extern "C" fn crosvm_client_usb_list(
    socket_path: *const c_char,
    entries: *mut UsbDeviceEntry,
    entries_length: ssize_t,
) -> ssize_t {
    catch_unwind(|| {
        if let Some(socket_path) = validate_socket_path(socket_path) {
            if let Ok(UsbControlResult::Devices(res)) = do_usb_list(&socket_path) {
                let mut i = 0;
                for entry in res.iter().filter(|x| x.valid()) {
                    if i >= entries_length {
                        break;
                    }
                    unsafe {
                        *entries.offset(i) = entry.into();
                        i += 1;
                    }
                }
                i
            } else {
                -1
            }
        } else {
            -1
        }
    })
    .unwrap_or(-1)
}

/// Attaches an USB device to crosvm instance whose control socket is listening on `socket_path`.
///
/// The function returns the amount of entries written.
/// # Arguments
///
/// * `socket_path` - Path to the crosvm control socket
/// * `bus` - USB device bus ID (unused)
/// * `addr` - USB device address (unused)
/// * `vid` - USB device vendor ID (unused)
/// * `pid` - USB device product ID (unused)
/// * `dev_path` - Path to the USB device (Most likely `/dev/bus/usb/<bus>/<addr>`).
/// * `out_port` - (optional) internal port will be written here if provided.
///
/// The function returns true on success or false if an error occured.
#[no_mangle]
pub extern "C" fn crosvm_client_usb_attach(
    socket_path: *const c_char,
    _bus: u8,
    _addr: u8,
    _vid: u16,
    _pid: u16,
    dev_path: *const c_char,
    out_port: *mut u8,
) -> bool {
    catch_unwind(|| {
        if let Some(socket_path) = validate_socket_path(socket_path) {
            if dev_path.is_null() {
                return false;
            }
            let dev_path = Path::new(unsafe { CStr::from_ptr(dev_path) }.to_str().unwrap_or(""));

            if let Ok(UsbControlResult::Ok { port }) = do_usb_attach(&socket_path, dev_path) {
                if !out_port.is_null() {
                    unsafe { *out_port = port };
                }
                true
            } else {
                false
            }
        } else {
            false
        }
    })
    .unwrap_or(false)
}

/// Detaches an USB device from crosvm instance whose control socket is listening on `socket_path`.
/// `port` determines device to be detached.
///
/// The function returns true on success or false if an error occured.
#[no_mangle]
pub extern "C" fn crosvm_client_usb_detach(socket_path: *const c_char, port: u8) -> bool {
    catch_unwind(|| {
        if let Some(socket_path) = validate_socket_path(socket_path) {
            do_usb_detach(&socket_path, port).is_ok()
        } else {
            false
        }
    })
    .unwrap_or(false)
}

/// Modifies the battery status of crosvm instance whose control socket is listening on
/// `socket_path`.
///
/// The function returns true on success or false if an error occured.
#[no_mangle]
pub extern "C" fn crosvm_client_modify_battery(
    socket_path: *const c_char,
    battery_type: *const c_char,
    property: *const c_char,
    target: *const c_char,
) -> bool {
    catch_unwind(|| {
        if let Some(socket_path) = validate_socket_path(socket_path) {
            if battery_type.is_null() || property.is_null() || target.is_null() {
                return false;
            }
            let battery_type = unsafe { CStr::from_ptr(battery_type) };
            let property = unsafe { CStr::from_ptr(property) };
            let target = unsafe { CStr::from_ptr(target) };

            do_modify_battery(
                &socket_path,
                battery_type.to_str().unwrap(),
                property.to_str().unwrap(),
                target.to_str().unwrap(),
            )
            .is_ok()
        } else {
            false
        }
    })
    .unwrap_or(false)
}

/// Resizes the disk of the crosvm instance whose control socket is listening on `socket_path`.
///
/// The function returns true on success or false if an error occured.
#[no_mangle]
pub extern "C" fn crosvm_client_resize_disk(
    socket_path: *const c_char,
    disk_index: u64,
    new_size: u64,
) -> bool {
    catch_unwind(|| {
        if let Some(socket_path) = validate_socket_path(socket_path) {
            if let Ok(disk_index) = usize::try_from(disk_index) {
                let request = VmRequest::DiskCommand {
                    disk_index,
                    command: DiskControlCommand::Resize { new_size },
                };
                vms_request(&request, &socket_path).is_ok()
            } else {
                false
            }
        } else {
            false
        }
    })
    .unwrap_or(false)
}

/// Similar to internally used `BalloonStats` but using i64 instead of
/// Option<u64>. `None` (or values bigger than i64::max) will be encoded as -1.
#[repr(C)]
pub struct BalloonStatsFfi {
    swap_in: i64,
    swap_out: i64,
    major_faults: i64,
    minor_faults: i64,
    free_memory: i64,
    total_memory: i64,
    available_memory: i64,
    disk_caches: i64,
    hugetlb_allocations: i64,
    hugetlb_failures: i64,
    shared_memory: i64,
    unevictable_memory: i64,
}

impl From<&BalloonStats> for BalloonStatsFfi {
    fn from(other: &BalloonStats) -> Self {
        let convert = |x: Option<u64>| -> i64 { x.and_then(|y| y.try_into().ok()).unwrap_or(-1) };
        Self {
            swap_in: convert(other.swap_in),
            swap_out: convert(other.swap_out),
            major_faults: convert(other.major_faults),
            minor_faults: convert(other.minor_faults),
            free_memory: convert(other.free_memory),
            total_memory: convert(other.total_memory),
            available_memory: convert(other.available_memory),
            disk_caches: convert(other.disk_caches),
            hugetlb_allocations: convert(other.hugetlb_allocations),
            hugetlb_failures: convert(other.hugetlb_failures),
            shared_memory: convert(other.shared_memory),
            unevictable_memory: convert(other.unevictable_memory),
        }
    }
}

/// Returns balloon stats of the crosvm instance whose control socket is listening on `socket_path`.
///
/// The parameters `stats` and `actual` are optional and will only be written to if they are
/// non-null.
///
/// The function returns true on success or false if an error occured.
///
/// # Note
///
/// Entries in `BalloonStatsFfi` that are not available will be set to `-1`.
#[no_mangle]
pub extern "C" fn crosvm_client_balloon_stats(
    socket_path: *const c_char,
    stats: *mut BalloonStatsFfi,
    actual: *mut u64,
) -> bool {
    catch_unwind(|| {
        if let Some(socket_path) = validate_socket_path(socket_path) {
            let request = &VmRequest::BalloonCommand(BalloonControlCommand::Stats {});
            if let Ok(VmResponse::BalloonStats {
                stats: ref balloon_stats,
                balloon_actual,
            }) = handle_request(request, &socket_path)
            {
                if !stats.is_null() {
                    unsafe {
                        *stats = balloon_stats.into();
                    }
                }

                if !actual.is_null() {
                    unsafe {
                        *actual = balloon_actual;
                    }
                }
                true
            } else {
                false
            }
        } else {
            false
        }
    })
    .unwrap_or(false)
}
<commit_msg>crosvm_control: Expose USB_CONTROL_MAX_PORTS<commit_after>// Copyright 2021 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

//! Provides parts of crosvm as a library to communicate with running crosvm instances.
//!
//! This crate is a programmatic alternative to invoking crosvm with subcommands that produce the
//! result on stdout.
//!
//! Downstream projects rely on this library maintaining a stable API surface.
//! Do not make changes to this library without consulting the crosvm externalization team.
//! Email: crosvm-dev@chromium.org
//! For more information see:
//! <https://google.github.io/crosvm/running_crosvm/programmatic_interaction.html#usage>

use std::convert::{TryFrom, TryInto};
use std::ffi::CStr;
use std::panic::catch_unwind;
use std::path::{Path, PathBuf};

use libc::{c_char, ssize_t};

use vm_control::{
    client::*, BalloonControlCommand, BalloonStats, DiskControlCommand, UsbControlAttachedDevice,
    UsbControlResult, VmRequest, VmResponse, USB_CONTROL_MAX_PORTS,
};

fn validate_socket_path(socket_path: *const c_char) -> Option<PathBuf> {
    if !socket_path.is_null() {
        let socket_path = unsafe { CStr::from_ptr(socket_path) };
        Some(PathBuf::from(socket_path.to_str().ok()?))
    } else {
        None
    }
}

/// Stops the crosvm instance whose control socket is listening on `socket_path`.
///
/// The function returns true on success or false if an error occured.
#[no_mangle]
pub extern "C" fn crosvm_client_stop_vm(socket_path: *const c_char) -> bool {
    catch_unwind(|| {
        if let Some(socket_path) = validate_socket_path(socket_path) {
            vms_request(&VmRequest::Exit, &socket_path).is_ok()
        } else {
            false
        }
    })
    .unwrap_or(false)
}

/// Suspends the crosvm instance whose control socket is listening on `socket_path`.
///
/// The function returns true on success or false if an error occured.
#[no_mangle]
pub extern "C" fn crosvm_client_suspend_vm(socket_path: *const c_char) -> bool {
    catch_unwind(|| {
        if let Some(socket_path) = validate_socket_path(socket_path) {
            vms_request(&VmRequest::Suspend, &socket_path).is_ok()
        } else {
            false
        }
    })
    .unwrap_or(false)
}

/// Resumes the crosvm instance whose control socket is listening on `socket_path`.
///
/// The function returns true on success or false if an error occured.
#[no_mangle]
pub extern "C" fn crosvm_client_resume_vm(socket_path: *const c_char) -> bool {
    catch_unwind(|| {
        if let Some(socket_path) = validate_socket_path(socket_path) {
            vms_request(&VmRequest::Resume, &socket_path).is_ok()
        } else {
            false
        }
    })
    .unwrap_or(false)
}

/// Creates an RT vCPU for the crosvm instance whose control socket is listening on `socket_path`.
///
/// The function returns true on success or false if an error occured.
#[no_mangle]
pub extern "C" fn crosvm_client_make_rt_vm(socket_path: *const c_char) -> bool {
    catch_unwind(|| {
        if let Some(socket_path) = validate_socket_path(socket_path) {
            vms_request(&VmRequest::MakeRT, &socket_path).is_ok()
        } else {
            false
        }
    })
    .unwrap_or(false)
}

/// Adjusts the balloon size of the crosvm instance whose control socket is
/// listening on `socket_path`.
///
/// The function returns true on success or false if an error occured.
#[no_mangle]
pub extern "C" fn crosvm_client_balloon_vms(socket_path: *const c_char, num_bytes: u64) -> bool {
    catch_unwind(|| {
        if let Some(socket_path) = validate_socket_path(socket_path) {
            let command = BalloonControlCommand::Adjust { num_bytes };
            vms_request(&VmRequest::BalloonCommand(command), &socket_path).is_ok()
        } else {
            false
        }
    })
    .unwrap_or(false)
}

/// Represents an individual attached USB device.
#[repr(C)]
pub struct UsbDeviceEntry {
    /// Internal port index used for identifying this individual device.
    port: u8,
    /// USB vendor ID
    vendor_id: u16,
    /// USB product ID
    product_id: u16,
}

impl From<&UsbControlAttachedDevice> for UsbDeviceEntry {
    fn from(other: &UsbControlAttachedDevice) -> Self {
        Self {
            port: other.port,
            vendor_id: other.vendor_id,
            product_id: other.product_id,
        }
    }
}

/// Simply returns the maximum possible number of USB devices
#[no_mangle]
pub extern "C" fn crosvm_client_max_usb_devices() -> usize {
    USB_CONTROL_MAX_PORTS
}

/// Returns all USB devices passed through the crosvm instance whose control socket is listening on `socket_path`.
///
/// The function returns the amount of entries written.
/// # Arguments
///
/// * `socket_path` - Path to the crosvm control socket
/// * `entries` - Pointer to an array of `UsbDeviceEntry` where the details about the attached
///               devices will be written to
/// * `entries_length` - Amount of entries in the array specified by `entries`
///
/// Use the value returned by crosvm_client_max_usb_devices() to determine the size of the input
/// array to this function.
#[no_mangle]
pub extern "C" fn crosvm_client_usb_list(
    socket_path: *const c_char,
    entries: *mut UsbDeviceEntry,
    entries_length: ssize_t,
) -> ssize_t {
    catch_unwind(|| {
        if let Some(socket_path) = validate_socket_path(socket_path) {
            if let Ok(UsbControlResult::Devices(res)) = do_usb_list(&socket_path) {
                let mut i = 0;
                for entry in res.iter().filter(|x| x.valid()) {
                    if i >= entries_length {
                        break;
                    }
                    unsafe {
                        *entries.offset(i) = entry.into();
                        i += 1;
                    }
                }
                i
            } else {
                -1
            }
        } else {
            -1
        }
    })
    .unwrap_or(-1)
}

/// Attaches an USB device to crosvm instance whose control socket is listening on `socket_path`.
///
/// The function returns the amount of entries written.
/// # Arguments
///
/// * `socket_path` - Path to the crosvm control socket
/// * `bus` - USB device bus ID (unused)
/// * `addr` - USB device address (unused)
/// * `vid` - USB device vendor ID (unused)
/// * `pid` - USB device product ID (unused)
/// * `dev_path` - Path to the USB device (Most likely `/dev/bus/usb/<bus>/<addr>`).
/// * `out_port` - (optional) internal port will be written here if provided.
///
/// The function returns true on success or false if an error occured.
#[no_mangle]
pub extern "C" fn crosvm_client_usb_attach(
    socket_path: *const c_char,
    _bus: u8,
    _addr: u8,
    _vid: u16,
    _pid: u16,
    dev_path: *const c_char,
    out_port: *mut u8,
) -> bool {
    catch_unwind(|| {
        if let Some(socket_path) = validate_socket_path(socket_path) {
            if dev_path.is_null() {
                return false;
            }
            let dev_path = Path::new(unsafe { CStr::from_ptr(dev_path) }.to_str().unwrap_or(""));

            if let Ok(UsbControlResult::Ok { port }) = do_usb_attach(&socket_path, dev_path) {
                if !out_port.is_null() {
                    unsafe { *out_port = port };
                }
                true
            } else {
                false
            }
        } else {
            false
        }
    })
    .unwrap_or(false)
}

/// Detaches an USB device from crosvm instance whose control socket is listening on `socket_path`.
/// `port` determines device to be detached.
///
/// The function returns true on success or false if an error occured.
#[no_mangle]
pub extern "C" fn crosvm_client_usb_detach(socket_path: *const c_char, port: u8) -> bool {
    catch_unwind(|| {
        if let Some(socket_path) = validate_socket_path(socket_path) {
            do_usb_detach(&socket_path, port).is_ok()
        } else {
            false
        }
    })
    .unwrap_or(false)
}

/// Modifies the battery status of crosvm instance whose control socket is listening on
/// `socket_path`.
///
/// The function returns true on success or false if an error occured.
#[no_mangle]
pub extern "C" fn crosvm_client_modify_battery(
    socket_path: *const c_char,
    battery_type: *const c_char,
    property: *const c_char,
    target: *const c_char,
) -> bool {
    catch_unwind(|| {
        if let Some(socket_path) = validate_socket_path(socket_path) {
            if battery_type.is_null() || property.is_null() || target.is_null() {
                return false;
            }
            let battery_type = unsafe { CStr::from_ptr(battery_type) };
            let property = unsafe { CStr::from_ptr(property) };
            let target = unsafe { CStr::from_ptr(target) };

            do_modify_battery(
                &socket_path,
                battery_type.to_str().unwrap(),
                property.to_str().unwrap(),
                target.to_str().unwrap(),
            )
            .is_ok()
        } else {
            false
        }
    })
    .unwrap_or(false)
}

/// Resizes the disk of the crosvm instance whose control socket is listening on `socket_path`.
///
/// The function returns true on success or false if an error occured.
#[no_mangle]
pub extern "C" fn crosvm_client_resize_disk(
    socket_path: *const c_char,
    disk_index: u64,
    new_size: u64,
) -> bool {
    catch_unwind(|| {
        if let Some(socket_path) = validate_socket_path(socket_path) {
            if let Ok(disk_index) = usize::try_from(disk_index) {
                let request = VmRequest::DiskCommand {
                    disk_index,
                    command: DiskControlCommand::Resize { new_size },
                };
                vms_request(&request, &socket_path).is_ok()
            } else {
                false
            }
        } else {
            false
        }
    })
    .unwrap_or(false)
}

/// Similar to internally used `BalloonStats` but using i64 instead of
/// Option<u64>. `None` (or values bigger than i64::max) will be encoded as -1.
#[repr(C)]
pub struct BalloonStatsFfi {
    swap_in: i64,
    swap_out: i64,
    major_faults: i64,
    minor_faults: i64,
    free_memory: i64,
    total_memory: i64,
    available_memory: i64,
    disk_caches: i64,
    hugetlb_allocations: i64,
    hugetlb_failures: i64,
    shared_memory: i64,
    unevictable_memory: i64,
}

impl From<&BalloonStats> for BalloonStatsFfi {
    fn from(other: &BalloonStats) -> Self {
        let convert = |x: Option<u64>| -> i64 { x.and_then(|y| y.try_into().ok()).unwrap_or(-1) };
        Self {
            swap_in: convert(other.swap_in),
            swap_out: convert(other.swap_out),
            major_faults: convert(other.major_faults),
            minor_faults: convert(other.minor_faults),
            free_memory: convert(other.free_memory),
            total_memory: convert(other.total_memory),
            available_memory: convert(other.available_memory),
            disk_caches: convert(other.disk_caches),
            hugetlb_allocations: convert(other.hugetlb_allocations),
            hugetlb_failures: convert(other.hugetlb_failures),
            shared_memory: convert(other.shared_memory),
            unevictable_memory: convert(other.unevictable_memory),
        }
    }
}

/// Returns balloon stats of the crosvm instance whose control socket is listening on `socket_path`.
///
/// The parameters `stats` and `actual` are optional and will only be written to if they are
/// non-null.
///
/// The function returns true on success or false if an error occured.
///
/// # Note
///
/// Entries in `BalloonStatsFfi` that are not available will be set to `-1`.
#[no_mangle]
pub extern "C" fn crosvm_client_balloon_stats(
    socket_path: *const c_char,
    stats: *mut BalloonStatsFfi,
    actual: *mut u64,
) -> bool {
    catch_unwind(|| {
        if let Some(socket_path) = validate_socket_path(socket_path) {
            let request = &VmRequest::BalloonCommand(BalloonControlCommand::Stats {});
            if let Ok(VmResponse::BalloonStats {
                stats: ref balloon_stats,
                balloon_actual,
            }) = handle_request(request, &socket_path)
            {
                if !stats.is_null() {
                    unsafe {
                        *stats = balloon_stats.into();
                    }
                }

                if !actual.is_null() {
                    unsafe {
                        *actual = balloon_actual;
                    }
                }
                true
            } else {
                false
            }
        } else {
            false
        }
    })
    .unwrap_or(false)
}
<|endoftext|>


<commit_before>use crate::intrinsics;
use crate::iter::{
    DoubleEndedIterator, ExactSizeIterator, FusedIterator, Iterator, TrustedRandomAccess,
};
use crate::ops::Try;

/// An iterator that yields `None` forever after the underlying iterator
/// yields `None` once.
///
/// This `struct` is created by the [`fuse`] method on [`Iterator`]. See its
/// documentation for more.
///
/// [`fuse`]: trait.Iterator.html#method.fuse
/// [`Iterator`]: trait.Iterator.html
#[derive(Clone, Debug)]
#[must_use = "iterators are lazy and do nothing unless consumed"]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct Fuse<I> {
    // NOTE: for `I: FusedIterator`, this is always assumed `Some`!
    iter: Option<I>,
}
impl<I> Fuse<I> {
    pub(in crate::iter) fn new(iter: I) -> Fuse<I> {
        Fuse { iter: Some(iter) }
    }
}

#[stable(feature = "fused", since = "1.26.0")]
impl<I> FusedIterator for Fuse<I> where I: Iterator {}

/// Fuse the iterator if the expression is `None`.
macro_rules! fuse {
    ($self:ident . iter . $($call:tt)+) => {
        match $self.iter {
            Some(ref mut iter) => match iter.$($call)+ {
                None => {
                    $self.iter = None;
                    None
                }
                item => item,
            },
            None => None,
        }
    };
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<I> Iterator for Fuse<I>
where
    I: Iterator,
{
    type Item = <I as Iterator>::Item;

    #[inline]
    default fn next(&mut self) -> Option<<I as Iterator>::Item> {
        fuse!(self.iter.next())
    }

    #[inline]
    default fn nth(&mut self, n: usize) -> Option<I::Item> {
        fuse!(self.iter.nth(n))
    }

    #[inline]
    default fn last(self) -> Option<I::Item> {
        match self.iter {
            Some(iter) => iter.last(),
            None => None,
        }
    }

    #[inline]
    default fn count(self) -> usize {
        match self.iter {
            Some(iter) => iter.count(),
            None => 0,
        }
    }

    #[inline]
    default fn size_hint(&self) -> (usize, Option<usize>) {
        match self.iter {
            Some(ref iter) => iter.size_hint(),
            None => (0, Some(0)),
        }
    }

    #[inline]
    default fn try_fold<Acc, Fold, R>(&mut self, mut acc: Acc, fold: Fold) -> R
    where
        Self: Sized,
        Fold: FnMut(Acc, Self::Item) -> R,
        R: Try<Ok = Acc>,
    {
        if let Some(ref mut iter) = self.iter {
            acc = iter.try_fold(acc, fold)?;
            self.iter = None;
        }
        Try::from_ok(acc)
    }

    #[inline]
    default fn fold<Acc, Fold>(self, mut acc: Acc, fold: Fold) -> Acc
    where
        Fold: FnMut(Acc, Self::Item) -> Acc,
    {
        if let Some(iter) = self.iter {
            acc = iter.fold(acc, fold);
        }
        acc
    }

    #[inline]
    default fn find<P>(&mut self, predicate: P) -> Option<Self::Item>
    where
        P: FnMut(&Self::Item) -> bool,
    {
        fuse!(self.iter.find(predicate))
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<I> DoubleEndedIterator for Fuse<I>
where
    I: DoubleEndedIterator,
{
    #[inline]
    default fn next_back(&mut self) -> Option<<I as Iterator>::Item> {
        fuse!(self.iter.next_back())
    }

    #[inline]
    default fn nth_back(&mut self, n: usize) -> Option<<I as Iterator>::Item> {
        fuse!(self.iter.nth_back(n))
    }

    #[inline]
    default fn try_rfold<Acc, Fold, R>(&mut self, mut acc: Acc, fold: Fold) -> R
    where
        Self: Sized,
        Fold: FnMut(Acc, Self::Item) -> R,
        R: Try<Ok = Acc>,
    {
        if let Some(ref mut iter) = self.iter {
            acc = iter.try_rfold(acc, fold)?;
            self.iter = None;
        }
        Try::from_ok(acc)
    }

    #[inline]
    default fn rfold<Acc, Fold>(self, mut acc: Acc, fold: Fold) -> Acc
    where
        Fold: FnMut(Acc, Self::Item) -> Acc,
    {
        if let Some(iter) = self.iter {
            acc = iter.rfold(acc, fold);
        }
        acc
    }

    #[inline]
    default fn rfind<P>(&mut self, predicate: P) -> Option<Self::Item>
    where
        P: FnMut(&Self::Item) -> bool,
    {
        fuse!(self.iter.rfind(predicate))
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<I> ExactSizeIterator for Fuse<I>
where
    I: ExactSizeIterator,
{
    default fn len(&self) -> usize {
        match self.iter {
            Some(ref iter) => iter.len(),
            None => 0,
        }
    }

    default fn is_empty(&self) -> bool {
        match self.iter {
            Some(ref iter) => iter.is_empty(),
            None => true,
        }
    }
}

// NOTE: for `I: FusedIterator`, we assume that the iterator is always `Some`.
// Implementing this as a directly-expanded macro helps codegen performance.
macro_rules! unchecked {
    ($self:ident) => {
        match $self {
            Fuse { iter: Some(iter) } => iter,
            // SAFETY: the specialized iterator never sets `None`
            Fuse { iter: None } => unsafe { intrinsics::unreachable() },
        }
    };
}

#[stable(feature = "fused", since = "1.26.0")]
impl<I> Iterator for Fuse<I>
where
    I: FusedIterator,
{
    #[inline]
    fn next(&mut self) -> Option<<I as Iterator>::Item> {
        unchecked!(self).next()
    }

    #[inline]
    fn nth(&mut self, n: usize) -> Option<I::Item> {
        unchecked!(self).nth(n)
    }

    #[inline]
    fn last(self) -> Option<I::Item> {
        unchecked!(self).last()
    }

    #[inline]
    fn count(self) -> usize {
        unchecked!(self).count()
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        unchecked!(self).size_hint()
    }

    #[inline]
    fn try_fold<Acc, Fold, R>(&mut self, init: Acc, fold: Fold) -> R
    where
        Self: Sized,
        Fold: FnMut(Acc, Self::Item) -> R,
        R: Try<Ok = Acc>,
    {
        unchecked!(self).try_fold(init, fold)
    }

    #[inline]
    fn fold<Acc, Fold>(self, init: Acc, fold: Fold) -> Acc
    where
        Fold: FnMut(Acc, Self::Item) -> Acc,
    {
        unchecked!(self).fold(init, fold)
    }

    #[inline]
    fn find<P>(&mut self, predicate: P) -> Option<Self::Item>
    where
        P: FnMut(&Self::Item) -> bool,
    {
        unchecked!(self).find(predicate)
    }
}

#[stable(feature = "fused", since = "1.26.0")]
impl<I> DoubleEndedIterator for Fuse<I>
where
    I: DoubleEndedIterator + FusedIterator,
{
    #[inline]
    fn next_back(&mut self) -> Option<<I as Iterator>::Item> {
        unchecked!(self).next_back()
    }

    #[inline]
    fn nth_back(&mut self, n: usize) -> Option<<I as Iterator>::Item> {
        unchecked!(self).nth_back(n)
    }

    #[inline]
    fn try_rfold<Acc, Fold, R>(&mut self, init: Acc, fold: Fold) -> R
    where
        Self: Sized,
        Fold: FnMut(Acc, Self::Item) -> R,
        R: Try<Ok = Acc>,
    {
        unchecked!(self).try_rfold(init, fold)
    }

    #[inline]
    fn rfold<Acc, Fold>(self, init: Acc, fold: Fold) -> Acc
    where
        Fold: FnMut(Acc, Self::Item) -> Acc,
    {
        unchecked!(self).rfold(init, fold)
    }

    #[inline]
    fn rfind<P>(&mut self, predicate: P) -> Option<Self::Item>
    where
        P: FnMut(&Self::Item) -> bool,
    {
        unchecked!(self).rfind(predicate)
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<I> ExactSizeIterator for Fuse<I>
where
    I: ExactSizeIterator + FusedIterator,
{
    fn len(&self) -> usize {
        unchecked!(self).len()
    }

    fn is_empty(&self) -> bool {
        unchecked!(self).is_empty()
    }
}

unsafe impl<I> TrustedRandomAccess for Fuse<I>
where
    I: TrustedRandomAccess,
{
    unsafe fn get_unchecked(&mut self, i: usize) -> I::Item {
        match self.iter {
            Some(ref mut iter) => iter.get_unchecked(i),
            // SAFETY: the caller asserts there is an item at `i`, so we're not exhausted.
            None => intrinsics::unreachable(),
        }
    }

    fn may_have_side_effect() -> bool {
        I::may_have_side_effect()
    }
}
<commit_msg>Added FuseIteratorImpl, FustDoubleEndedIteratorImpl and FuseExactSizeIteratorImpl to avoid exposing default functions outside of the current crate.<commit_after>use crate::intrinsics;
use crate::iter::{
    DoubleEndedIterator, ExactSizeIterator, FusedIterator, Iterator, TrustedRandomAccess,
};
use crate::ops::Try;

/// An iterator that yields `None` forever after the underlying iterator
/// yields `None` once.
///
/// This `struct` is created by the [`fuse`] method on [`Iterator`]. See its
/// documentation for more.
///
/// [`fuse`]: trait.Iterator.html#method.fuse
/// [`Iterator`]: trait.Iterator.html
#[derive(Clone, Debug)]
#[must_use = "iterators are lazy and do nothing unless consumed"]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct Fuse<I> {
    // NOTE: for `I: FusedIterator`, this is always assumed `Some`!
    iter: Option<I>,
}
impl<I> Fuse<I> {
    pub(in crate::iter) fn new(iter: I) -> Fuse<I> {
        Fuse { iter: Some(iter) }
    }
}

#[stable(feature = "fused", since = "1.26.0")]
impl<I> FusedIterator for Fuse<I> where I: Iterator {}

/// Fuse the iterator if the expression is `None`.
macro_rules! fuse {
    ($self:ident . iter . $($call:tt)+) => {
        match $self.iter {
            Some(ref mut iter) => match iter.$($call)+ {
                None => {
                    $self.iter = None;
                    None
                }
                item => item,
            },
            None => None,
        }
    };
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<I> Iterator for Fuse<I>
where
    I: Iterator,
{
    type Item = <I as Iterator>::Item;

    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        FuseIteratorImpl::next(self)
    }

    #[inline]
    fn nth(&mut self, n: usize) -> Option<I::Item> {
        FuseIteratorImpl::nth(self, n)
    }

    #[inline]
    fn last(self) -> Option<Self::Item> {
        FuseIteratorImpl::last(self)
    }

    #[inline]
    fn count(self) -> usize {
        FuseIteratorImpl::count(self)
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        FuseIteratorImpl::size_hint(self)
    }

    #[inline]
    fn try_fold<Acc, Fold, R>(&mut self, acc: Acc, fold: Fold) -> R
    where
        Self: Sized,
        Fold: FnMut(Acc, Self::Item) -> R,
        R: Try<Ok = Acc>,
    {
        FuseIteratorImpl::try_fold(self, acc, fold)
    }

    #[inline]
    fn fold<Acc, Fold>(self, acc: Acc, fold: Fold) -> Acc
    where
        Fold: FnMut(Acc, Self::Item) -> Acc,
    {
        FuseIteratorImpl::fold(self, acc, fold)
    }

    #[inline]
    fn find<P>(&mut self, predicate: P) -> Option<Self::Item>
    where
        P: FnMut(&Self::Item) -> bool,
    {
        FuseIteratorImpl::find(self, predicate)
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<I> DoubleEndedIterator for Fuse<I>
where
    I: DoubleEndedIterator,
{
    #[inline]
    fn next_back(&mut self) -> Option<<I as Iterator>::Item> {
        FuseDoubleEndedIteratorImpl::next_back(self)
    }

    #[inline]
    fn nth_back(&mut self, n: usize) -> Option<<I as Iterator>::Item> {
        FuseDoubleEndedIteratorImpl::nth_back(self, n)
    }

    #[inline]
    fn try_rfold<Acc, Fold, R>(&mut self, acc: Acc, fold: Fold) -> R
    where
        Self: Sized,
        Fold: FnMut(Acc, Self::Item) -> R,
        R: Try<Ok = Acc>,
    {
        FuseDoubleEndedIteratorImpl::try_rfold(self, acc, fold)
    }

    #[inline]
    fn rfold<Acc, Fold>(self, acc: Acc, fold: Fold) -> Acc
    where
        Fold: FnMut(Acc, Self::Item) -> Acc,
    {
        FuseDoubleEndedIteratorImpl::rfold(self, acc, fold)
    }

    #[inline]
    fn rfind<P>(&mut self, predicate: P) -> Option<Self::Item>
    where
        P: FnMut(&Self::Item) -> bool,
    {
        FuseDoubleEndedIteratorImpl::rfind(self, predicate)
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<I> ExactSizeIterator for Fuse<I>
where
    I: ExactSizeIterator,
{
    fn len(&self) -> usize {
        FuseExactSizeIteratorImpl::len(self)
    }

    fn is_empty(&self) -> bool {
        FuseExactSizeIteratorImpl::is_empty(self)
    }
}

// NOTE: for `I: FusedIterator`, we assume that the iterator is always `Some`.
// Implementing this as a directly-expanded macro helps codegen performance.
macro_rules! unchecked {
    ($self:ident) => {
        match $self {
            Fuse { iter: Some(iter) } => iter,
            // SAFETY: the specialized iterator never sets `None`
            Fuse { iter: None } => unsafe { intrinsics::unreachable() },
        }
    };
}

#[stable(feature = "fused", since = "1.26.0")]
impl<I> Iterator for Fuse<I> where I: FusedIterator {}

#[stable(feature = "fused", since = "1.26.0")]
impl<I> DoubleEndedIterator for Fuse<I> where I: DoubleEndedIterator + FusedIterator {}

#[stable(feature = "rust1", since = "1.0.0")]
impl<I> ExactSizeIterator for Fuse<I> where I: ExactSizeIterator + FusedIterator {}

unsafe impl<I> TrustedRandomAccess for Fuse<I>
where
    I: TrustedRandomAccess,
{
    unsafe fn get_unchecked(&mut self, i: usize) -> I::Item {
        match self.iter {
            Some(ref mut iter) => iter.get_unchecked(i),
            // SAFETY: the caller asserts there is an item at `i`, so we're not exhausted.
            None => intrinsics::unreachable(),
        }
    }

    fn may_have_side_effect() -> bool {
        I::may_have_side_effect()
    }
}

// Fuse specialization trait
#[doc(hidden)]
trait FuseIteratorImpl<I> {
    type Item;
    fn next(&mut self) -> Option<Self::Item>;
    fn nth(&mut self, n: usize) -> Option<Self::Item>;
    fn last(self) -> Option<Self::Item>;
    fn count(self) -> usize;
    fn size_hint(&self) -> (usize, Option<usize>);
    fn try_fold<Acc, Fold, R>(&mut self, acc: Acc, fold: Fold) -> R
    where
        Self: Sized,
        Fold: FnMut(Acc, Self::Item) -> R,
        R: Try<Ok = Acc>;
    fn fold<Acc, Fold>(self, acc: Acc, fold: Fold) -> Acc
    where
        Fold: FnMut(Acc, Self::Item) -> Acc;
    fn find<P>(&mut self, predicate: P) -> Option<Self::Item>
    where
        P: FnMut(&Self::Item) -> bool;
}

// General Fuse impl
#[doc(hidden)]
impl<I> FuseIteratorImpl<I> for Fuse<I>
where
    I: Iterator,
{
    type Item = <I as Iterator>::Item;

    #[inline]
    default fn next(&mut self) -> Option<<I as Iterator>::Item> {
        fuse!(self.iter.next())
    }

    #[inline]
    default fn nth(&mut self, n: usize) -> Option<I::Item> {
        fuse!(self.iter.nth(n))
    }

    #[inline]
    default fn last(self) -> Option<I::Item> {
        match self.iter {
            Some(iter) => iter.last(),
            None => None,
        }
    }

    #[inline]
    default fn count(self) -> usize {
        match self.iter {
            Some(iter) => iter.count(),
            None => 0,
        }
    }

    #[inline]
    default fn size_hint(&self) -> (usize, Option<usize>) {
        match self.iter {
            Some(ref iter) => iter.size_hint(),
            None => (0, Some(0)),
        }
    }

    #[inline]
    default fn try_fold<Acc, Fold, R>(&mut self, mut acc: Acc, fold: Fold) -> R
    where
        Self: Sized,
        Fold: FnMut(Acc, Self::Item) -> R,
        R: Try<Ok = Acc>,
    {
        if let Some(ref mut iter) = self.iter {
            acc = iter.try_fold(acc, fold)?;
            self.iter = None;
        }
        Try::from_ok(acc)
    }

    #[inline]
    default fn fold<Acc, Fold>(self, mut acc: Acc, fold: Fold) -> Acc
    where
        Fold: FnMut(Acc, Self::Item) -> Acc,
    {
        if let Some(iter) = self.iter {
            acc = iter.fold(acc, fold);
        }
        acc
    }

    #[inline]
    default fn find<P>(&mut self, predicate: P) -> Option<Self::Item>
    where
        P: FnMut(&Self::Item) -> bool,
    {
        fuse!(self.iter.find(predicate))
    }
}

#[doc(hidden)]
impl<I> FuseIteratorImpl<I> for Fuse<I>
where
    I: FusedIterator,
{
    #[inline]
    fn next(&mut self) -> Option<<I as Iterator>::Item> {
        unchecked!(self).next()
    }

    #[inline]
    fn nth(&mut self, n: usize) -> Option<I::Item> {
        unchecked!(self).nth(n)
    }

    #[inline]
    fn last(self) -> Option<I::Item> {
        unchecked!(self).last()
    }

    #[inline]
    fn count(self) -> usize {
        unchecked!(self).count()
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        unchecked!(self).size_hint()
    }

    #[inline]
    fn try_fold<Acc, Fold, R>(&mut self, init: Acc, fold: Fold) -> R
    where
        Self: Sized,
        Fold: FnMut(Acc, Self::Item) -> R,
        R: Try<Ok = Acc>,
    {
        unchecked!(self).try_fold(init, fold)
    }

    #[inline]
    fn fold<Acc, Fold>(self, init: Acc, fold: Fold) -> Acc
    where
        Fold: FnMut(Acc, Self::Item) -> Acc,
    {
        unchecked!(self).fold(init, fold)
    }

    #[inline]
    fn find<P>(&mut self, predicate: P) -> Option<Self::Item>
    where
        P: FnMut(&Self::Item) -> bool,
    {
        unchecked!(self).find(predicate)
    }
}

#[doc(hidden)]
trait FuseDoubleEndedIteratorImpl<I> {
    type Item;
    fn next_back(&mut self) -> Option<Self::Item>;
    fn nth_back(&mut self, n: usize) -> Option<Self::Item>;
    fn try_rfold<Acc, Fold, R>(&mut self, acc: Acc, fold: Fold) -> R
    where
        Self: Sized,
        Fold: FnMut(Acc, Self::Item) -> R,
        R: Try<Ok = Acc>;
    fn rfold<Acc, Fold>(self, acc: Acc, fold: Fold) -> Acc
    where
        Fold: FnMut(Acc, Self::Item) -> Acc;
    fn rfind<P>(&mut self, predicate: P) -> Option<Self::Item>
    where
        P: FnMut(&Self::Item) -> bool;
}

#[doc(hidden)]
impl<I> FuseDoubleEndedIteratorImpl<I> for Fuse<I>
where
    I: DoubleEndedIterator,
{
    type Item = <I as Iterator>::Item;

    #[inline]
    default fn next_back(&mut self) -> Option<<I as Iterator>::Item> {
        fuse!(self.iter.next_back())
    }

    #[inline]
    default fn nth_back(&mut self, n: usize) -> Option<<I as Iterator>::Item> {
        fuse!(self.iter.nth_back(n))
    }

    #[inline]
    default fn try_rfold<Acc, Fold, R>(&mut self, mut acc: Acc, fold: Fold) -> R
    where
        Self: Sized,
        Fold: FnMut(Acc, Self::Item) -> R,
        R: Try<Ok = Acc>,
    {
        if let Some(ref mut iter) = self.iter {
            acc = iter.try_rfold(acc, fold)?;
            self.iter = None;
        }
        Try::from_ok(acc)
    }

    #[inline]
    default fn rfold<Acc, Fold>(self, mut acc: Acc, fold: Fold) -> Acc
    where
        Fold: FnMut(Acc, Self::Item) -> Acc,
    {
        if let Some(iter) = self.iter {
            acc = iter.rfold(acc, fold);
        }
        acc
    }

    #[inline]
    default fn rfind<P>(&mut self, predicate: P) -> Option<Self::Item>
    where
        P: FnMut(&Self::Item) -> bool,
    {
        fuse!(self.iter.rfind(predicate))
    }
}

#[doc(hidden)]
impl<I> FuseDoubleEndedIteratorImpl<I> for Fuse<I>
where
    I: DoubleEndedIterator + FusedIterator,
{
    #[inline]
    fn next_back(&mut self) -> Option<<I as Iterator>::Item> {
        unchecked!(self).next_back()
    }

    #[inline]
    fn nth_back(&mut self, n: usize) -> Option<<I as Iterator>::Item> {
        unchecked!(self).nth_back(n)
    }

    #[inline]
    fn try_rfold<Acc, Fold, R>(&mut self, init: Acc, fold: Fold) -> R
    where
        Self: Sized,
        Fold: FnMut(Acc, Self::Item) -> R,
        R: Try<Ok = Acc>,
    {
        unchecked!(self).try_rfold(init, fold)
    }

    #[inline]
    fn rfold<Acc, Fold>(self, init: Acc, fold: Fold) -> Acc
    where
        Fold: FnMut(Acc, Self::Item) -> Acc,
    {
        unchecked!(self).rfold(init, fold)
    }

    #[inline]
    fn rfind<P>(&mut self, predicate: P) -> Option<Self::Item>
    where
        P: FnMut(&Self::Item) -> bool,
    {
        unchecked!(self).rfind(predicate)
    }
}

#[doc(hidden)]
trait FuseExactSizeIteratorImpl<I> {
    fn len(&self) -> usize;
    fn is_empty(&self) -> bool;
}

impl<I> FuseExactSizeIteratorImpl<I> for Fuse<I>
where
    I: ExactSizeIterator,
{
    default fn len(&self) -> usize {
        match self.iter {
            Some(ref iter) => iter.len(),
            None => 0,
        }
    }

    default fn is_empty(&self) -> bool {
        match self.iter {
            Some(ref iter) => iter.is_empty(),
            None => true,
        }
    }
}

impl<I> FuseExactSizeIteratorImpl<I> for Fuse<I>
where
    I: ExactSizeIterator + FusedIterator,
{
    fn len(&self) -> usize {
        unchecked!(self).len()
    }

    fn is_empty(&self) -> bool {
        unchecked!(self).is_empty()
    }
}
<|endoftext|>


<commit_before>use rustc::hir::def_id::DefId;
use rustc::mir::*;
use rustc::ty::TyCtxt;
use rustc_index::vec::Idx;
use std::fmt::Debug;
use std::io::{self, Write};

use super::pretty::dump_mir_def_ids;

/// Write a graphviz DOT graph of a list of MIRs.
pub fn write_mir_graphviz<W>(
    tcx: TyCtxt<'_>,
    single: Option<DefId>,
    w: &mut W,
) -> io::Result<()>
where
    W: Write,
{
    for def_id in dump_mir_def_ids(tcx, single) {
        let body = &tcx.optimized_mir(def_id);
        write_mir_fn_graphviz(tcx, def_id, body, w)?;
    }
    Ok(())
}

// Must match `[0-9A-Za-z_]*`. This does not appear in the rendered graph, so
// it does not have to be user friendly.
pub fn graphviz_safe_def_name(def_id: DefId) -> String {
    format!(
        "{}_{}",
        def_id.krate.index(),
        def_id.index.index(),
    )
}

/// Write a graphviz DOT graph of the MIR.
pub fn write_mir_fn_graphviz<'tcx, W>(
    tcx: TyCtxt<'tcx>,
    def_id: DefId,
    body: &Body<'_>,
    w: &mut W,
) -> io::Result<()>
where
    W: Write,
{
    writeln!(w, "digraph Mir_{} {{", graphviz_safe_def_name(def_id))?;

    // Global graph properties
    writeln!(w, r#"    graph [fontname="monospace"];"#)?;
    writeln!(w, r#"    node [fontname="monospace"];"#)?;
    writeln!(w, r#"    edge [fontname="monospace"];"#)?;

    // Graph label
    write_graph_label(tcx, def_id, body, w)?;

    // Nodes
    for (block, _) in body.basic_blocks().iter_enumerated() {
        write_node(block, body, w)?;
    }

    // Edges
    for (source, _) in body.basic_blocks().iter_enumerated() {
        write_edges(source, body, w)?;
    }
    writeln!(w, "}}")
}

/// Write a graphviz HTML-styled label for the given basic block, with
/// all necessary escaping already performed. (This is suitable for
/// emitting directly, as is done in this module, or for use with the
/// LabelText::HtmlStr from libgraphviz.)
///
/// `init` and `fini` are callbacks for emitting additional rows of
/// data (using HTML enclosed with `<tr>` in the emitted text).
pub fn write_node_label<W: Write, INIT, FINI>(block: BasicBlock,
                                              body: &Body<'_>,
                                              w: &mut W,
                                              num_cols: u32,
                                              init: INIT,
                                              fini: FINI) -> io::Result<()>
    where INIT: Fn(&mut W) -> io::Result<()>,
          FINI: Fn(&mut W) -> io::Result<()>
{
    let data = &body[block];

    write!(w, r#"
"#)?;

    // Basic block number at the top.
    write!(w, r#"
{blk}
"#,
           attrs=r#"bgcolor="gray" align="center""#,
           colspan=num_cols,
           blk=block.index())?;

    init(w)?;

    // List of statements in the middle.
    if !data.statements.is_empty() {
        write!(w, r#"
"#)?;
        for statement in &data.statements {
            write!(w, "{}
", escape(statement))?;
        }
        write!(w, "
")?;
    }

    // Terminator head at the bottom, not including the list of successor blocks. Those will be
    // displayed as labels on the edges between blocks.
    let mut terminator_head = String::new();
    data.terminator().kind.fmt_head(&mut terminator_head).unwrap();
    write!(w, r#"
{}
"#, dot::escape_html(&terminator_head))?;

    fini(w)?;

    // Close the table
    writeln!(w, "
")
}

/// Write a graphviz DOT node for the given basic block.
fn write_node<W: Write>(block: BasicBlock, body: &Body<'_>, w: &mut W) -> io::Result<()> {
    // Start a new node with the label to follow, in one of DOT's pseudo-HTML tables.
    write!(w, r#"    {} [shape="none", label=<"#, node(block))?;
    write_node_label(block, body, w, 1, |_| Ok(()), |_| Ok(()))?;
    // Close the node label and the node itself.
    writeln!(w, ">];")
}

/// Write graphviz DOT edges with labels between the given basic block and all of its successors.
fn write_edges<W: Write>(source: BasicBlock, body: &Body<'_>, w: &mut W) -> io::Result<()> {
    let terminator = body[source].terminator();
    let labels = terminator.kind.fmt_successor_labels();

    for (&target, label) in terminator.successors().zip(labels) {
        writeln!(w, r#"    {} -> {} [label="{}"];"#, node(source), node(target), label)?;
    }

    Ok(())
}

/// Write the graphviz DOT label for the overall graph. This is essentially a block of text that
/// will appear below the graph, showing the type of the `fn` this MIR represents and the types of
/// all the variables and temporaries.
fn write_graph_label<'tcx, W: Write>(
    tcx: TyCtxt<'tcx>,
    def_id: DefId,
    body: &Body<'_>,
    w: &mut W,
) -> io::Result<()> {
    write!(w, "    label=<fn {}(", dot::escape_html(&tcx.def_path_str(def_id)))?;

    // fn argument types.
    for (i, arg) in body.args_iter().enumerate() {
        if i > 0 {
            write!(w, ", ")?;
        }
        write!(w,
               "{:?}: {}",
               Place::from(arg),
               escape(&body.local_decls[arg].ty)
        )?;
    }

    write!(w, ") -> {}", escape(&body.return_ty()))?;
    write!(w, r#"
"#)?;

    for local in body.vars_and_temps_iter() {
        let decl = &body.local_decls[local];

        write!(w, "let ")?;
        if decl.mutability == Mutability::Mut {
            write!(w, "mut ")?;
        }

        if let Some(name) = decl.name {
            write!(w, r#"{:?}: {}; // {}
"#,
                   Place::from(local), escape(&decl.ty), name)?;
        } else {
            write!(w, r#"{:?}: {};
"#,
                   Place::from(local), escape(&decl.ty))?;
        }
    }

    writeln!(w, ">;")
}

fn node(block: BasicBlock) -> String {
    format!("bb{}", block.index())
}

fn escape<T: Debug>(t: &T) -> String {
    dot::escape_html(&format!("{:?}", t))
}
<commit_msg>Don't print newline inside a node label<commit_after>use rustc::hir::def_id::DefId;
use rustc::mir::*;
use rustc::ty::TyCtxt;
use rustc_index::vec::Idx;
use std::fmt::Debug;
use std::io::{self, Write};

use super::pretty::dump_mir_def_ids;

/// Write a graphviz DOT graph of a list of MIRs.
pub fn write_mir_graphviz<W>(
    tcx: TyCtxt<'_>,
    single: Option<DefId>,
    w: &mut W,
) -> io::Result<()>
where
    W: Write,
{
    for def_id in dump_mir_def_ids(tcx, single) {
        let body = &tcx.optimized_mir(def_id);
        write_mir_fn_graphviz(tcx, def_id, body, w)?;
    }
    Ok(())
}

// Must match `[0-9A-Za-z_]*`. This does not appear in the rendered graph, so
// it does not have to be user friendly.
pub fn graphviz_safe_def_name(def_id: DefId) -> String {
    format!(
        "{}_{}",
        def_id.krate.index(),
        def_id.index.index(),
    )
}

/// Write a graphviz DOT graph of the MIR.
pub fn write_mir_fn_graphviz<'tcx, W>(
    tcx: TyCtxt<'tcx>,
    def_id: DefId,
    body: &Body<'_>,
    w: &mut W,
) -> io::Result<()>
where
    W: Write,
{
    writeln!(w, "digraph Mir_{} {{", graphviz_safe_def_name(def_id))?;

    // Global graph properties
    writeln!(w, r#"    graph [fontname="monospace"];"#)?;
    writeln!(w, r#"    node [fontname="monospace"];"#)?;
    writeln!(w, r#"    edge [fontname="monospace"];"#)?;

    // Graph label
    write_graph_label(tcx, def_id, body, w)?;

    // Nodes
    for (block, _) in body.basic_blocks().iter_enumerated() {
        write_node(block, body, w)?;
    }

    // Edges
    for (source, _) in body.basic_blocks().iter_enumerated() {
        write_edges(source, body, w)?;
    }
    writeln!(w, "}}")
}

/// Write a graphviz HTML-styled label for the given basic block, with
/// all necessary escaping already performed. (This is suitable for
/// emitting directly, as is done in this module, or for use with the
/// LabelText::HtmlStr from libgraphviz.)
///
/// `init` and `fini` are callbacks for emitting additional rows of
/// data (using HTML enclosed with `<tr>` in the emitted text).
pub fn write_node_label<W: Write, INIT, FINI>(block: BasicBlock,
                                              body: &Body<'_>,
                                              w: &mut W,
                                              num_cols: u32,
                                              init: INIT,
                                              fini: FINI) -> io::Result<()>
    where INIT: Fn(&mut W) -> io::Result<()>,
          FINI: Fn(&mut W) -> io::Result<()>
{
    let data = &body[block];

    write!(w, r#"
"#)?;

    // Basic block number at the top.
    write!(w, r#"
{blk}
"#,
           attrs=r#"bgcolor="gray" align="center""#,
           colspan=num_cols,
           blk=block.index())?;

    init(w)?;

    // List of statements in the middle.
    if !data.statements.is_empty() {
        write!(w, r#"
"#)?;
        for statement in &data.statements {
            write!(w, "{}
", escape(statement))?;
        }
        write!(w, "
")?;
    }

    // Terminator head at the bottom, not including the list of successor blocks. Those will be
    // displayed as labels on the edges between blocks.
    let mut terminator_head = String::new();
    data.terminator().kind.fmt_head(&mut terminator_head).unwrap();
    write!(w, r#"
{}
"#, dot::escape_html(&terminator_head))?;

    fini(w)?;

    // Close the table
    write!(w, "
")
}

/// Write a graphviz DOT node for the given basic block.
fn write_node<W: Write>(block: BasicBlock, body: &Body<'_>, w: &mut W) -> io::Result<()> {
    // Start a new node with the label to follow, in one of DOT's pseudo-HTML tables.
    write!(w, r#"    {} [shape="none", label=<"#, node(block))?;
    write_node_label(block, body, w, 1, |_| Ok(()), |_| Ok(()))?;
    // Close the node label and the node itself.
    writeln!(w, ">];")
}

/// Write graphviz DOT edges with labels between the given basic block and all of its successors.
fn write_edges<W: Write>(source: BasicBlock, body: &Body<'_>, w: &mut W) -> io::Result<()> {
    let terminator = body[source].terminator();
    let labels = terminator.kind.fmt_successor_labels();

    for (&target, label) in terminator.successors().zip(labels) {
        writeln!(w, r#"    {} -> {} [label="{}"];"#, node(source), node(target), label)?;
    }

    Ok(())
}

/// Write the graphviz DOT label for the overall graph. This is essentially a block of text that
/// will appear below the graph, showing the type of the `fn` this MIR represents and the types of
/// all the variables and temporaries.
fn write_graph_label<'tcx, W: Write>(
    tcx: TyCtxt<'tcx>,
    def_id: DefId,
    body: &Body<'_>,
    w: &mut W,
) -> io::Result<()> {
    write!(w, "    label=<fn {}(", dot::escape_html(&tcx.def_path_str(def_id)))?;

    // fn argument types.
    for (i, arg) in body.args_iter().enumerate() {
        if i > 0 {
            write!(w, ", ")?;
        }
        write!(w,
               "{:?}: {}",
               Place::from(arg),
               escape(&body.local_decls[arg].ty)
        )?;
    }

    write!(w, ") -> {}", escape(&body.return_ty()))?;
    write!(w, r#"
"#)?;

    for local in body.vars_and_temps_iter() {
        let decl = &body.local_decls[local];

        write!(w, "let ")?;
        if decl.mutability == Mutability::Mut {
            write!(w, "mut ")?;
        }

        if let Some(name) = decl.name {
            write!(w, r#"{:?}: {}; // {}
"#,
                   Place::from(local), escape(&decl.ty), name)?;
        } else {
            write!(w, r#"{:?}: {};
"#,
                   Place::from(local), escape(&decl.ty))?;
        }
    }

    writeln!(w, ">;")
}

fn node(block: BasicBlock) -> String {
    format!("bb{}", block.index())
}

fn escape<T: Debug>(t: &T) -> String {
    dot::escape_html(&format!("{:?}", t))
}
<|endoftext|>


<commit_before>use std::borrow;
use std::cmp::Ordering;
use std::fmt;

#[cfg(feature = "object_sorted")]
use std::collections::BTreeMap;

#[cfg(not(any(feature = "object_sorted")))]
use std::collections::HashMap;

use super::Scalar;

#[cfg(feature = "object_sorted")]
type MapImpl<K, V> = BTreeMap<K, V>;

#[cfg(not(any(feature = "object_sorted")))]
type MapImpl<K, V> = HashMap<K, V>;

/// An enum to represent different value types
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Value {
    /// A scalar value.
    Scalar(Scalar),
    /// A sequence of `Value`s.
    Array(Array),
    /// A sequence of key/`Value` pairs.
    Object(Object),
    /// Nothing.
    Nil,
}

/// Type representing a Liquid array, payload of the `Value::Array` variant
pub type Array = Vec<Value>;

/// Type representing a Liquid object, payload of the `Value::Object` variant
pub type Object = MapImpl<borrow::Cow<'static, str>, Value>;

impl Value {
    /// Create as a `Scalar`.
    pub fn scalar<T: Into<Scalar>>(value: T) -> Self {
        Value::Scalar(Scalar::new(value))
    }

    /// Create as an `Array`.
    pub fn array<I: IntoIterator<Item = Self>>(iter: I) -> Self {
        let v: Array = iter.into_iter().collect();
        Value::Array(v)
    }

    /// Create as nothing.
    pub fn nil() -> Self {
        Value::Nil
    }

    /// Interpret as a string.
    pub fn to_str(&self) -> borrow::Cow<str> {
        match *self {
            Value::Scalar(ref x) => x.to_str(),
            Value::Array(ref x) => {
                let arr: Vec<String> = x.iter().map(|v| v.to_string()).collect();
                borrow::Cow::Owned(arr.join(", "))
            }
            Value::Object(ref x) => {
                let arr: Vec<String> = x.iter().map(|(k, v)| format!("{}: {}", k, v)).collect();
                borrow::Cow::Owned(arr.join(", "))
            }
            Value::Nil => borrow::Cow::Borrowed(""),
        }
    }

    /// Extracts the scalar value if it is a scalar.
    pub fn as_scalar(&self) -> Option<&Scalar> {
        match *self {
            Value::Scalar(ref s) => Some(s),
            _ => None,
        }
    }

    /// Tests whether this value is a scalar
    pub fn is_scalar(&self) -> bool {
        self.as_scalar().is_some()
    }

    /// Extracts the array value if it is an array.
    pub fn as_array(&self) -> Option<&Array> {
        match *self {
            Value::Array(ref s) => Some(s),
            _ => None,
        }
    }

    /// Extracts the array value if it is an array.
    pub fn as_array_mut(&mut self) -> Option<&mut Array> {
        match *self {
            Value::Array(ref mut s) => Some(s),
            _ => None,
        }
    }

    /// Tests whether this value is an array
    pub fn is_array(&self) -> bool {
        self.as_array().is_some()
    }

    /// Extracts the object value if it is a object.
    pub fn as_object(&self) -> Option<&Object> {
        match *self {
            Value::Object(ref s) => Some(s),
            _ => None,
        }
    }

    /// Extracts the object value if it is a object.
    pub fn as_object_mut(&mut self) -> Option<&mut Object> {
        match *self {
            Value::Object(ref mut s) => Some(s),
            _ => None,
        }
    }

    /// Tests whether this value is an object
    pub fn is_object(&self) -> bool {
        self.as_object().is_some()
    }

    /// Tests whether this value is nil
    pub fn is_nil(&self) -> bool {
        match *self {
            Value::Nil => true,
            _ => false,
        }
    }

    /// Evaluate using Liquid "truthiness"
    pub fn is_truthy(&self) -> bool {
        // encode Ruby truthiness: all values except false and nil are true
        match *self {
            Value::Scalar(ref x) => x.is_truthy(),
            Value::Nil => false,
            _ => true,
        }
    }

    /// Whether a default constructed value.
    pub fn is_default(&self) -> bool {
        match *self {
            Value::Scalar(ref x) => x.is_default(),
            Value::Nil => true,
            Value::Array(ref x) => x.is_empty(),
            Value::Object(ref x) => x.is_empty(),
        }
    }

    /// Report the data type (generally for error reporting).
    pub fn type_name(&self) -> &'static str {
        match *self {
            Value::Scalar(ref x) => x.type_name(),
            Value::Nil => "nil",
            Value::Array(_) => "array",
            Value::Object(_) => "object",
        }
    }

    /// Access a contained `Value`.
    pub fn get<'i, I: Into<&'i Scalar>>(&self, index: I) -> Option<&Self> {
        let index: &Scalar = index.into();
        self.get_index(index)
    }

    fn get_index(&self, index: &Scalar) -> Option<&Self> {
        match *self {
            Value::Array(ref x) => {
                if let Some(index) = index.to_integer() {
                    let index = if 0 <= index {
                        index as isize
                    } else {
                        (x.len() as isize) + (index as isize)
                    };
                    x.get(index as usize)
                } else {
                    None
                }
            }
            Value::Object(ref x) => x.get(index.to_str().as_ref()),
            _ => None,
        }
    }
}

impl Default for Value {
    fn default() -> Self {
        Self::nil()
    }
}

impl PartialEq<Value> for Value {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (&Value::Scalar(ref x), &Value::Scalar(ref y)) => x == y,
            (&Value::Array(ref x), &Value::Array(ref y)) => x == y,
            (&Value::Object(ref x), &Value::Object(ref y)) => x == y,
            (&Value::Nil, &Value::Nil) => true,

            // encode Ruby truthiness: all values except false and nil are true
            (&Value::Nil, &Value::Scalar(ref b)) | (&Value::Scalar(ref b), &Value::Nil) => {
                !b.to_bool().unwrap_or(true)
            }
            (_, &Value::Scalar(ref b)) | (&Value::Scalar(ref b), _) => b.to_bool().unwrap_or(false),

            _ => false,
        }
    }
}

impl Eq for Value {}

impl PartialOrd<Value> for Value {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        match (self, other) {
            (&Value::Scalar(ref x), &Value::Scalar(ref y)) => x.partial_cmp(y),
            (&Value::Array(ref x), &Value::Array(ref y)) => x.iter().partial_cmp(y.iter()),
            (&Value::Object(ref x), &Value::Object(ref y)) => x.iter().partial_cmp(y.iter()),
            _ => None,
        }
    }
}

impl fmt::Display for Value {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let data = self.to_str();
        write!(f, "{}", data)
    }
}

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

    #[test]
    fn test_to_string_scalar() {
        let val = Value::scalar(42f64);
        assert_eq!(&val.to_string(), "42");
    }

    #[test]
    fn test_to_string_array() {
        let val = Value::Array(vec![
            Value::scalar(3f64),
            Value::scalar("test"),
            Value::scalar(5.3),
        ]);
        assert_eq!(&val.to_string(), "3, test, 5.3");
    }

    // TODO make a test for object, remember values are in arbitrary orders in HashMaps

    #[test]
    fn test_to_string_nil() {
        assert_eq!(&Value::nil().to_string(), "");
    }

    #[test]
    fn scalar_equality() {
        assert_eq!(Value::scalar("alpha"), Value::scalar("alpha"));
        assert_eq!(Value::scalar(""), Value::scalar(""));
        assert!(Value::scalar("alpha") != Value::scalar("beta"));
        assert!(Value::scalar("beta") != Value::scalar("alpha"));
    }

    #[test]
    fn scalars_have_ruby_truthiness() {
        // all strings in ruby are true
        assert_eq!(Value::scalar(true), Value::scalar("All strings are truthy"));
        assert_eq!(Value::scalar(true), Value::scalar(""));
        assert!(Value::scalar("").is_truthy());

        assert_eq!(Value::scalar(true), Value::scalar(true));
        assert!(Value::scalar(true) != Value::scalar(false));
    }

    #[test]
    fn array_equality() {
        let a = Value::Array(vec![Value::scalar("one"), Value::scalar("two")]);
        let b = Value::Array(vec![Value::scalar("alpha"), Value::scalar("beta")]);

        assert_eq!(a, a);
        assert!(a != b);
        assert!(b != a);
    }

    #[test]
    fn arrays_have_ruby_truthiness() {
        assert_eq!(Value::scalar(true), Value::Array(Vec::new()));
        assert!(Value::Array(Vec::new()).is_truthy());
    }

    #[test]
    fn object_equality() {
        let a: Object = [
            ("alpha".into(), Value::scalar("1")),
            ("beta".into(), Value::scalar(2f64)),
        ]
            .into_iter()
            .cloned()
            .collect();
        let a = Value::Object(a);

        let b: Object = [
            ("alpha".into(), Value::scalar("1")),
            ("beta".into(), Value::scalar(2f64)),
            ("gamma".into(), Value::Array(vec![])),
        ]
            .into_iter()
            .cloned()
            .collect();
        let b = Value::Object(b);

        assert_eq!(a, a);
        assert!(a != b);
        assert!(b != a);
    }

    #[test]
    fn objects_have_ruby_truthiness() {
        assert_eq!(Value::scalar(true), Value::Object(Object::new()));
        assert!(Value::Object(Object::new()).is_truthy());
    }

    #[test]
    fn nil_equality() {
        assert_eq!(Value::Nil, Value::Nil);
    }

    #[test]
    fn nils_have_ruby_truthiness() {
        assert_eq!(Value::scalar(false), Value::Nil);
        assert!(!Value::Nil.is_truthy());

        assert_eq!(Value::scalar(false), Value::Nil);
        assert!(Value::scalar(true) != Value::Nil);
        assert!(Value::scalar("") != Value::Nil);
    }
}
<commit_msg>feat(array): indexing with `.first` and `.last`<commit_after>use std::borrow;
use std::cmp::Ordering;
use std::fmt;

#[cfg(feature = "object_sorted")]
use std::collections::BTreeMap;

#[cfg(not(any(feature = "object_sorted")))]
use std::collections::HashMap;

use super::Scalar;

#[cfg(feature = "object_sorted")]
type MapImpl<K, V> = BTreeMap<K, V>;

#[cfg(not(any(feature = "object_sorted")))]
type MapImpl<K, V> = HashMap<K, V>;

/// An enum to represent different value types
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Value {
    /// A scalar value.
    Scalar(Scalar),
    /// A sequence of `Value`s.
    Array(Array),
    /// A sequence of key/`Value` pairs.
    Object(Object),
    /// Nothing.
    Nil,
}

/// Type representing a Liquid array, payload of the `Value::Array` variant
pub type Array = Vec<Value>;

/// Type representing a Liquid object, payload of the `Value::Object` variant
pub type Object = MapImpl<borrow::Cow<'static, str>, Value>;

impl Value {
    /// Create as a `Scalar`.
    pub fn scalar<T: Into<Scalar>>(value: T) -> Self {
        Value::Scalar(Scalar::new(value))
    }

    /// Create as an `Array`.
    pub fn array<I: IntoIterator<Item = Self>>(iter: I) -> Self {
        let v: Array = iter.into_iter().collect();
        Value::Array(v)
    }

    /// Create as nothing.
    pub fn nil() -> Self {
        Value::Nil
    }

    /// Interpret as a string.
    pub fn to_str(&self) -> borrow::Cow<str> {
        match *self {
            Value::Scalar(ref x) => x.to_str(),
            Value::Array(ref x) => {
                let arr: Vec<String> = x.iter().map(|v| v.to_string()).collect();
                borrow::Cow::Owned(arr.join(", "))
            }
            Value::Object(ref x) => {
                let arr: Vec<String> = x.iter().map(|(k, v)| format!("{}: {}", k, v)).collect();
                borrow::Cow::Owned(arr.join(", "))
            }
            Value::Nil => borrow::Cow::Borrowed(""),
        }
    }

    /// Extracts the scalar value if it is a scalar.
    pub fn as_scalar(&self) -> Option<&Scalar> {
        match *self {
            Value::Scalar(ref s) => Some(s),
            _ => None,
        }
    }

    /// Tests whether this value is a scalar
    pub fn is_scalar(&self) -> bool {
        self.as_scalar().is_some()
    }

    /// Extracts the array value if it is an array.
    pub fn as_array(&self) -> Option<&Array> {
        match *self {
            Value::Array(ref s) => Some(s),
            _ => None,
        }
    }

    /// Extracts the array value if it is an array.
    pub fn as_array_mut(&mut self) -> Option<&mut Array> {
        match *self {
            Value::Array(ref mut s) => Some(s),
            _ => None,
        }
    }

    /// Tests whether this value is an array
    pub fn is_array(&self) -> bool {
        self.as_array().is_some()
    }

    /// Extracts the object value if it is a object.
    pub fn as_object(&self) -> Option<&Object> {
        match *self {
            Value::Object(ref s) => Some(s),
            _ => None,
        }
    }

    /// Extracts the object value if it is a object.
    pub fn as_object_mut(&mut self) -> Option<&mut Object> {
        match *self {
            Value::Object(ref mut s) => Some(s),
            _ => None,
        }
    }

    /// Tests whether this value is an object
    pub fn is_object(&self) -> bool {
        self.as_object().is_some()
    }

    /// Tests whether this value is nil
    pub fn is_nil(&self) -> bool {
        match *self {
            Value::Nil => true,
            _ => false,
        }
    }

    /// Evaluate using Liquid "truthiness"
    pub fn is_truthy(&self) -> bool {
        // encode Ruby truthiness: all values except false and nil are true
        match *self {
            Value::Scalar(ref x) => x.is_truthy(),
            Value::Nil => false,
            _ => true,
        }
    }

    /// Whether a default constructed value.
    pub fn is_default(&self) -> bool {
        match *self {
            Value::Scalar(ref x) => x.is_default(),
            Value::Nil => true,
            Value::Array(ref x) => x.is_empty(),
            Value::Object(ref x) => x.is_empty(),
        }
    }

    /// Report the data type (generally for error reporting).
    pub fn type_name(&self) -> &'static str {
        match *self {
            Value::Scalar(ref x) => x.type_name(),
            Value::Nil => "nil",
            Value::Array(_) => "array",
            Value::Object(_) => "object",
        }
    }

    /// Access a contained `Value`.
    pub fn get<'i, I: Into<&'i Scalar>>(&self, index: I) -> Option<&Self> {
        let index: &Scalar = index.into();
        self.get_index(index)
    }

    fn get_index(&self, index: &Scalar) -> Option<&Self> {
        match *self {
            Value::Array(ref x) => {
                if let Some(index) = index.to_integer() {
                    let index = if 0 <= index {
                        index as isize
                    } else {
                        (x.len() as isize) + (index as isize)
                    };
                    x.get(index as usize)
                } else {
                    match &*index.to_str() {
                        "first" => x.get(0),
                        "last" => x.get(x.len() - 1),
                        _ => None,
                    }
                }
            }
            Value::Object(ref x) => x.get(index.to_str().as_ref()),
            _ => None,
        }
    }
}

impl Default for Value {
    fn default() -> Self {
        Self::nil()
    }
}

impl PartialEq<Value> for Value {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (&Value::Scalar(ref x), &Value::Scalar(ref y)) => x == y,
            (&Value::Array(ref x), &Value::Array(ref y)) => x == y,
            (&Value::Object(ref x), &Value::Object(ref y)) => x == y,
            (&Value::Nil, &Value::Nil) => true,

            // encode Ruby truthiness: all values except false and nil are true
            (&Value::Nil, &Value::Scalar(ref b)) | (&Value::Scalar(ref b), &Value::Nil) => {
                !b.to_bool().unwrap_or(true)
            }
            (_, &Value::Scalar(ref b)) | (&Value::Scalar(ref b), _) => b.to_bool().unwrap_or(false),

            _ => false,
        }
    }
}

impl Eq for Value {}

impl PartialOrd<Value> for Value {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        match (self, other) {
            (&Value::Scalar(ref x), &Value::Scalar(ref y)) => x.partial_cmp(y),
            (&Value::Array(ref x), &Value::Array(ref y)) => x.iter().partial_cmp(y.iter()),
            (&Value::Object(ref x), &Value::Object(ref y)) => x.iter().partial_cmp(y.iter()),
            _ => None,
        }
    }
}

impl fmt::Display for Value {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let data = self.to_str();
        write!(f, "{}", data)
    }
}

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

    #[test]
    fn test_to_string_scalar() {
        let val = Value::scalar(42f64);
        assert_eq!(&val.to_string(), "42");
    }

    #[test]
    fn test_to_string_array() {
        let val = Value::Array(vec![
            Value::scalar(3f64),
            Value::scalar("test"),
            Value::scalar(5.3),
        ]);
        assert_eq!(&val.to_string(), "3, test, 5.3");
    }

    // TODO make a test for object, remember values are in arbitrary orders in HashMaps

    #[test]
    fn test_to_string_nil() {
        assert_eq!(&Value::nil().to_string(), "");
    }

    #[test]
    fn scalar_equality() {
        assert_eq!(Value::scalar("alpha"), Value::scalar("alpha"));
        assert_eq!(Value::scalar(""), Value::scalar(""));
        assert!(Value::scalar("alpha") != Value::scalar("beta"));
        assert!(Value::scalar("beta") != Value::scalar("alpha"));
    }

    #[test]
    fn scalars_have_ruby_truthiness() {
        // all strings in ruby are true
        assert_eq!(Value::scalar(true), Value::scalar("All strings are truthy"));
        assert_eq!(Value::scalar(true), Value::scalar(""));
        assert!(Value::scalar("").is_truthy());

        assert_eq!(Value::scalar(true), Value::scalar(true));
        assert!(Value::scalar(true) != Value::scalar(false));
    }

    #[test]
    fn array_equality() {
        let a = Value::Array(vec![Value::scalar("one"), Value::scalar("two")]);
        let b = Value::Array(vec![Value::scalar("alpha"), Value::scalar("beta")]);

        assert_eq!(a, a);
        assert!(a != b);
        assert!(b != a);
    }

    #[test]
    fn arrays_have_ruby_truthiness() {
        assert_eq!(Value::scalar(true), Value::Array(Vec::new()));
        assert!(Value::Array(Vec::new()).is_truthy());
    }

    #[test]
    fn object_equality() {
        let a: Object = [
            ("alpha".into(), Value::scalar("1")),
            ("beta".into(), Value::scalar(2f64)),
        ]
            .into_iter()
            .cloned()
            .collect();
        let a = Value::Object(a);

        let b: Object = [
            ("alpha".into(), Value::scalar("1")),
            ("beta".into(), Value::scalar(2f64)),
            ("gamma".into(), Value::Array(vec![])),
        ]
            .into_iter()
            .cloned()
            .collect();
        let b = Value::Object(b);

        assert_eq!(a, a);
        assert!(a != b);
        assert!(b != a);
    }

    #[test]
    fn objects_have_ruby_truthiness() {
        assert_eq!(Value::scalar(true), Value::Object(Object::new()));
        assert!(Value::Object(Object::new()).is_truthy());
    }

    #[test]
    fn nil_equality() {
        assert_eq!(Value::Nil, Value::Nil);
    }

    #[test]
    fn nils_have_ruby_truthiness() {
        assert_eq!(Value::scalar(false), Value::Nil);
        assert!(!Value::Nil.is_truthy());

        assert_eq!(Value::scalar(false), Value::Nil);
        assert!(Value::scalar(true) != Value::Nil);
        assert!(Value::scalar("") != Value::Nil);
    }
}
<|endoftext|>


<commit_before>extern crate iridium;

mod commons;

#[test]
fn create_vm() {
    commons::setup();
    let vm = iridium::vm::VM::new();
    assert_eq!(vm.registers.len(), 32);
}

#[test]
fn test_call_return() {
    commons::setup();
    let mut vm = iridium::vm::VM::new();
    let mut asm = iridium::assembler::Assembler::new();
    let code = r"
    .data
    .code
    LOAD $0 #400
    CALL @test
    HLT
    test: LOAD $0 #500
    RET";
    let program = asm.assemble(code);
    vm.add_bytes(program.unwrap());
    let events = vm.run();
    assert_eq!(events[1].event.stop_code(), 0);
}

#[test]
fn test_inc_loop() {
    commons::setup();
    let mut vm = iridium::vm::VM::new();
    let mut asm = iridium::assembler::Assembler::new();
    let code = r"
    .data
    .code
    cloop #10
    load $1 #20
    test: inc $1
    loop @test
    hlt";
    let program = asm.assemble(code);
    vm.add_bytes(program.unwrap());
    let events = vm.run();
    assert_eq!(events[1].event.stop_code(), 0);
    assert_eq!(vm.registers[1], 31);
}

#[test]
fn test_hlt() {
    commons::setup();
    let mut vm = iridium::vm::VM::new();
    let mut asm = iridium::assembler::Assembler::new();
    let code = r"
    .data
    .code
    hlt";
    let program = asm.assemble(code);
    vm.add_bytes(program.unwrap());
    let events = vm.run();
    assert_eq!(events[1].event.stop_code(), 0);
}
<commit_msg>Adds another integration test<commit_after>extern crate iridium;

mod commons;

#[test]
fn create_vm() {
    commons::setup();
    let vm = iridium::vm::VM::new();
    assert_eq!(vm.registers.len(), 32);
}

#[test]
fn test_call_return() {
    commons::setup();
    let mut vm = iridium::vm::VM::new();
    let mut asm = iridium::assembler::Assembler::new();
    let code = r"
    .data
    .code
    LOAD $0 #400
    CALL @test
    HLT
    test: LOAD $0 #500
    RET";
    let program = asm.assemble(code);
    vm.add_bytes(program.unwrap());
    let events = vm.run();
    assert_eq!(events[1].event.stop_code(), 0);
}

#[test]
fn test_inc_loop() {
    commons::setup();
    let mut vm = iridium::vm::VM::new();
    let mut asm = iridium::assembler::Assembler::new();
    let code = r"
    .data
    .code
    cloop #10
    load $1 #20
    test: inc $1
    loop @test
    hlt";
    let program = asm.assemble(code);
    vm.add_bytes(program.unwrap());
    let events = vm.run();
    assert_eq!(events[1].event.stop_code(), 0);
    assert_eq!(vm.registers[1], 31);
}

#[test]
fn test_hlt() {
    commons::setup();
    let mut vm = iridium::vm::VM::new();
    let mut asm = iridium::assembler::Assembler::new();
    let code = r"
    .data
    .code
    hlt";
    let program = asm.assemble(code);
    vm.add_bytes(program.unwrap());
    let events = vm.run();
    assert_eq!(events[1].event.stop_code(), 0);
}

#[test]
fn test_function_call() {
    commons::setup();
    let mut vm = iridium::vm::VM::new();
    let mut asm = iridium::assembler::Assembler::new();
    let code = r"
    .data
    .code
    call @test
    hlt
    test:
    load $31 #1
    ret";
    let program = asm.assemble(code);
    vm.add_bytes(program.unwrap());
    let events = vm.run();
    assert_eq!(vm.registers[31], 1);
    assert_eq!(events[1].event.stop_code(), 0);
}<|endoftext|>


<commit_before>
use std::old_io::fs;
use std::old_io::fs::PathExtensions;
use std::old_io::{ File, USER_DIR, USER_FILE };

use rustc_serialize::json;
use rustc_serialize::json::{ ToJson, Json };

use handlebars::Handlebars;

use ::file_type::{ create_parent_links, Link };

pub fn register_handlebars(source_root: &Path, handlebars: &mut Handlebars) -> Result<(), &'static str> {
    // Validate generic stuff
    let header_hbs_path = source_root.clone().join("partials/header.hbs");
    if !header_hbs_path.exists() {
        return Err("Missing partials/header.hbs");
    }

    let footer_hbs_path = source_root.clone().join("partials/footer.hbs");
    if !footer_hbs_path.exists() {
        return Err("Missing partials/footer.hbs");
    }

    // Validate Dir
    let dir_hbs_path = source_root.clone().join("layouts/dir.hbs");
    if !dir_hbs_path.exists() {
        return Err("Missing /layouts/dir.hbs");
    }

    // Grab generic stuff
    let header_hbs_contents = File::open(&header_hbs_path).read_to_string().unwrap();
    let footer_hbs_contents = File::open(&footer_hbs_path).read_to_string().unwrap();

    // Create Dir
    let dir_template_name = type_str();
    let dir_hbs_contents = File::open(&dir_hbs_path).read_to_string().unwrap();
    handlebars.register_template_string(dir_template_name, format!("{}\n{}\n{}", header_hbs_contents, dir_hbs_contents, footer_hbs_contents))
        .ok().expect("Error registering header|dir|footer template");

    Ok(())
}

pub fn get_url(context: &::AppContext, path: &Path) -> String {
    let relative = path.path_relative_from(&context.root_notes).expect("Problem parsing relative url");
    let relative = if relative.as_str().unwrap() == "." { String::new() } else {
        format!("{}/", relative.as_str().unwrap())
    };
    format!("{}{}", context.base_url, relative)
}

pub fn type_str() -> &'static str {
    "dir"
}

pub fn is_valid_path(path: &Path) -> bool {
    path.is_dir()
}

#[derive(RustcEncodable, Debug, PartialEq)]
struct Child {
    name: String,
    url: String,
    file_type: String
}

#[derive(RustcEncodable)]
struct DirModel {
    name: String,
    parents: Vec<Link>,
    children: Vec<Child>,
    base_url: String
}

impl ToJson for DirModel {
    fn to_json(&self) -> Json {
        Json::from_str(json::encode(&self).unwrap().as_slice()).unwrap()
    }
}

pub fn convert(context: &::AppContext, path: &Path) {
    let relative = path.path_relative_from(&context.root_notes).expect("Problem parsing relative url");
    let new_dir = context.root_dest.join(&relative);
    let new_dir_index = new_dir.join("index.html");
    if !new_dir.exists() {
        fs::mkdir(&new_dir, USER_DIR).ok().expect("Cannot create destination subdir");
    }
    let children = get_children(context, path);
    let name = match relative.filename() {
        Some(_) => String::from_str(relative.filename_str().unwrap()),
        None => String::from_str("root")
    };
    let parents = create_parent_links(context.base_url.as_slice(), &relative, true);
    let dir_model = DirModel {
        name: name,
        parents: parents,
        children: children,
        base_url: context.base_url.clone()
    };
    match context.handlebars.render(type_str(), &dir_model) {
        Ok(rendered) => {
            // Create File
            let mut file = File::create(&new_dir_index).ok().expect("Could not create dir index.html file");
            fs::chmod(&new_dir_index, USER_FILE).ok().expect("Couldn't chmod new file");
            file.write_str(rendered.as_slice())
                .ok().expect("Could not write html to file");
        },
        Err(why) => panic!("Error rendering markdown: {:?}", why)
    }
}

fn get_children(context: &::AppContext, path: &Path) -> Vec<Child> {
    let mut result: Vec<Child> = Vec::new();

        match fs::readdir(&path) {
            Ok(items) => {
                for item in items.iter() {
                    let child_opt = ::file_type::FileType::new(item)
                        .map(|ft| Child {
                            name: String::from_str(item.filestem_str().unwrap()),
                            url: ft.get_url(context),
                            file_type: String::from_str(ft.get_type_str())
                        });
                    if child_opt.is_some() { result.push(child_opt.unwrap()); }
                }
            },
            Err(_) => ()
        }
    result
}

#[test]
fn test() {
    let mut handlebars = Handlebars::new();
    register_handlebars(&Path::new("/home/freied/dev/git/notes-site"), &mut handlebars).ok().unwrap();

    let context = ::AppContext {
        root_source: Path::new("/home/freied/dev/git/notes-site"),
        root_dest: Path::new("/home/freied/temp/dest"),
        root_notes: Path::new("/home/freied/dev/git/notes-site/notes"),
        handlebars: handlebars,
        base_url: String::from_str("/abcd/")
    };

    let path = Path::new("/home/freied/dev/git/notes-site/notes/recipes");
    convert(&context, &path);

    assert!(true);

}
<commit_msg>added ordering<commit_after>
use std::cmp::Ordering;
use std::old_io::fs;
use std::old_io::fs::PathExtensions;
use std::old_io::{ File, USER_DIR, USER_FILE };

use rustc_serialize::json;
use rustc_serialize::json::{ ToJson, Json };

use handlebars::Handlebars;

use ::file_type::{ create_parent_links, Link };

pub fn register_handlebars(source_root: &Path, handlebars: &mut Handlebars) -> Result<(), &'static str> {
    // Validate generic stuff
    let header_hbs_path = source_root.clone().join("partials/header.hbs");
    if !header_hbs_path.exists() {
        return Err("Missing partials/header.hbs");
    }

    let footer_hbs_path = source_root.clone().join("partials/footer.hbs");
    if !footer_hbs_path.exists() {
        return Err("Missing partials/footer.hbs");
    }

    // Validate Dir
    let dir_hbs_path = source_root.clone().join("layouts/dir.hbs");
    if !dir_hbs_path.exists() {
        return Err("Missing /layouts/dir.hbs");
    }

    // Grab generic stuff
    let header_hbs_contents = File::open(&header_hbs_path).read_to_string().unwrap();
    let footer_hbs_contents = File::open(&footer_hbs_path).read_to_string().unwrap();

    // Create Dir
    let dir_template_name = type_str();
    let dir_hbs_contents = File::open(&dir_hbs_path).read_to_string().unwrap();
    handlebars.register_template_string(dir_template_name, format!("{}\n{}\n{}", header_hbs_contents, dir_hbs_contents, footer_hbs_contents))
        .ok().expect("Error registering header|dir|footer template");

    Ok(())
}

pub fn get_url(context: &::AppContext, path: &Path) -> String {
    let relative = path.path_relative_from(&context.root_notes).expect("Problem parsing relative url");
    let relative = if relative.as_str().unwrap() == "." { String::new() } else {
        format!("{}/", relative.as_str().unwrap())
    };
    format!("{}{}", context.base_url, relative)
}

pub fn type_str() -> &'static str {
    "dir"
}

pub fn is_valid_path(path: &Path) -> bool {
    path.is_dir()
}

#[derive(RustcEncodable, Debug, PartialEq)]
struct Child {
    name: String,
    url: String,
    file_type: String
}

#[derive(RustcEncodable)]
struct DirModel {
    name: String,
    parents: Vec<Link>,
    children: Vec<Child>,
    base_url: String
}

impl ToJson for DirModel {
    fn to_json(&self) -> Json {
        Json::from_str(json::encode(&self).unwrap().as_slice()).unwrap()
    }
}

pub fn convert(context: &::AppContext, path: &Path) {
    let relative = path.path_relative_from(&context.root_notes).expect("Problem parsing relative url");
    let new_dir = context.root_dest.join(&relative);
    let new_dir_index = new_dir.join("index.html");
    if !new_dir.exists() {
        fs::mkdir(&new_dir, USER_DIR).ok().expect("Cannot create destination subdir");
    }
    let children = get_children(context, path);
    let name = match relative.filename() {
        Some(_) => String::from_str(relative.filename_str().unwrap()),
        None => String::from_str("root")
    };
    let parents = create_parent_links(context.base_url.as_slice(), &relative, true);
    let dir_model = DirModel {
        name: name,
        parents: parents,
        children: children,
        base_url: context.base_url.clone()
    };
    match context.handlebars.render(type_str(), &dir_model) {
        Ok(rendered) => {
            // Create File
            let mut file = File::create(&new_dir_index).ok().expect("Could not create dir index.html file");
            fs::chmod(&new_dir_index, USER_FILE).ok().expect("Couldn't chmod new file");
            file.write_str(rendered.as_slice())
                .ok().expect("Could not write html to file");
        },
        Err(why) => panic!("Error rendering markdown: {:?}", why)
    }
}

fn get_children(context: &::AppContext, path: &Path) -> Vec<Child> {
    let mut result: Vec<Child> = Vec::new();

        match fs::readdir(&path) {
            Ok(items) => {
                for item in items.iter() {
                    let child_opt = ::file_type::FileType::new(item)
                        .map(|ft| Child {
                            name: String::from_str(item.filestem_str().unwrap()),
                            url: ft.get_url(context),
                            file_type: String::from_str(ft.get_type_str())
                        });
                    if child_opt.is_some() { result.push(child_opt.unwrap()); }
                }
            },
            Err(_) => ()
        }

    result.as_mut_slice().sort_by(|a, b| {
        if a.file_type == String::from_str(type_str()) && b.file_type != String::from_str(type_str()) {
            Ordering::Less
        } else {
            a.file_type.cmp(&b.file_type)
        }

    });

    result
}

#[test]
fn test() {
    let mut handlebars = Handlebars::new();
    register_handlebars(&Path::new("/home/freied/dev/git/notes-site"), &mut handlebars).ok().unwrap();

    let context = ::AppContext {
        root_source: Path::new("/home/freied/dev/git/notes-site"),
        root_dest: Path::new("/home/freied/temp/dest"),
        root_notes: Path::new("/home/freied/dev/git/notes-site/notes"),
        handlebars: handlebars,
        base_url: String::from_str("/abcd/")
    };

    let path = Path::new("/home/freied/dev/git/notes-site/notes/recipes");
    convert(&context, &path);

    assert!(true);

}
<|endoftext|>


<commit_before>use std;
use rustc;

import std::fs;
import std::getopts;
import std::getopts::optopt;
import std::getopts::opt_present;
import std::getopts::opt_str;
import std::io;
import std::vec;
import std::ivec;
import std::str;

import rustc::back::link;
import rustc::syntax::ast;
import driver = rustc::driver::rustc; // see https://github.com/graydon/rust/issues/624
import rustc::driver::session;

fn find_rust_files(&mutable str[] files, str root) {
    for (str filename in fs::list_dir(root)) {
        if (str::ends_with(filename, ".rs")) {
           files += ~[filename];
        }
    }
}

fn main(vec[str] args) {
    auto files = ~[];
    auto root = "/Users/jruderman/code/rust/src/lib/"; // XXX
    find_rust_files(files, root); // not using driver::time here because that currently screws with passing-a-mutable-array

    auto binary = vec::shift[str](args);
    auto binary_dir = fs::dirname(binary);

    let @session::options sopts =
        @rec(library=false,
             static=false,
             optimize=0u,
             debuginfo=false,
             verify=true,
             run_typestate=true,
             save_temps=false,
             stats=false,
             time_passes=false,
             time_llvm_passes=false,
             output_type=link::output_type_bitcode,
             library_search_paths=[binary_dir + "/lib"],
             sysroot=driver::get_default_sysroot(binary),
             cfg=~[],
             test=false);

    let session::session sess = driver::build_session(sopts);

    log_err ivec::len(files);
    for (str file in files) {
        log_err file;
        // Can't use parse_input here because of https://github.com/graydon/rust/issues/632 :(
        //auto crate = driver::parse_input(sess, ~[], file);
        //let @ast::crate crate = driver::time(true, "parsing " + file, bind driver::parse_input(sess, ~[], file));
    }
}

// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
<commit_msg>fuzzer.rs is now a pseudo-fuzzer that takes an AST and replaces expressions inside it<commit_after>use std;
use rustc;

import std::fs;
import std::getopts;
import std::getopts::optopt;
import std::getopts::opt_present;
import std::getopts::opt_str;
import std::io;
import std::io::stdout;
import std::vec;
import std::ivec;
import std::str;
import std::uint;

import rustc::syntax::ast;
import rustc::syntax::fold;
import rustc::syntax::walk;
import rustc::syntax::codemap;
import rustc::syntax::print::pprust;

import driver = rustc::driver::rustc; // see https://github.com/graydon/rust/issues/624
import rustc::back::link;
import rustc::driver::rustc::time;
import rustc::driver::session;

/*
// Imports for "the rest of driver::compile_input"
import rustc::metadata::creader;
import rustc::metadata::cstore;
import rustc::syntax::parse::parser;
import rustc::syntax::parse::token;
import rustc::front;
import rustc::front::attr;
import rustc::middle;
import rustc::middle::trans;
import rustc::middle::resolve;
import rustc::middle::ty;
import rustc::middle::typeck;
import rustc::middle::tstate::ck;
import rustc::syntax::print::pp;
import rustc::util::ppaux;
import rustc::lib::llvm;
*/

fn file_contains(&str filename, &str needle) -> bool {
    auto r = io::file_reader(filename);
    auto contents = str::unsafe_from_bytes(r.read_whole_stream());
    ret str::find(contents, needle) != -1;
}

fn find_rust_files(&mutable str[] files, str path) {
    if (str::ends_with(path, ".rs")) {
        if (file_contains(path, "xfail-stage1")) {
            //log_err "Skipping " + path + " because it is marked as xfail-stage1";
        } else if (
            !str::ends_with(path, "constrained-type.rs") &&     // https://github.com/graydon/rust/issues/653
             str::find(path, "utf8") != -1 &&  // https://github.com/graydon/rust/issues/654
             true) {
            //log_err "Skipping " + path + " because of a known bug";
        } else {
            files += ~[path];
        }
    } else if (fs::file_is_dir(path) && str::find(path, "compile-fail") == -1) {
        for (str p in fs::list_dir(path)) {
            find_rust_files(files, p);
        }
    }
}

fn steal_exprs(&ast::crate crate) -> ast::expr[] {
    let @mutable ast::expr[] exprs = @mutable ~[];
    // "Stash" cannot be type-parameterized because of https://github.com/graydon/rust/issues/375
    fn stash_expr(@mutable ast::expr[] es, &@ast::expr e) { *es += ~[*e]; }
    auto v = rec(visit_expr_pre = bind stash_expr(exprs, _) with walk::default_visitor());
    walk::walk_crate(v, crate);
    *exprs
}

// https://github.com/graydon/rust/issues/652
fn safe_to_replace(ast::expr_ e) -> bool {
    alt (e) {
        case (ast::expr_if(_, _, _)) { false }
        case (ast::expr_block(_)) { false }
        case (_) { true }
    }
}

// Replace the |i|th expr (in fold order) of |crate| with |newexpr|.
fn replace_expr_in_crate(&ast::crate crate, uint i, ast::expr_ newexpr) -> ast::crate {
    let @mutable uint j = @mutable 0u;
    fn fold_expr_rep(@mutable uint j_, uint i_, &ast::expr_ newexpr_, &ast::expr_ original, fold::ast_fold fld) -> ast::expr_ {
      *j_ += 1u;
      if (i_ + 1u == *j_ && safe_to_replace(original)) {
        newexpr_
      } else {
        fold::noop_fold_expr(original, fld)
      }
    }
    auto afp = rec(fold_expr = bind fold_expr_rep(j, i, newexpr, _, _) with *fold::default_ast_fold());
    auto af = fold::make_fold(afp);
    let @ast::crate crate2 = @af.fold_crate(crate);
    fold::dummy_out(af); // work around a leak (https://github.com/graydon/rust/issues/651)
    *crate2
}

iter under(uint n) -> uint { let uint i = 0u; while (i < n) { put i; i += 1u; } }

fn devnull() -> io::writer { std::io::string_writer().get_writer() }

fn pp_variants(&ast::crate crate, &session::session sess, &str filename) {
    auto exprs = steal_exprs(crate);
    auto exprsL = ivec::len(exprs);
    if (exprsL < 100u) {
        for each (uint i in under(uint::min(exprsL, 20u))) {
            log_err "Replacing... " + pprust::expr_to_str(@exprs.(i));
            for each (uint j in under(uint::min(exprsL, 5u))) {
                log_err "With... " + pprust::expr_to_str(@exprs.(j));
                auto crate2 = @replace_expr_in_crate(crate, i, exprs.(j).node);
                pprust::print_crate(sess.get_codemap(), crate2, filename, devnull(), pprust::no_ann());
            }
        }
    }
}

fn main(vec[str] args) {
    auto files = ~[];
    auto root = "/Users/jruderman/code/rust/src/"; // XXX
    find_rust_files(files, root); // not using time here because that currently screws with passing-a-mutable-array
    log_err uint::str(ivec::len(files)) + " files";

    auto binary = vec::shift[str](args);
    auto binary_dir = fs::dirname(binary);

    let @session::options sopts =
        @rec(library=false,
             static=false,
             optimize=0u,
             debuginfo=false,
             verify=true,
             run_typestate=true,
             save_temps=false,
             stats=false,
             time_passes=false,
             time_llvm_passes=false,
             output_type=link::output_type_bitcode,
             library_search_paths=[binary_dir + "/lib"],
             sysroot=driver::get_default_sysroot(binary),
             cfg=~[],
             test=false);

    for (str file in files) {
        log_err "=== " + file + " ===";
        let session::session sess = driver::build_session(sopts);
        let @ast::crate crate = time(true, "parsing " + file, bind driver::parse_input(sess, ~[], file));
        pprust::print_crate(sess.get_codemap(), crate, file, devnull(), pprust::no_ann());
        pp_variants(*crate, sess, file);
    }
}

// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
<|endoftext|>