<commit_before>use prodbg_api::read_write::{Reader, Writer};
use prodbg_api::backend::{CBackendCallbacks};
use plugins::PluginHandler;
use reader_wrapper::{ReaderWrapper, WriterWrapper};
use backend_plugin::{BackendHandle, BackendPlugins};
use libc::{c_void};

#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct SessionHandle(pub u64);

///! Session is a major part of ProDBG. There can be several sessions active at the same time
///! and each session has exactly one backend. There are only communication internally in a session
///! sessions can't (at least now) not talk to eachother.
///!
///! A backend can have several views at the same time. Data can be sent between the backend and
///| views using the PDReader/PDWriter APIs (prodbg::Writer prodbg::Reader in Rust) and this is the
///| only way for views and backends to talk with each other. There are several reasons for this
///| approach:
///!
///| 1. No "hacks" on trying to share memory. Plugins can be over a socket/webview/etc.
///! 2. Views and backends makes no assumetions on the inner workings of the others.
///! 3. Backends and views can post messages which anyone can decide to (optionally) act on.
///!
pub struct Session {
    pub handle: SessionHandle,
    pub reader: Reader,

    current_writer: usize,
    writers: [Writer; 2],

    backend: Option<BackendHandle>,
}

///! Connection options for Remote connections. Currently just one Ip adderss
///!
pub struct ConnectionSettings<'a> {
    pub address: &'a str,
}

impl Session {
    pub fn new(handle: SessionHandle) -> Session {
        Session {
            handle: handle,
            writers: [
                WriterWrapper::create_writer(),
                WriterWrapper::create_writer(),
            ],
            reader: ReaderWrapper::create_reader(),
            current_writer: 0,
            backend: None,
        }
    }

    pub fn get_current_writer(&mut self) -> &mut Writer {
        &mut self.writers[self.current_writer]
    }

    pub fn start_remote(_plugin_handler: &PluginHandler, _settings: &ConnectionSettings) {}

    pub fn start_local(_: &str, _: usize) {}

    pub fn set_backend(&mut self, backend: Option<BackendHandle>) {
        self.backend = backend
    }

    pub fn update(&mut self, backend_plugins: &mut BackendPlugins) {
        // swap the writers
        let c_writer = self.current_writer;
        let p_writer = (self.current_writer + 1) & 1;
        self.current_writer = p_writer;

        ReaderWrapper::init_from_writer(&mut self.reader, &self.writers[p_writer]);
        ReaderWrapper::reset_writer(&mut self.writers[c_writer]);

        if let Some(backend) = backend_plugins.get_backend(self.backend) {
            unsafe {
                let plugin_funcs = backend.plugin_type.plugin_funcs as *mut CBackendCallbacks;
                ((*plugin_funcs).update.unwrap())(backend.plugin_data,
                                                  0,
                                                  self.reader.api as *mut c_void,
                                                  self.writers[p_writer].api as *mut c_void);
            }
        }

        ReaderWrapper::init_from_writer(&mut self.reader, &self.writers[p_writer]);
    }
}

///
/// Sessions handler
///
pub struct Sessions {
    instances: Vec<Session>,
    current: usize,
    session_counter: SessionHandle,
}

impl Sessions {
    pub fn new() -> Sessions {
        Sessions {
            instances: Vec::new(),
            current: 0,
            session_counter: SessionHandle(0),
        }
    }

    pub fn create_instance(&mut self) -> SessionHandle {
        let s = Session::new(self.session_counter);
        let handle = s.handle;
        self.instances.push(s);
        self.session_counter.0 += 1;
        handle
    }

    pub fn update(&mut self, backend_plugins: &mut BackendPlugins) {
        for session in self.instances.iter_mut() {
            session.update(backend_plugins);
        }
    }

    pub fn get_current(&mut self) -> &mut Session {
        let current = self.current;
        &mut self.instances[current]
    }

    pub fn get_session(&mut self, handle: SessionHandle) -> Option<&mut Session> {
        for i in 0..self.instances.len() {
            if self.instances[i].handle == handle {
                return Some(&mut self.instances[i]);
            }
        }

        None
    }
}

#[cfg(test)]
mod tests {
    use core::reader_wrapper::{ReaderWrapper};
    use super::*;

    #[test]
    fn create_session() {
        let _session = Session::new();
    }

    #[test]
    fn write_simple_event() {
        let mut session = Session::new();

        session.writers[0].event_begin(0x44);
        session.writers[0].event_end();

        ReaderWrapper::init_from_writer(&mut session.reader, &session.writers[0]);

        assert_eq!(session.reader.get_event().unwrap(), 0x44);
    }
}

<commit_msg>Made buffer-managment more explicit<commit_after>use prodbg_api::read_write::{Reader, Writer};
use prodbg_api::backend::{CBackendCallbacks};
use plugins::PluginHandler;
use reader_wrapper::{ReaderWrapper, WriterWrapper};
use backend_plugin::{BackendHandle, BackendPlugins};
use libc::{c_void};

#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct SessionHandle(pub u64);

///! Session is a major part of ProDBG. There can be several sessions active at the same time
///! and each session has exactly one backend. There are only communication internally in a session
///! sessions can't (at least now) not talk to eachother.
///!
///! A backend can have several views at the same time. Data can be sent between the backend and
///| views using the PDReader/PDWriter APIs (prodbg::Writer prodbg::Reader in Rust) and this is the
///| only way for views and backends to talk with each other. There are several reasons for this
///| approach:
///!
///| 1. No "hacks" on trying to share memory. Plugins can be over a socket/webview/etc.
///! 2. Views and backends makes no assumetions on the inner workings of the others.
///! 3. Backends and views can post messages which anyone can decide to (optionally) act on.
///!
pub struct Session {
    pub handle: SessionHandle,
    pub reader: Reader,

    //current_writer: usize,
    writers: [Writer; 2],
    action: i32,

    backend: Option<BackendHandle>,
}

///! Connection options for Remote connections. Currently just one Ip adderss
///!
pub struct ConnectionSettings<'a> {
    pub address: &'a str,
}

impl Session {
    pub fn new(handle: SessionHandle) -> Session {
        Session {
            handle: handle,
            writers: [
                WriterWrapper::create_writer(),
                WriterWrapper::create_writer(),
            ],
            reader: ReaderWrapper::create_reader(),
            action: 0,
            //current_writer: 0,
            backend: None,
        }
    }

    pub fn get_current_writer(&mut self) -> &mut Writer {
        &mut self.writers[0]
    }

    pub fn start_remote(_plugin_handler: &PluginHandler, _settings: &ConnectionSettings) {}

    pub fn start_local(_: &str, _: usize) {}

    pub fn set_backend(&mut self, backend: Option<BackendHandle>) {
        self.backend = backend
    }

    pub fn action_step(&mut self) {
        println!("do step");
        self.action = 4;
    }

    pub fn update(&mut self, backend_plugins: &mut BackendPlugins) {
        // swap the writers
        //let c_writer = self.current_writer;
        //let n_writer = (self.current_writer + 1) & 1;

        ReaderWrapper::init_from_writer(&mut self.reader, &self.writers[0]);
        ReaderWrapper::reset_writer(&mut self.writers[1]);

        if let Some(backend) = backend_plugins.get_backend(self.backend) {
            unsafe {
                let plugin_funcs = backend.plugin_type.plugin_funcs as *mut CBackendCallbacks;
                ((*plugin_funcs).update.unwrap())(backend.plugin_data,
                                                  self.action,
                                                  self.reader.api as *mut c_void,
                                                  self.writers[1].api as *mut c_void);
            }
        }

        self.action = 0;

        ReaderWrapper::init_from_writer(&mut self.reader, &self.writers[1]);
        ReaderWrapper::reset_writer(&mut self.writers[0]);
    }
}

///
/// Sessions handler
///
pub struct Sessions {
    instances: Vec<Session>,
    current: usize,
    session_counter: SessionHandle,
}

impl Sessions {
    pub fn new() -> Sessions {
        Sessions {
            instances: Vec::new(),
            current: 0,
            session_counter: SessionHandle(0),
        }
    }

    pub fn create_instance(&mut self) -> SessionHandle {
        let s = Session::new(self.session_counter);
        let handle = s.handle;
        self.instances.push(s);
        self.session_counter.0 += 1;
        handle
    }

    pub fn update(&mut self, backend_plugins: &mut BackendPlugins) {
        for session in self.instances.iter_mut() {
            session.update(backend_plugins);
        }
    }

    pub fn get_current(&mut self) -> &mut Session {
        let current = self.current;
        &mut self.instances[current]
    }

    pub fn get_session(&mut self, handle: SessionHandle) -> Option<&mut Session> {
        for i in 0..self.instances.len() {
            if self.instances[i].handle == handle {
                return Some(&mut self.instances[i]);
            }
        }

        None
    }
}

#[cfg(test)]
mod tests {
    use core::reader_wrapper::{ReaderWrapper};
    use super::*;

    #[test]
    fn create_session() {
        let _session = Session::new();
    }

    #[test]
    fn write_simple_event() {
        let mut session = Session::new();

        session.writers[0].event_begin(0x44);
        session.writers[0].event_end();

        ReaderWrapper::init_from_writer(&mut session.reader, &session.writers[0]);

        assert_eq!(session.reader.get_event().unwrap(), 0x44);
    }
}

<|endoftext|>


<commit_before>use std::ops::{Fn, Mul, Add};
use context::{Context};
use tensor::{Tensor};
use node::{Graph};

pub struct Node<T> {
    id: &'static str,
    op: Box<Fn(Vec<Tensor<T>>) -> Tensor<T>>,
    op_train: Box<Fn(Vec<&Tensor<T>>) -> Tensor<T>>,
    op_prime: Box<Fn(&Tensor<T>) -> Tensor<T>>,
    param: Vec<Box<Graph<T>>>,
}

impl <T> Node<T> {
    pub fn new(&self, node_id: &'static str, operation: Box<Fn(Vec<Tensor<T>>) -> Tensor<T>>, operation_train: Box<Fn(Vec<&Tensor<T>>) -> Tensor<T>>, operation_prime: Box<Fn(&Tensor<T>) -> Tensor<T>>, parameter: Vec<Box<Graph<T>>>) -> Node<T> {
        Node {
            id: node_id,
            op: operation,
            op_train: operation_train,
            op_prime: operation_prime,
            param: parameter,
        }
    }
}

impl <T> Graph<T> for Node<T> where T: Copy + Mul<Output=T> + Add<Output=T> {
    fn get_id(&self) -> &'static str {
        self.id
    }

    fn run(&self, state: &Context<T>, variable: &Context<T>) -> Tensor<T> {
        (self.op)(self.param.iter().map(|node| node.run(state, variable)).collect())
    }

    fn train(&self, state: &Context<T>, variable: &Context<T>, history: &mut Context<T>) {
        let tensor = self.forward_pass(state, variable, history);
        history.set(self.get_id(), tensor);
    }

    fn forward_pass(&self, state: &Context<T>, variable: &Context<T>, history: &Context<T>) -> Tensor<T> {
        (self.op_train)(self.param.iter().map(|node| {
            node.forward_pass(state, variable, history);
            match history.get(node.get_id()) {
                Some(x) => x,
                None => panic!("Node {} does not exist in history", node.get_id()),
            }
        }).collect())
    }

    fn backward_pass(&self, state: &mut Context<T>, variable: &Context<T>, history: &Context<T>, gradient: &Tensor<T>, learning_rate: &f64) {
        for node in self.param.iter() {

        }
    }
}
<commit_msg>gradient computation<commit_after>use std::ops::{Fn, Mul, Add};
use context::{Context};
use tensor::{Tensor};
use node::{Graph};

pub struct Node<T> {
    id: &'static str,
    op: Box<Fn(Vec<Tensor<T>>) -> Tensor<T>>,
    op_train: Box<Fn(Vec<&Tensor<T>>) -> Tensor<T>>,
    op_prime: Box<Fn(&Tensor<T>, Vec<&Tensor<T>>) -> Vec<Tensor<T>>>,
    param: Vec<Box<Graph<T>>>,
}

impl <T> Node<T> {
    pub fn new(&self, node_id: &'static str, operation: Box<Fn(Vec<Tensor<T>>) -> Tensor<T>>, operation_train: Box<Fn(Vec<&Tensor<T>>) -> Tensor<T>>, operation_prime: Box<Fn(&Tensor<T>, Vec<&Tensor<T>>) -> Vec<Tensor<T>>>, parameter: Vec<Box<Graph<T>>>) -> Node<T> {
        Node {
            id: node_id,
            op: operation,
            op_train: operation_train,
            op_prime: operation_prime,
            param: parameter,
        }
    }
}

impl <T> Graph<T> for Node<T> where T: Copy + Mul<Output=T> + Add<Output=T> {
    fn get_id(&self) -> &'static str {
        self.id
    }

    fn run(&self, state: &Context<T>, variable: &Context<T>) -> Tensor<T> {
        (self.op)(self.param.iter().map(|node| node.run(state, variable)).collect())
    }

    fn train(&self, state: &Context<T>, variable: &Context<T>, history: &mut Context<T>) {
        let tensor = self.forward_pass(state, variable, history);
        history.set(self.get_id(), tensor);
    }

    fn forward_pass(&self, state: &Context<T>, variable: &Context<T>, history: &Context<T>) -> Tensor<T> {
        (self.op_train)(self.param.iter().map(|node| {
            node.forward_pass(state, variable, history);
            match history.get(node.get_id()) {
                Some(x) => x,
                None    => panic!("Node {} does not exist in history", node.get_id()),
            }
        }).collect())
    }

    fn backward_pass(&self, state: &mut Context<T>, variable: &Context<T>, history: &Context<T>, gradient: &Tensor<T>, learning_rate: &f64) {
        let deltas = (self.op_prime)(gradient, self.param.iter().map(|node| match history.get(node.get_id()) {
            Some(x) => x,
            None    => panic!("Node {} does not exist in history", node.get_id()),
        }).collect());
        for (delta, parameter) in deltas.iter().zip(self.param.iter()) {
            parameter.backward_pass(state, variable, history, delta, learning_rate);
        }
    }
}
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

// Code to handle a collection of block devices.

use std::io;
use std::collections::{HashMap, HashSet};
use std::fs::{File, OpenOptions};
use std::path::{Path, PathBuf};
use std::str::FromStr;

use time::{Duration, Timespec};
use uuid::Uuid;

use devicemapper::{Bytes, Device, Sectors, Segment};

use super::super::consts::IEC;
use super::super::errors::{EngineError, EngineResult, ErrorEnum};
use super::super::types::{DevUuid, PoolUuid};

use super::cleanup::wipe_blockdevs;
use super::blockdev::BlockDev;
use super::device::blkdev_size;
use super::engine::DevOwnership;
use super::metadata::{BDA, MIN_MDA_SECTORS, StaticHeader, validate_mda_size};
use super::range_alloc::RangeAllocator;
use super::serde_structs::{BlockDevSave, Recordable};

const MIN_DEV_SIZE: Bytes = Bytes(IEC::Gi);

/// Resolve a list of Paths of some sort to a set of unique Devices.
/// Return an IOError if there was a problem resolving any particular device.
pub fn resolve_devices(paths: &[&Path]) -> io::Result<HashSet<Device>> {
    paths
        .iter()
        .map(|p| Device::from_str(&p.to_string_lossy()))
        .collect()
}

#[derive(Debug)]
pub struct BlockDevMgr {
    block_devs: Vec<BlockDev>,
    last_update_time: Option<Timespec>,
}

impl BlockDevMgr {
    pub fn new(block_devs: Vec<BlockDev>) -> BlockDevMgr {
        BlockDevMgr {
            block_devs: block_devs,
            last_update_time: None,
        }
    }

    /// Initialize a new BlockDevMgr with specified pool and devices.
    pub fn initialize(pool_uuid: &PoolUuid,
                      paths: &[&Path],
                      mda_size: Sectors,
                      force: bool)
                      -> EngineResult<BlockDevMgr> {
        let devices = try!(resolve_devices(paths));
        Ok(BlockDevMgr::new(try!(initialize(pool_uuid, devices, mda_size, force))))
    }

    /// Obtain a BlockDev by its Device.
    pub fn get_by_device(&self, device: Device) -> Option<&BlockDev> {
        self.block_devs.iter().find(|d| d.device() == &device)
    }

    // Obtain a BlockDev by its UUID.
    pub fn get_by_uuid(&self, uuid: &DevUuid) -> Option<&BlockDev> {
        self.block_devs.iter().find(|d| d.uuid() == uuid)
    }

    pub fn add(&mut self,
               pool_uuid: &PoolUuid,
               paths: &[&Path],
               force: bool)
               -> EngineResult<Vec<PathBuf>> {
        let devices = try!(resolve_devices(paths));
        let bds = try!(initialize(pool_uuid, devices, MIN_MDA_SECTORS, force));
        let bdev_paths = bds.iter().map(|p| p.devnode.clone()).collect();
        for bd in bds {
            self.block_devs.push(bd);
        }
        Ok(bdev_paths)
    }

    pub fn destroy_all(self) -> EngineResult<()> {
        Ok(try!(wipe_blockdevs(self.block_devs)))
    }

    // Unused space left on blockdevs
    pub fn avail_space(&self) -> Sectors {
        self.block_devs.iter().map(|bd| bd.available()).sum()
    }

    /// If available space is less than size, return None, else return
    /// the segments allocated.
    pub fn alloc_space(&mut self, size: Sectors) -> Option<Vec<Segment>> {
        let mut needed: Sectors = size;
        let mut segs = Vec::new();

        if self.avail_space() < size {
            return None;
        }

        for mut bd in self.block_devs.iter_mut() {
            if needed == Sectors(0) {
                break;
            }

            let (gotten, r_segs) = bd.request_space(needed);
            segs.extend(r_segs);
            needed = needed - gotten;
        }

        assert_eq!(needed, Sectors(0));

        Some(segs)
    }

    pub fn devnodes(&self) -> Vec<PathBuf> {
        self.block_devs
            .iter()
            .map(|d| d.devnode.clone())
            .collect()
    }

    /// Write the given data to all blockdevs marking with specified time.
    /// Return an error if data was not written to any blockdev.
    /// Omit blockdevs which do not have sufficient space in BDA to accommodate
    /// metadata. If specified time is not more recent than previously written
    /// time, use a time that is one nanosecond greater than that previously
    /// written.
    // TODO: Cap # of blockdevs written to, as described in SWDD
    pub fn save_state(&mut self, time: &Timespec, metadata: &[u8]) -> EngineResult<()> {
        let stamp_time = if Some(*time) <= self.last_update_time {
            self.last_update_time.expect("> Some(time)") + Duration::nanoseconds(1)
        } else {
            *time
        };

        let data_size = Bytes(metadata.len() as u64).sectors();
        let saved = self.block_devs
            .iter_mut()
            .filter(|b| b.max_metadata_size() >= data_size)
            .fold(false,
                  |acc, mut b| acc | b.save_state(&stamp_time, metadata).is_ok());

        if saved {
            self.last_update_time = Some(stamp_time);
            Ok(())
        } else {
            let err_msg = "Failed to save metadata to even one device in pool";
            Err(EngineError::Engine(ErrorEnum::Error, err_msg.into()))
        }
    }
}

impl Recordable<HashMap<Uuid, BlockDevSave>> for BlockDevMgr {
    fn record(&self) -> EngineResult<HashMap<Uuid, BlockDevSave>> {
        self.block_devs
            .iter()
            .map(|bd| bd.record().and_then(|bdsave| Ok((*bd.uuid(), bdsave))))
            .collect()
    }
}

/// Initialize multiple blockdevs at once. This allows all of them
/// to be checked for usability before writing to any of them.
pub fn initialize(pool_uuid: &PoolUuid,
                  devices: HashSet<Device>,
                  mda_size: Sectors,
                  force: bool)
                  -> EngineResult<Vec<BlockDev>> {

    /// Get device information, returns an error if problem with obtaining
    /// that information.
    /// Returns a tuple with the device's path, its size in bytes,
    /// its ownership as determined by calling determine_ownership(),
    /// and an open File handle, all of which are needed later.
    pub fn dev_info(dev: &Device) -> EngineResult<(PathBuf, Bytes, DevOwnership, File)> {
        let devnode = try!(dev.devnode().ok_or_else(|| {
            EngineError::Engine(ErrorEnum::NotFound,
                                format!("could not get device node from dev {}", dev.dstr()))
        }));

        let mut f = try!(OpenOptions::new().read(true).write(true).open(&devnode));
        let dev_size = try!(blkdev_size(&f));
        let ownership = try!(StaticHeader::determine_ownership(&mut f));

        Ok((devnode, dev_size, ownership, f))
    }

    /// Filter devices for admission to pool based on dev_infos.
    /// If there is an error finding out the info, return that error.
    /// Also, return an error if a device is not appropriate for this pool.
    #[allow(type_complexity)]
    fn filter_devs<I>(dev_infos: I,
                      pool_uuid: &PoolUuid,
                      force: bool)
                      -> EngineResult<Vec<(Device, (PathBuf, Bytes, File))>>
        where I: Iterator<Item = (Device, EngineResult<(PathBuf, Bytes, DevOwnership, File)>)>
    {
        let mut add_devs = Vec::new();
        for (dev, dev_result) in dev_infos {
            let (devnode, dev_size, ownership, f) = try!(dev_result);
            if dev_size < MIN_DEV_SIZE {
                let error_message = format!("{} too small, minimum {} bytes",
                                            devnode.display(),
                                            MIN_DEV_SIZE);
                return Err(EngineError::Engine(ErrorEnum::Invalid, error_message));
            };
            match ownership {
                DevOwnership::Unowned => add_devs.push((dev, (devnode, dev_size, f))),
                DevOwnership::Theirs => {
                    if !force {
                        let err_str = format!("Device {} appears to belong to another application",
                                              devnode.display());
                        return Err(EngineError::Engine(ErrorEnum::Invalid, err_str));
                    } else {
                        add_devs.push((dev, (devnode, dev_size, f)))
                    }
                }
                DevOwnership::Ours(uuid) => {
                    if *pool_uuid != uuid {
                        let error_str = format!("Device {} already belongs to Stratis pool {}",
                                                devnode.display(),
                                                uuid);
                        return Err(EngineError::Engine(ErrorEnum::Invalid, error_str));
                    } else {
                        // Already in this pool (according to its header)
                        // TODO: Check we already know about it
                        // if yes, ignore. If no, add it w/o initializing?
                    }
                }
            }
        }
        Ok(add_devs)
    }

    try!(validate_mda_size(mda_size));

    let dev_infos = devices.into_iter().map(|d: Device| (d, dev_info(&d)));

    let add_devs = try!(filter_devs(dev_infos, pool_uuid, force));

    let mut bds: Vec<BlockDev> = Vec::new();
    for (dev, (devnode, dev_size, mut f)) in add_devs {

        let bda = BDA::initialize(&mut f,
                                  pool_uuid,
                                  &Uuid::new_v4(),
                                  mda_size,
                                  dev_size.sectors());
        if let Ok(bda) = bda {
            let allocator = RangeAllocator::new(bda.dev_size(), &[(Sectors(0), bda.size())])
                .expect("bda.size() < bda.dev_size() and single range");

            bds.push(BlockDev::new(dev, devnode, bda, allocator));
        } else {
            // TODO: check the return values and update state machine on failure
            let _ = BDA::wipe(&mut f);
            let _ = wipe_blockdevs(bds);

            return Err(bda.unwrap_err());
        }
    }
    Ok(bds)
}
<commit_msg>Randomly select a subset of the block devices to write to<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

// Code to handle a collection of block devices.

use std::io;
use std::collections::{HashMap, HashSet};
use std::fs::{File, OpenOptions};
use std::path::{Path, PathBuf};
use std::str::FromStr;

use rand::{thread_rng, sample};
use time::{Duration, Timespec};
use uuid::Uuid;

use devicemapper::{Bytes, Device, Sectors, Segment};

use super::super::consts::IEC;
use super::super::errors::{EngineError, EngineResult, ErrorEnum};
use super::super::types::{DevUuid, PoolUuid};

use super::cleanup::wipe_blockdevs;
use super::blockdev::BlockDev;
use super::device::blkdev_size;
use super::engine::DevOwnership;
use super::metadata::{BDA, MIN_MDA_SECTORS, StaticHeader, validate_mda_size};
use super::range_alloc::RangeAllocator;
use super::serde_structs::{BlockDevSave, Recordable};

const MIN_DEV_SIZE: Bytes = Bytes(IEC::Gi);
const MAX_NUM_TO_WRITE: usize = 10;

/// Resolve a list of Paths of some sort to a set of unique Devices.
/// Return an IOError if there was a problem resolving any particular device.
pub fn resolve_devices(paths: &[&Path]) -> io::Result<HashSet<Device>> {
    paths
        .iter()
        .map(|p| Device::from_str(&p.to_string_lossy()))
        .collect()
}

#[derive(Debug)]
pub struct BlockDevMgr {
    block_devs: Vec<BlockDev>,
    last_update_time: Option<Timespec>,
}

impl BlockDevMgr {
    pub fn new(block_devs: Vec<BlockDev>) -> BlockDevMgr {
        BlockDevMgr {
            block_devs: block_devs,
            last_update_time: None,
        }
    }

    /// Initialize a new BlockDevMgr with specified pool and devices.
    pub fn initialize(pool_uuid: &PoolUuid,
                      paths: &[&Path],
                      mda_size: Sectors,
                      force: bool)
                      -> EngineResult<BlockDevMgr> {
        let devices = try!(resolve_devices(paths));
        Ok(BlockDevMgr::new(try!(initialize(pool_uuid, devices, mda_size, force))))
    }

    /// Obtain a BlockDev by its Device.
    pub fn get_by_device(&self, device: Device) -> Option<&BlockDev> {
        self.block_devs.iter().find(|d| d.device() == &device)
    }

    // Obtain a BlockDev by its UUID.
    pub fn get_by_uuid(&self, uuid: &DevUuid) -> Option<&BlockDev> {
        self.block_devs.iter().find(|d| d.uuid() == uuid)
    }

    pub fn add(&mut self,
               pool_uuid: &PoolUuid,
               paths: &[&Path],
               force: bool)
               -> EngineResult<Vec<PathBuf>> {
        let devices = try!(resolve_devices(paths));
        let bds = try!(initialize(pool_uuid, devices, MIN_MDA_SECTORS, force));
        let bdev_paths = bds.iter().map(|p| p.devnode.clone()).collect();
        for bd in bds {
            self.block_devs.push(bd);
        }
        Ok(bdev_paths)
    }

    pub fn destroy_all(self) -> EngineResult<()> {
        Ok(try!(wipe_blockdevs(self.block_devs)))
    }

    // Unused space left on blockdevs
    pub fn avail_space(&self) -> Sectors {
        self.block_devs.iter().map(|bd| bd.available()).sum()
    }

    /// If available space is less than size, return None, else return
    /// the segments allocated.
    pub fn alloc_space(&mut self, size: Sectors) -> Option<Vec<Segment>> {
        let mut needed: Sectors = size;
        let mut segs = Vec::new();

        if self.avail_space() < size {
            return None;
        }

        for mut bd in self.block_devs.iter_mut() {
            if needed == Sectors(0) {
                break;
            }

            let (gotten, r_segs) = bd.request_space(needed);
            segs.extend(r_segs);
            needed = needed - gotten;
        }

        assert_eq!(needed, Sectors(0));

        Some(segs)
    }

    pub fn devnodes(&self) -> Vec<PathBuf> {
        self.block_devs
            .iter()
            .map(|d| d.devnode.clone())
            .collect()
    }

    /// Write the given data to all blockdevs marking with specified time.
    /// Return an error if data was not written to any blockdev.
    /// Omit blockdevs which do not have sufficient space in BDA to accommodate
    /// metadata. If specified time is not more recent than previously written
    /// time, use a time that is one nanosecond greater than that previously
    /// written. Randomly select no more than MAX_NUM_TO_WRITE blockdevs to
    /// write to.
    pub fn save_state(&mut self, time: &Timespec, metadata: &[u8]) -> EngineResult<()> {
        let stamp_time = if Some(*time) <= self.last_update_time {
            self.last_update_time.expect("> Some(time)") + Duration::nanoseconds(1)
        } else {
            *time
        };

        let data_size = Bytes(metadata.len() as u64).sectors();
        let candidates = self.block_devs
            .iter_mut()
            .filter(|b| b.max_metadata_size() >= data_size);

        // TODO: consider making selection not entirely random, i.e, ensuring
        // distribution of metadata over different paths.
        let saved = sample(&mut thread_rng(), candidates, MAX_NUM_TO_WRITE)
            .iter_mut()
            .fold(false,
                  |acc, mut b| acc | b.save_state(&stamp_time, metadata).is_ok());

        if saved {
            self.last_update_time = Some(stamp_time);
            Ok(())
        } else {
            let err_msg = "Failed to save metadata to even one device in pool";
            Err(EngineError::Engine(ErrorEnum::Error, err_msg.into()))
        }
    }
}

impl Recordable<HashMap<Uuid, BlockDevSave>> for BlockDevMgr {
    fn record(&self) -> EngineResult<HashMap<Uuid, BlockDevSave>> {
        self.block_devs
            .iter()
            .map(|bd| bd.record().and_then(|bdsave| Ok((*bd.uuid(), bdsave))))
            .collect()
    }
}

/// Initialize multiple blockdevs at once. This allows all of them
/// to be checked for usability before writing to any of them.
pub fn initialize(pool_uuid: &PoolUuid,
                  devices: HashSet<Device>,
                  mda_size: Sectors,
                  force: bool)
                  -> EngineResult<Vec<BlockDev>> {

    /// Get device information, returns an error if problem with obtaining
    /// that information.
    /// Returns a tuple with the device's path, its size in bytes,
    /// its ownership as determined by calling determine_ownership(),
    /// and an open File handle, all of which are needed later.
    pub fn dev_info(dev: &Device) -> EngineResult<(PathBuf, Bytes, DevOwnership, File)> {
        let devnode = try!(dev.devnode().ok_or_else(|| {
            EngineError::Engine(ErrorEnum::NotFound,
                                format!("could not get device node from dev {}", dev.dstr()))
        }));

        let mut f = try!(OpenOptions::new().read(true).write(true).open(&devnode));
        let dev_size = try!(blkdev_size(&f));
        let ownership = try!(StaticHeader::determine_ownership(&mut f));

        Ok((devnode, dev_size, ownership, f))
    }

    /// Filter devices for admission to pool based on dev_infos.
    /// If there is an error finding out the info, return that error.
    /// Also, return an error if a device is not appropriate for this pool.
    #[allow(type_complexity)]
    fn filter_devs<I>(dev_infos: I,
                      pool_uuid: &PoolUuid,
                      force: bool)
                      -> EngineResult<Vec<(Device, (PathBuf, Bytes, File))>>
        where I: Iterator<Item = (Device, EngineResult<(PathBuf, Bytes, DevOwnership, File)>)>
    {
        let mut add_devs = Vec::new();
        for (dev, dev_result) in dev_infos {
            let (devnode, dev_size, ownership, f) = try!(dev_result);
            if dev_size < MIN_DEV_SIZE {
                let error_message = format!("{} too small, minimum {} bytes",
                                            devnode.display(),
                                            MIN_DEV_SIZE);
                return Err(EngineError::Engine(ErrorEnum::Invalid, error_message));
            };
            match ownership {
                DevOwnership::Unowned => add_devs.push((dev, (devnode, dev_size, f))),
                DevOwnership::Theirs => {
                    if !force {
                        let err_str = format!("Device {} appears to belong to another application",
                                              devnode.display());
                        return Err(EngineError::Engine(ErrorEnum::Invalid, err_str));
                    } else {
                        add_devs.push((dev, (devnode, dev_size, f)))
                    }
                }
                DevOwnership::Ours(uuid) => {
                    if *pool_uuid != uuid {
                        let error_str = format!("Device {} already belongs to Stratis pool {}",
                                                devnode.display(),
                                                uuid);
                        return Err(EngineError::Engine(ErrorEnum::Invalid, error_str));
                    } else {
                        // Already in this pool (according to its header)
                        // TODO: Check we already know about it
                        // if yes, ignore. If no, add it w/o initializing?
                    }
                }
            }
        }
        Ok(add_devs)
    }

    try!(validate_mda_size(mda_size));

    let dev_infos = devices.into_iter().map(|d: Device| (d, dev_info(&d)));

    let add_devs = try!(filter_devs(dev_infos, pool_uuid, force));

    let mut bds: Vec<BlockDev> = Vec::new();
    for (dev, (devnode, dev_size, mut f)) in add_devs {

        let bda = BDA::initialize(&mut f,
                                  pool_uuid,
                                  &Uuid::new_v4(),
                                  mda_size,
                                  dev_size.sectors());
        if let Ok(bda) = bda {
            let allocator = RangeAllocator::new(bda.dev_size(), &[(Sectors(0), bda.size())])
                .expect("bda.size() < bda.dev_size() and single range");

            bds.push(BlockDev::new(dev, devnode, bda, allocator));
        } else {
            // TODO: check the return values and update state machine on failure
            let _ = BDA::wipe(&mut f);
            let _ = wipe_blockdevs(bds);

            return Err(bda.unwrap_err());
        }
    }
    Ok(bds)
}
<|endoftext|>


<commit_before>/*!
 * Normalization processes a parse tree until it is in suitable form to
 * be converted to the more canonical form. This is done as a series of
 * passes, each contained in their own module below.
 */

use grammar::parse_tree as pt;

pub type NormResult<T> = Result<T, NormError>;

#[derive(Clone, Debug)]
pub struct NormError {
    message: String,
    span: pt::Span,
}

macro_rules! return_err {
    ($span: expr, $($args:expr),+) => {
        return Err(NormError {
            message: format!($($args),+),
            span: $span
        });
    }
}

// These are executed *IN ORDER*:

// Expands macros and expressions
//
//     X = ...1 Comma<X> (X Y Z) ...2
//
// to
//
//     X = ...1 `Comma<X>` `(X Y Z)` ...2
//     `Comma_X`: Vec<<X>> = ...;
//     `(X Y Z)` = X Y Z;
//
// AFTER THIS POINT: No more macros, macro references, guarded
// alternatives, or expr symbols, though type indirections may occur.
mod macro_expand;

// Computes types where the user omitted them (or from macro
// byproducts).
//
// AFTER THIS POINT: there is a separate `repr::Types` table
// providing all nonterminals with an explicit type.
mod tyinfer;

// Synthesizes action code for all nonterminals.
//
// AFTER THIS POINT: All nonterminals have action code, and all
// Symbol::Choose and Symbol::Name are removed.
// mod action;

// Converts
//
//     X = ...1 Y* ...2
//
// to
//
//     X = ...1    ...2
//       | ...1 Y+ ...2
//
// AFTER THIS POINT: No more Symbol::Star remain.
// mod remove_star;

// Converts X+ to a new terminal X_PLUS like:
//
//     X_PLUS = {
//         <e:X> => { vec![x] }
//         <v:X_PLUS> <e:X> => { let mut v = v; v.push(e); v }
//     }
//
// AFTER THIS POINT: No more Symbol::Plus remain.
// mod remove_plus;

// Converts
//
//     X = ...1 Y? ...2
//
// to
//
//     X = ...1    ...2
//       | ...1 Y  ...2
//
// AFTER THIS POINT: No more Symbol::Question remain.
// mod remove_question;

///////////////////////////////////////////////////////////////////////////
// Shared routines

#[cfg(test)]
mod test_util;

mod norm_util;

<commit_msg>remove steps from the normalization plan that no longer apply<commit_after>/*!
 * Normalization processes a parse tree until it is in suitable form to
 * be converted to the more canonical form. This is done as a series of
 * passes, each contained in their own module below.
 */

use grammar::parse_tree as pt;

pub type NormResult<T> = Result<T, NormError>;

#[derive(Clone, Debug)]
pub struct NormError {
    message: String,
    span: pt::Span,
}

macro_rules! return_err {
    ($span: expr, $($args:expr),+) => {
        return Err(NormError {
            message: format!($($args),+),
            span: $span
        });
    }
}

// These are executed *IN ORDER*:

// Expands macros and expressions
//
//     X = ...1 Comma<X> (X Y Z) ...2
//
// to
//
//     X = ...1 `Comma<X>` `(X Y Z)` ...2
//     `Comma_X`: Vec<<X>> = ...;
//     `(X Y Z)` = X Y Z;
//
// AFTER THIS POINT: No more macros, macro references, guarded
// alternatives, repeats, or expr symbols, though type indirections
// may occur.
mod macro_expand;

// Computes types where the user omitted them (or from macro
// byproducts).
//
// AFTER THIS POINT: there is a separate `repr::Types` table
// providing all nonterminals with an explicit type.
mod tyinfer;

// Synthesizes action code for all nonterminals.
//
// AFTER THIS POINT: All nonterminals have action code, and all
// Symbol::Choose and Symbol::Name are removed.
// mod action;

///////////////////////////////////////////////////////////////////////////
// Shared routines

#[cfg(test)]
mod test_util;

mod norm_util;

<|endoftext|>


<commit_before>use std::env;
use std::fs;
use std::path::Path;

use clap::ArgMatches;
use tempdir::TempDir;
use hyper::Client;
use util;
use extractor;
use downloader;
use profile;
use config::Config;

pub fn init() {
    let config = Config::new();
    if config.install_dir.exists() || config.shim_dir.exists() {
        println!("Already initalized. Reinitializing....");
    }
    fs::create_dir_all(&config.install_dir).expect("Could not create installation directory.");
    fs::create_dir_all(&config.shim_dir).expect("Could not create shims directory.");
    if !env::home_dir()
        .unwrap()
        .join("Documents")
        .join("WindowsPowerShell")
        .join("profile.ps1")
        .exists() {
        println!(r#"Please create profile.ps1 the following place:

$Env:USERPROFILE\Documents\WindowsPowerShell\profile.ps1

And write the following thing:

. $Env:USERPROFILE\.groonga\shims\bin\source-groonga.ps1
"#)
    }
}

pub fn uninstall(m: &ArgMatches) {
    let config = Config::from_matches(m);
    let groonga_dir = format!("groonga-{}-{}",
                              config.version.unwrap(),
                              config.arch.unwrap());
    if config.versions_dir.join(groonga_dir.clone()).exists() {
        println!("Removing {}....", groonga_dir.clone());
        fs::remove_dir_all(&config.versions_dir.join(groonga_dir))
            .expect("Could not remove specified directory.");
    }

}

pub fn install(m: &ArgMatches) {
    const BASE_URL: &'static str = "http://packages.groonga.org/windows/groonga";
    let config = Config::from_matches(m);
    println!("Value for architecture: {}",
             config.arch.clone().expect("unsupported platform"));
    println!("Obtaining Groonga version: {}",
             config.version.clone().unwrap());
    let groonga_dir = format!("groonga-{}-{}",
                              config.version.unwrap(),
                              config.arch.unwrap());
    let groonga_binary = format!("{}.zip", groonga_dir.clone());
    if config.versions_dir.join(groonga_dir.clone()).exists() {
        println!("Already installed. Ready to use it.");
        return ();
    }

    let download_dir = TempDir::new("grnenv-rs")
        .expect("Could not create temp dir.")
        .into_path();

    let client = Client::new();
    let filename = downloader::file_download(&client,
                                             &*format!("{}/{}", BASE_URL, groonga_binary),
                                             download_dir)
        .expect("Failed to download");
    extractor::extract_zip(&filename, &config.versions_dir);
}

pub fn switch(m: &ArgMatches) {
    let config = Config::from_matches(m);
    println!("Value for architecture: {}",
             config.arch.clone().expect("unsupported platform"));
    println!("Using Groonga version: {}", config.version.clone().unwrap());
    let groonga_dir = format!("groonga-{}-{}",
                              config.version.unwrap(),
                              config.arch.unwrap());
    match config.version {
        Some("system") => {
            profile::remove_grnenv_profile(&config.shim_dir).unwrap();
            return ();
        }
        Some(_) => {
            profile::create_profile_source(&config.shim_dir, &groonga_dir, &config.versions_dir)
                .expect("Could not create source-groonga.ps1")
        }
        None => unreachable!(),
    }
}

pub fn versions() {
    let groonga_versioned_dir = util::obtain_groonga_versioned_path();
    let paths = fs::read_dir(&Path::new(&groonga_versioned_dir)).unwrap();

    let names = paths.filter_map(|entry| {
            entry.ok().and_then(|e| {
                e.path()
                    .file_name()
                    .and_then(|n| n.to_str().map(|s| String::from(s)))
            })
        })
        .collect::<Vec<String>>();

    println!("Installed Groonga:");
    println!("\tsystem");
    for entry in names {
        println!("\t{}", entry);
    }
}
<commit_msg>Align<commit_after>use std::env;
use std::fs;
use std::path::Path;

use clap::ArgMatches;
use tempdir::TempDir;
use hyper::Client;
use util;
use extractor;
use downloader;
use profile;
use config::Config;

pub fn init() {
    let config = Config::new();
    if config.install_dir.exists() || config.shim_dir.exists() {
        println!("Already initalized. Reinitializing....");
    }
    fs::create_dir_all(&config.install_dir).expect("Could not create installation directory.");
    fs::create_dir_all(&config.shim_dir).expect("Could not create shims directory.");
    if !env::home_dir()
        .unwrap()
        .join("Documents")
        .join("WindowsPowerShell")
        .join("profile.ps1")
        .exists() {
        println!(r#"Please create profile.ps1 the following place:

$Env:USERPROFILE\Documents\WindowsPowerShell\profile.ps1

And write the following thing:

. $Env:USERPROFILE\.groonga\shims\bin\source-groonga.ps1
"#)
    }
}

pub fn install(m: &ArgMatches) {
    const BASE_URL: &'static str = "http://packages.groonga.org/windows/groonga";
    let config = Config::from_matches(m);
    println!("Value for architecture: {}",
             config.arch.clone().expect("unsupported platform"));
    println!("Obtaining Groonga version: {}",
             config.version.clone().unwrap());
    let groonga_dir = format!("groonga-{}-{}",
                              config.version.unwrap(),
                              config.arch.unwrap());
    let groonga_binary = format!("{}.zip", groonga_dir.clone());
    if config.versions_dir.join(groonga_dir.clone()).exists() {
        println!("Already installed. Ready to use it.");
        return ();
    }

    let download_dir = TempDir::new("grnenv-rs")
        .expect("Could not create temp dir.")
        .into_path();

    let client = Client::new();
    let filename = downloader::file_download(&client,
                                             &*format!("{}/{}", BASE_URL, groonga_binary),
                                             download_dir)
        .expect("Failed to download");
    extractor::extract_zip(&filename, &config.versions_dir);
}

pub fn switch(m: &ArgMatches) {
    let config = Config::from_matches(m);
    println!("Value for architecture: {}",
             config.arch.clone().expect("unsupported platform"));
    println!("Using Groonga version: {}", config.version.clone().unwrap());
    let groonga_dir = format!("groonga-{}-{}",
                              config.version.unwrap(),
                              config.arch.unwrap());
    match config.version {
        Some("system") => {
            profile::remove_grnenv_profile(&config.shim_dir).unwrap();
            return ();
        }
        Some(_) => {
            profile::create_profile_source(&config.shim_dir, &groonga_dir, &config.versions_dir)
                .expect("Could not create source-groonga.ps1")
        }
        None => unreachable!(),
    }
}

pub fn versions() {
    let groonga_versioned_dir = util::obtain_groonga_versioned_path();
    let paths = fs::read_dir(&Path::new(&groonga_versioned_dir)).unwrap();

    let names = paths.filter_map(|entry| {
            entry.ok().and_then(|e| {
                e.path()
                    .file_name()
                    .and_then(|n| n.to_str().map(|s| String::from(s)))
            })
        })
        .collect::<Vec<String>>();

    println!("Installed Groonga:");
    println!("\tsystem");
    for entry in names {
        println!("\t{}", entry);
    }
}

pub fn uninstall(m: &ArgMatches) {
    let config = Config::from_matches(m);
    let groonga_dir = format!("groonga-{}-{}",
                              config.version.unwrap(),
                              config.arch.unwrap());
    if config.versions_dir.join(groonga_dir.clone()).exists() {
        println!("Removing {}....", groonga_dir.clone());
        fs::remove_dir_all(&config.versions_dir.join(groonga_dir))
            .expect("Could not remove specified directory.");
    }
}
<|endoftext|>


<commit_before>use std::thread;

static NTHREADS: i32 = 10;

// This is the `main` thread
fn main() {
    let mut thread_vec: Vec<_> = Vec::new();

    for i in 0..NTHREADS {
        // Spawn up another thread
        let child = thread::spawn(move || {
            println!("this is thread number {}", i)
        });
        thread_vec.push(child);
    }

    for handle in thread_vec {
        handle.join().unwrap();
    }
}<commit_msg>added comment<commit_after>use std::thread;

static NTHREADS: i32 = 10;

// This is the `main` thread
fn main() {
    let mut thread_vec: Vec<_> = Vec::new();

    for i in 0..NTHREADS {
        // Spawn up another thread
        let child = thread::spawn(move || {
            println!("this is thread number {}", i)
        });
        thread_vec.push(child);
    }

    for handle in thread_vec {
        // .unwrap() will propagate a panic from the child thread to the whole program
        handle.join().unwrap();
    }
}<|endoftext|>


<commit_before>use std::sync::Arc;

use parking_lot::{Condvar, Mutex};

/// A Future value which may or may not be filled
#[derive(Debug)]
pub struct Promise<T> {
    mu: Arc<Mutex<(bool, Option<T>)>>,
    cv: Arc<Condvar>,
}

/// The completer side of the Future
pub struct PromiseFiller<T> {
    mu: Arc<Mutex<(bool, Option<T>)>>,
    cv: Arc<Condvar>,
    completed: bool,
}

impl<T> Promise<T> {
    /// Create a new PromiseFiller and the Promise
    /// that will be filled by its completion.
    pub fn pair() -> (PromiseFiller<T>, Self) {
        let mu = Arc::new(Mutex::new((false, None)));
        let cv = Arc::new(Condvar::new());
        let future = Self {
            mu: mu.clone(),
            cv: cv.clone(),
        };
        let filler = PromiseFiller {
            mu,
            cv,
            completed: false,
        };

        (filler, future)
    }

    /// Block on the Promise's completion
    /// or dropping of the PromiseFiller
    pub fn wait(self) -> Option<T> {
        let mut inner = self.mu.lock();
        while !inner.0 {
            self.cv.wait(&mut inner);
        }
        inner.1.take()
    }

    /// Block on the Promise's completion
    /// or dropping of the PromiseFiller.
    ///
    /// # Panics
    /// panics if the PromiseFiller is dropped
    /// without completing the promise.
    pub fn unwrap(self) -> T {
        self.wait().unwrap()
    }
}

impl<T> PromiseFiller<T> {
    /// Complete the Promise
    pub fn fill(mut self, inner: T) {
        let mut mu = self.mu.lock();
        *mu = (true, Some(inner));
        self.cv.notify_all();
        self.completed = true;
    }
}

impl<T> Drop for PromiseFiller<T> {
    fn drop(&mut self) {
        if self.completed {
            return;
        }
        let mut mu = self.mu.lock();
        *mu = (true, None);
        self.cv.notify_all();
    }
}
<commit_msg>Remove old Promise module<commit_after><|endoftext|>


<commit_before>//! FIXME: write short doc here

use std::sync::Arc;

use ra_arena::{impl_arena_id, Arena, RawId};
use rustc_hash::FxHashMap;

use crate::{
    db::HirDatabase,
    expr::{Body, Expr, ExprId, Pat, PatId, Statement},
    DefWithBody, Name,
};

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ScopeId(RawId);
impl_arena_id!(ScopeId);

#[derive(Debug, PartialEq, Eq)]
pub struct ExprScopes {
    pub(crate) body: Arc<Body>,
    scopes: Arena<ScopeId, ScopeData>,
    scope_by_expr: FxHashMap<ExprId, ScopeId>,
}

#[derive(Debug, PartialEq, Eq)]
pub(crate) struct ScopeEntry {
    name: Name,
    pat: PatId,
}

impl ScopeEntry {
    pub(crate) fn name(&self) -> &Name {
        &self.name
    }

    pub(crate) fn pat(&self) -> PatId {
        self.pat
    }
}

#[derive(Debug, PartialEq, Eq)]
pub(crate) struct ScopeData {
    parent: Option<ScopeId>,
    entries: Vec<ScopeEntry>,
}

impl ExprScopes {
    pub(crate) fn expr_scopes_query(db: &impl HirDatabase, def: DefWithBody) -> Arc<ExprScopes> {
        let body = db.body(def);
        let res = ExprScopes::new(body);
        Arc::new(res)
    }

    fn new(body: Arc<Body>) -> ExprScopes {
        let mut scopes = ExprScopes {
            body: body.clone(),
            scopes: Arena::default(),
            scope_by_expr: FxHashMap::default(),
        };
        let root = scopes.root_scope();
        scopes.add_params_bindings(root, body.params());
        compute_expr_scopes(body.body_expr(), &body, &mut scopes, root);
        scopes
    }

    pub(crate) fn entries(&self, scope: ScopeId) -> &[ScopeEntry] {
        &self.scopes[scope].entries
    }

    pub(crate) fn scope_chain(&self, scope: Option<ScopeId>) -> impl Iterator<Item = ScopeId> + '_ {
        std::iter::successors(scope, move |&scope| self.scopes[scope].parent)
    }

    pub(crate) fn scope_for(&self, expr: ExprId) -> Option<ScopeId> {
        self.scope_by_expr.get(&expr).copied()
    }

    pub(crate) fn scope_by_expr(&self) -> &FxHashMap<ExprId, ScopeId> {
        &self.scope_by_expr
    }

    fn root_scope(&mut self) -> ScopeId {
        self.scopes.alloc(ScopeData { parent: None, entries: vec![] })
    }

    fn new_scope(&mut self, parent: ScopeId) -> ScopeId {
        self.scopes.alloc(ScopeData { parent: Some(parent), entries: vec![] })
    }

    fn add_bindings(&mut self, body: &Body, scope: ScopeId, pat: PatId) {
        match &body[pat] {
            Pat::Bind { name, .. } => {
                // bind can have a sub pattern, but it's actually not allowed
                // to bind to things in there
                let entry = ScopeEntry { name: name.clone(), pat };
                self.scopes[scope].entries.push(entry)
            }
            p => p.walk_child_pats(|pat| self.add_bindings(body, scope, pat)),
        }
    }

    fn add_params_bindings(&mut self, scope: ScopeId, params: &[PatId]) {
        let body = Arc::clone(&self.body);
        params.iter().for_each(|pat| self.add_bindings(&body, scope, *pat));
    }

    fn set_scope(&mut self, node: ExprId, scope: ScopeId) {
        self.scope_by_expr.insert(node, scope);
    }
}

fn compute_block_scopes(
    statements: &[Statement],
    tail: Option<ExprId>,
    body: &Body,
    scopes: &mut ExprScopes,
    mut scope: ScopeId,
) {
    for stmt in statements {
        match stmt {
            Statement::Let { pat, initializer, .. } => {
                if let Some(expr) = initializer {
                    scopes.set_scope(*expr, scope);
                    compute_expr_scopes(*expr, body, scopes, scope);
                }
                scope = scopes.new_scope(scope);
                scopes.add_bindings(body, scope, *pat);
            }
            Statement::Expr(expr) => {
                scopes.set_scope(*expr, scope);
                compute_expr_scopes(*expr, body, scopes, scope);
            }
        }
    }
    if let Some(expr) = tail {
        compute_expr_scopes(expr, body, scopes, scope);
    }
}

fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope: ScopeId) {
    scopes.set_scope(expr, scope);
    match &body[expr] {
        Expr::Block { statements, tail } => {
            compute_block_scopes(&statements, *tail, body, scopes, scope);
        }
        Expr::For { iterable, pat, body: body_expr } => {
            compute_expr_scopes(*iterable, body, scopes, scope);
            let scope = scopes.new_scope(scope);
            scopes.add_bindings(body, scope, *pat);
            compute_expr_scopes(*body_expr, body, scopes, scope);
        }
        Expr::Lambda { args, body: body_expr, .. } => {
            let scope = scopes.new_scope(scope);
            scopes.add_params_bindings(scope, &args);
            compute_expr_scopes(*body_expr, body, scopes, scope);
        }
        Expr::Match { expr, arms } => {
            compute_expr_scopes(*expr, body, scopes, scope);
            for arm in arms {
                let scope = scopes.new_scope(scope);
                for pat in &arm.pats {
                    scopes.add_bindings(body, scope, *pat);
                }
                scopes.set_scope(arm.expr, scope);
                compute_expr_scopes(arm.expr, body, scopes, scope);
            }
        }
        e => e.walk_child_exprs(|e| compute_expr_scopes(e, body, scopes, scope)),
    };
}

#[cfg(test)]
mod tests {
    use ra_db::{fixture::WithFixture, SourceDatabase};
    use ra_syntax::{algo::find_node_at_offset, ast, AstNode};
    use test_utils::{assert_eq_text, extract_offset};

    use crate::{source_binder::SourceAnalyzer, test_db::TestDB};

    fn do_check(code: &str, expected: &[&str]) {
        let (off, code) = extract_offset(code);
        let code = {
            let mut buf = String::new();
            let off = u32::from(off) as usize;
            buf.push_str(&code[..off]);
            buf.push_str("marker");
            buf.push_str(&code[off..]);
            buf
        };

        let (db, file_id) = TestDB::with_single_file(&code);
        let file = db.parse(file_id).ok().unwrap();
        let marker: ast::PathExpr = find_node_at_offset(file.syntax(), off).unwrap();
        let analyzer = SourceAnalyzer::new(&db, file_id, marker.syntax(), None);

        let scopes = analyzer.scopes();
        let expr_id = analyzer.body_source_map().node_expr(&marker.into()).unwrap();
        let scope = scopes.scope_for(expr_id);

        let actual = scopes
            .scope_chain(scope)
            .flat_map(|scope| scopes.entries(scope))
            .map(|it| it.name().to_string())
            .collect::<Vec<_>>()
            .join("\n");
        let expected = expected.join("\n");
        assert_eq_text!(&expected, &actual);
    }

    #[test]
    fn test_lambda_scope() {
        do_check(
            r"
            fn quux(foo: i32) {
                let f = |bar, baz: i32| {
                    <|>
                };
            }",
            &["bar", "baz", "foo"],
        );
    }

    #[test]
    fn test_call_scope() {
        do_check(
            r"
            fn quux() {
                f(|x| <|> );
            }",
            &["x"],
        );
    }

    #[test]
    fn test_method_call_scope() {
        do_check(
            r"
            fn quux() {
                z.f(|x| <|> );
            }",
            &["x"],
        );
    }

    #[test]
    fn test_loop_scope() {
        do_check(
            r"
            fn quux() {
                loop {
                    let x = ();
                    <|>
                };
            }",
            &["x"],
        );
    }

    #[test]
    fn test_match() {
        do_check(
            r"
            fn quux() {
                match () {
                    Some(x) => {
                        <|>
                    }
                };
            }",
            &["x"],
        );
    }

    #[test]
    fn test_shadow_variable() {
        do_check(
            r"
            fn foo(x: String) {
                let x : &str = &x<|>;
            }",
            &["x"],
        );
    }

    fn do_check_local_name(code: &str, expected_offset: u32) {
        let (off, code) = extract_offset(code);

        let (db, file_id) = TestDB::with_single_file(&code);
        let file = db.parse(file_id).ok().unwrap();
        let expected_name = find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into())
            .expect("failed to find a name at the target offset");
        let name_ref: ast::NameRef = find_node_at_offset(file.syntax(), off).unwrap();
        let analyzer = SourceAnalyzer::new(&db, file_id, name_ref.syntax(), None);

        let local_name_entry = analyzer.resolve_local_name(&name_ref).unwrap();
        let local_name =
            local_name_entry.ptr().either(|it| it.syntax_node_ptr(), |it| it.syntax_node_ptr());
        assert_eq!(local_name.range(), expected_name.syntax().text_range());
    }

    #[test]
    fn test_resolve_local_name() {
        do_check_local_name(
            r#"
            fn foo(x: i32, y: u32) {
                {
                    let z = x * 2;
                }
                {
                    let t = x<|> * 3;
                }
            }"#,
            21,
        );
    }

    #[test]
    fn test_resolve_local_name_declaration() {
        do_check_local_name(
            r#"
            fn foo(x: String) {
                let x : &str = &x<|>;
            }"#,
            21,
        );
    }

    #[test]
    fn test_resolve_local_name_shadow() {
        do_check_local_name(
            r"
            fn foo(x: String) {
                let x : &str = &x;
                x<|>
            }
            ",
            53,
        );
    }

    #[test]
    fn ref_patterns_contribute_bindings() {
        do_check_local_name(
            r"
            fn foo() {
                if let Some(&from) = bar() {
                    from<|>;
                }
            }
            ",
            53,
        );
    }
}
<commit_msg>Normalize data<commit_after>//! FIXME: write short doc here

use std::sync::Arc;

use ra_arena::{impl_arena_id, Arena, RawId};
use rustc_hash::FxHashMap;

use crate::{
    db::HirDatabase,
    expr::{Body, Expr, ExprId, Pat, PatId, Statement},
    DefWithBody, Name,
};

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ScopeId(RawId);
impl_arena_id!(ScopeId);

#[derive(Debug, PartialEq, Eq)]
pub struct ExprScopes {
    scopes: Arena<ScopeId, ScopeData>,
    scope_by_expr: FxHashMap<ExprId, ScopeId>,
}

#[derive(Debug, PartialEq, Eq)]
pub(crate) struct ScopeEntry {
    name: Name,
    pat: PatId,
}

impl ScopeEntry {
    pub(crate) fn name(&self) -> &Name {
        &self.name
    }

    pub(crate) fn pat(&self) -> PatId {
        self.pat
    }
}

#[derive(Debug, PartialEq, Eq)]
pub(crate) struct ScopeData {
    parent: Option<ScopeId>,
    entries: Vec<ScopeEntry>,
}

impl ExprScopes {
    pub(crate) fn expr_scopes_query(db: &impl HirDatabase, def: DefWithBody) -> Arc<ExprScopes> {
        let body = db.body(def);
        let res = ExprScopes::new(&*body);
        Arc::new(res)
    }

    fn new(body: &Body) -> ExprScopes {
        let mut scopes =
            ExprScopes { scopes: Arena::default(), scope_by_expr: FxHashMap::default() };
        let root = scopes.root_scope();
        scopes.add_params_bindings(body, root, body.params());
        compute_expr_scopes(body.body_expr(), body, &mut scopes, root);
        scopes
    }

    pub(crate) fn entries(&self, scope: ScopeId) -> &[ScopeEntry] {
        &self.scopes[scope].entries
    }

    pub(crate) fn scope_chain(&self, scope: Option<ScopeId>) -> impl Iterator<Item = ScopeId> + '_ {
        std::iter::successors(scope, move |&scope| self.scopes[scope].parent)
    }

    pub(crate) fn scope_for(&self, expr: ExprId) -> Option<ScopeId> {
        self.scope_by_expr.get(&expr).copied()
    }

    pub(crate) fn scope_by_expr(&self) -> &FxHashMap<ExprId, ScopeId> {
        &self.scope_by_expr
    }

    fn root_scope(&mut self) -> ScopeId {
        self.scopes.alloc(ScopeData { parent: None, entries: vec![] })
    }

    fn new_scope(&mut self, parent: ScopeId) -> ScopeId {
        self.scopes.alloc(ScopeData { parent: Some(parent), entries: vec![] })
    }

    fn add_bindings(&mut self, body: &Body, scope: ScopeId, pat: PatId) {
        match &body[pat] {
            Pat::Bind { name, .. } => {
                // bind can have a sub pattern, but it's actually not allowed
                // to bind to things in there
                let entry = ScopeEntry { name: name.clone(), pat };
                self.scopes[scope].entries.push(entry)
            }
            p => p.walk_child_pats(|pat| self.add_bindings(body, scope, pat)),
        }
    }

    fn add_params_bindings(&mut self, body: &Body, scope: ScopeId, params: &[PatId]) {
        params.iter().for_each(|pat| self.add_bindings(body, scope, *pat));
    }

    fn set_scope(&mut self, node: ExprId, scope: ScopeId) {
        self.scope_by_expr.insert(node, scope);
    }
}

fn compute_block_scopes(
    statements: &[Statement],
    tail: Option<ExprId>,
    body: &Body,
    scopes: &mut ExprScopes,
    mut scope: ScopeId,
) {
    for stmt in statements {
        match stmt {
            Statement::Let { pat, initializer, .. } => {
                if let Some(expr) = initializer {
                    scopes.set_scope(*expr, scope);
                    compute_expr_scopes(*expr, body, scopes, scope);
                }
                scope = scopes.new_scope(scope);
                scopes.add_bindings(body, scope, *pat);
            }
            Statement::Expr(expr) => {
                scopes.set_scope(*expr, scope);
                compute_expr_scopes(*expr, body, scopes, scope);
            }
        }
    }
    if let Some(expr) = tail {
        compute_expr_scopes(expr, body, scopes, scope);
    }
}

fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope: ScopeId) {
    scopes.set_scope(expr, scope);
    match &body[expr] {
        Expr::Block { statements, tail } => {
            compute_block_scopes(&statements, *tail, body, scopes, scope);
        }
        Expr::For { iterable, pat, body: body_expr } => {
            compute_expr_scopes(*iterable, body, scopes, scope);
            let scope = scopes.new_scope(scope);
            scopes.add_bindings(body, scope, *pat);
            compute_expr_scopes(*body_expr, body, scopes, scope);
        }
        Expr::Lambda { args, body: body_expr, .. } => {
            let scope = scopes.new_scope(scope);
            scopes.add_params_bindings(body, scope, &args);
            compute_expr_scopes(*body_expr, body, scopes, scope);
        }
        Expr::Match { expr, arms } => {
            compute_expr_scopes(*expr, body, scopes, scope);
            for arm in arms {
                let scope = scopes.new_scope(scope);
                for pat in &arm.pats {
                    scopes.add_bindings(body, scope, *pat);
                }
                scopes.set_scope(arm.expr, scope);
                compute_expr_scopes(arm.expr, body, scopes, scope);
            }
        }
        e => e.walk_child_exprs(|e| compute_expr_scopes(e, body, scopes, scope)),
    };
}

#[cfg(test)]
mod tests {
    use ra_db::{fixture::WithFixture, SourceDatabase};
    use ra_syntax::{algo::find_node_at_offset, ast, AstNode};
    use test_utils::{assert_eq_text, extract_offset};

    use crate::{source_binder::SourceAnalyzer, test_db::TestDB};

    fn do_check(code: &str, expected: &[&str]) {
        let (off, code) = extract_offset(code);
        let code = {
            let mut buf = String::new();
            let off = u32::from(off) as usize;
            buf.push_str(&code[..off]);
            buf.push_str("marker");
            buf.push_str(&code[off..]);
            buf
        };

        let (db, file_id) = TestDB::with_single_file(&code);
        let file = db.parse(file_id).ok().unwrap();
        let marker: ast::PathExpr = find_node_at_offset(file.syntax(), off).unwrap();
        let analyzer = SourceAnalyzer::new(&db, file_id, marker.syntax(), None);

        let scopes = analyzer.scopes();
        let expr_id = analyzer.body_source_map().node_expr(&marker.into()).unwrap();
        let scope = scopes.scope_for(expr_id);

        let actual = scopes
            .scope_chain(scope)
            .flat_map(|scope| scopes.entries(scope))
            .map(|it| it.name().to_string())
            .collect::<Vec<_>>()
            .join("\n");
        let expected = expected.join("\n");
        assert_eq_text!(&expected, &actual);
    }

    #[test]
    fn test_lambda_scope() {
        do_check(
            r"
            fn quux(foo: i32) {
                let f = |bar, baz: i32| {
                    <|>
                };
            }",
            &["bar", "baz", "foo"],
        );
    }

    #[test]
    fn test_call_scope() {
        do_check(
            r"
            fn quux() {
                f(|x| <|> );
            }",
            &["x"],
        );
    }

    #[test]
    fn test_method_call_scope() {
        do_check(
            r"
            fn quux() {
                z.f(|x| <|> );
            }",
            &["x"],
        );
    }

    #[test]
    fn test_loop_scope() {
        do_check(
            r"
            fn quux() {
                loop {
                    let x = ();
                    <|>
                };
            }",
            &["x"],
        );
    }

    #[test]
    fn test_match() {
        do_check(
            r"
            fn quux() {
                match () {
                    Some(x) => {
                        <|>
                    }
                };
            }",
            &["x"],
        );
    }

    #[test]
    fn test_shadow_variable() {
        do_check(
            r"
            fn foo(x: String) {
                let x : &str = &x<|>;
            }",
            &["x"],
        );
    }

    fn do_check_local_name(code: &str, expected_offset: u32) {
        let (off, code) = extract_offset(code);

        let (db, file_id) = TestDB::with_single_file(&code);
        let file = db.parse(file_id).ok().unwrap();
        let expected_name = find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into())
            .expect("failed to find a name at the target offset");
        let name_ref: ast::NameRef = find_node_at_offset(file.syntax(), off).unwrap();
        let analyzer = SourceAnalyzer::new(&db, file_id, name_ref.syntax(), None);

        let local_name_entry = analyzer.resolve_local_name(&name_ref).unwrap();
        let local_name =
            local_name_entry.ptr().either(|it| it.syntax_node_ptr(), |it| it.syntax_node_ptr());
        assert_eq!(local_name.range(), expected_name.syntax().text_range());
    }

    #[test]
    fn test_resolve_local_name() {
        do_check_local_name(
            r#"
            fn foo(x: i32, y: u32) {
                {
                    let z = x * 2;
                }
                {
                    let t = x<|> * 3;
                }
            }"#,
            21,
        );
    }

    #[test]
    fn test_resolve_local_name_declaration() {
        do_check_local_name(
            r#"
            fn foo(x: String) {
                let x : &str = &x<|>;
            }"#,
            21,
        );
    }

    #[test]
    fn test_resolve_local_name_shadow() {
        do_check_local_name(
            r"
            fn foo(x: String) {
                let x : &str = &x;
                x<|>
            }
            ",
            53,
        );
    }

    #[test]
    fn ref_patterns_contribute_bindings() {
        do_check_local_name(
            r"
            fn foo() {
                if let Some(&from) = bar() {
                    from<|>;
                }
            }
            ",
            53,
        );
    }
}
<|endoftext|>


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

use anyhow::{anyhow, bail, Result};
use dirs;
use std::io::Write;
use std::path::PathBuf;
use std::{env, fs};

const LOGO_32X32: &[u8] = include_bytes!("../third_party/rust/rust-logo-32x32.png");
const LOGO_64X64: &[u8] = include_bytes!("../third_party/rust/rust-logo-64x64.png");
const LOGO_LICENSE: &[u8] = include_bytes!("../third_party/rust/LICENSE.md");
const KERNEL_JS: &[u8] = include_bytes!("../client/kernel.js");
const LINT_JS: &[u8] = include_bytes!("../third_party/CodeMirror/addons/lint/lint.js");
const LINT_CSS: &[u8] = include_bytes!("../third_party/CodeMirror/addons/lint/lint.css");
const LINT_LICENSE: &[u8] = include_bytes!("../third_party/CodeMirror/LICENSE");
const VERSION_TXT: &[u8] = include_bytes!("../client/version.txt");

pub(crate) fn install() -> Result<()> {
    let kernel_dir = get_kernel_dir()?;
    fs::create_dir_all(&kernel_dir)?;
    let current_exe_path = env::current_exe()?;
    let current_exe = current_exe_path
        .to_str()
        .ok_or_else(|| anyhow!("current exe path isn't valid UTF-8"))?;
    let kernel_json = object! {
        "argv" => array![current_exe, "--control_file", "{connection_file}"],
        "display_name" => "Rust",
        "language" => "rust",
        "interrupt_mode" => "message",
    };
    let kernel_json_filename = kernel_dir.join("kernel.json");
    println!("Writing {}", kernel_json_filename.to_string_lossy());
    kernel_json.write_pretty(&mut fs::File::create(kernel_json_filename)?, 2)?;
    install_resource(&kernel_dir, "logo-32x32.png", LOGO_32X32)?;
    install_resource(&kernel_dir, "logo-64x64.png", LOGO_64X64)?;
    install_resource(&kernel_dir, "logo-LICENSE.md", LOGO_LICENSE)?;
    install_resource(&kernel_dir, "kernel.js", KERNEL_JS)?;
    install_resource(&kernel_dir, "lint.js", LINT_JS)?;
    install_resource(&kernel_dir, "lint.css", LINT_CSS)?;
    install_resource(&kernel_dir, "lint-LICENSE", LINT_LICENSE)?;
    install_resource(&kernel_dir, "version.txt", VERSION_TXT)?;
    println!("Installation complete");
    Ok(())
}

/// Checks if the current installation is out-of-date, by looking at what's in
/// version.txt. If it is out of date, then updates it.
pub(crate) fn update_if_necessary() -> Result<()> {
    let kernel_dir = get_kernel_dir()?;
    let installed_version = std::fs::read(kernel_dir.join("version.txt")).unwrap_or_default();
    if installed_version != VERSION_TXT {
        install()?;
        eprintln!(
            "\n\n==================================================================\n\
            Updated Evcxr Jupyter installation. Note, updates unfortunately \n\
            won't take effect until the next time you start jupyter notebook.\n\
            ==================================================================\n"
        );
    }
    Ok(())
}

pub(crate) fn install_resource(dir: &PathBuf, filename: &str, bytes: &'static [u8]) -> Result<()> {
    let res_path = dir.join(filename);
    println!("Writing {}", res_path.to_string_lossy());
    let mut file = fs::File::create(res_path)?;
    file.write_all(bytes)?;
    Ok(())
}

pub(crate) fn uninstall() -> Result<()> {
    let kernel_dir = get_kernel_dir()?;
    println!("Deleting {}", kernel_dir.to_string_lossy());
    fs::remove_dir_all(kernel_dir)?;
    println!("Uninstall complete");
    Ok(())
}

// https://jupyter-client.readthedocs.io/en/latest/kernels.html
fn get_kernel_dir() -> Result<PathBuf> {
    let jupyter_dir = if let Ok(dir) = env::var("JUPYTER_CONFIG_DIR") {
        PathBuf::from(dir)
    } else if let Ok(dir) = env::var("JUPYTER_PATH") {
        PathBuf::from(dir)
    } else if let Some(dir) = get_user_kernel_dir() {
        dir
    } else {
        bail!("Couldn't get XDG data directory");
    };
    Ok(jupyter_dir.join("kernels").join("rust"))
}

#[cfg(not(target_os = "macos"))]
fn get_user_kernel_dir() -> Option<PathBuf> {
    dirs::data_dir().map(|data_dir| data_dir.join("jupyter"))
}

#[cfg(target_os = "macos")]
fn get_user_kernel_dir() -> Option<PathBuf> {
    dirs::data_dir().and_then(|d| d.parent().map(|data_dir| data_dir.join("Jupyter")))
}
<commit_msg>Jupyter install: Ignore JUPYTER_CONFIG_DIR and JUPYTER_PATH<commit_after>// Copyright 2020 The Evcxr Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use anyhow::{anyhow, bail, Result};
use dirs;
use std::io::Write;
use std::path::PathBuf;
use std::{env, fs};

const LOGO_32X32: &[u8] = include_bytes!("../third_party/rust/rust-logo-32x32.png");
const LOGO_64X64: &[u8] = include_bytes!("../third_party/rust/rust-logo-64x64.png");
const LOGO_LICENSE: &[u8] = include_bytes!("../third_party/rust/LICENSE.md");
const KERNEL_JS: &[u8] = include_bytes!("../client/kernel.js");
const LINT_JS: &[u8] = include_bytes!("../third_party/CodeMirror/addons/lint/lint.js");
const LINT_CSS: &[u8] = include_bytes!("../third_party/CodeMirror/addons/lint/lint.css");
const LINT_LICENSE: &[u8] = include_bytes!("../third_party/CodeMirror/LICENSE");
const VERSION_TXT: &[u8] = include_bytes!("../client/version.txt");

pub(crate) fn install() -> Result<()> {
    let kernel_dir = get_kernel_dir()?;
    fs::create_dir_all(&kernel_dir)?;
    let current_exe_path = env::current_exe()?;
    let current_exe = current_exe_path
        .to_str()
        .ok_or_else(|| anyhow!("current exe path isn't valid UTF-8"))?;
    let kernel_json = object! {
        "argv" => array![current_exe, "--control_file", "{connection_file}"],
        "display_name" => "Rust",
        "language" => "rust",
        "interrupt_mode" => "message",
    };
    let kernel_json_filename = kernel_dir.join("kernel.json");
    println!("Writing {}", kernel_json_filename.to_string_lossy());
    kernel_json.write_pretty(&mut fs::File::create(kernel_json_filename)?, 2)?;
    install_resource(&kernel_dir, "logo-32x32.png", LOGO_32X32)?;
    install_resource(&kernel_dir, "logo-64x64.png", LOGO_64X64)?;
    install_resource(&kernel_dir, "logo-LICENSE.md", LOGO_LICENSE)?;
    install_resource(&kernel_dir, "kernel.js", KERNEL_JS)?;
    install_resource(&kernel_dir, "lint.js", LINT_JS)?;
    install_resource(&kernel_dir, "lint.css", LINT_CSS)?;
    install_resource(&kernel_dir, "lint-LICENSE", LINT_LICENSE)?;
    install_resource(&kernel_dir, "version.txt", VERSION_TXT)?;
    println!("Installation complete");
    Ok(())
}

/// Checks if the current installation is out-of-date, by looking at what's in
/// version.txt. If it is out of date, then updates it.
pub(crate) fn update_if_necessary() -> Result<()> {
    let kernel_dir = get_kernel_dir()?;
    let installed_version = std::fs::read(kernel_dir.join("version.txt")).unwrap_or_default();
    if installed_version != VERSION_TXT {
        install()?;
        eprintln!(
            "\n\n==================================================================\n\
            Updated Evcxr Jupyter installation. Note, updates unfortunately \n\
            won't take effect until the next time you start jupyter notebook.\n\
            ==================================================================\n"
        );
    }
    Ok(())
}

pub(crate) fn install_resource(dir: &PathBuf, filename: &str, bytes: &'static [u8]) -> Result<()> {
    let res_path = dir.join(filename);
    println!("Writing {}", res_path.to_string_lossy());
    let mut file = fs::File::create(res_path)?;
    file.write_all(bytes)?;
    Ok(())
}

pub(crate) fn uninstall() -> Result<()> {
    let kernel_dir = get_kernel_dir()?;
    println!("Deleting {}", kernel_dir.to_string_lossy());
    fs::remove_dir_all(kernel_dir)?;
    println!("Uninstall complete");
    Ok(())
}

// https://jupyter-client.readthedocs.io/en/latest/kernels.html
fn get_kernel_dir() -> Result<PathBuf> {
    let jupyter_dir = if let Some(dir) = get_user_kernel_dir() {
        dir
    } else {
        bail!("Couldn't get XDG data directory");
    };
    Ok(jupyter_dir.join("kernels").join("rust"))
}

#[cfg(not(target_os = "macos"))]
fn get_user_kernel_dir() -> Option<PathBuf> {
    dirs::data_dir().map(|data_dir| data_dir.join("jupyter"))
}

#[cfg(target_os = "macos")]
fn get_user_kernel_dir() -> Option<PathBuf> {
    dirs::data_dir().and_then(|d| d.parent().map(|data_dir| data_dir.join("Jupyter")))
}
<|endoftext|>


<commit_before>extern crate rgtk;

use rgtk::*;
use std::cmp::Ordering;
use std::collections::HashSet;
use std::io::fs;
use std::io::fs::PathExtensions;

fn path_sorter(a: &Path, b: &Path) -> Ordering {
    let leaf_a = a.filename_str();
    let leaf_b = b.filename_str();
    leaf_a.cmp(&leaf_b)
}

fn sort_paths(paths: &Vec<Path>) -> Vec<Path> {
    let mut paths_vec = paths.clone();
    paths_vec.sort_by(path_sorter);
    paths_vec
}

fn sort_string_paths(paths: &HashSet<String>) -> Vec<Path> {
    let mut paths_vec = Vec::new();
    for path in paths.iter() {
        paths_vec.push(Path::new(path));
    }
    paths_vec.sort_by(path_sorter);
    paths_vec
}

fn get_first_path(state: &::utils::State) -> Option<gtk::TreePath> {
    let mut iter = gtk::TreeIter::new().unwrap();
    let model = state.tree_store.get_model().unwrap();

    let path = if model.get_iter_first(&mut iter) {
        model.get_path(&iter)
    } else {
        None
    };

    iter.drop();
    path
}

pub fn update_project_buttons(state: &::utils::State) {
    if let Some(path_str) = ::utils::get_selected_path(state) {
        let is_project = state.projects.contains(&path_str);
        let path = Path::new(path_str);
        state.rename_button.set_sensitive(!path.is_dir());
        state.remove_button.set_sensitive(!path.is_dir() || is_project);
    } else {
        state.rename_button.set_sensitive(false);
        state.remove_button.set_sensitive(false);
    }
}

fn add_node(
    state: &::utils::State,
    node: &Path,
    parent: Option<&gtk::TreeIter>)
{
    let mut iter = gtk::TreeIter::new().unwrap();

    if let Some(leaf_str) = node.filename_str() {
        if !leaf_str.starts_with(".") {
            state.tree_store.append(&iter, parent);
            state.tree_store.set_string(&iter, 0, leaf_str);
            state.tree_store.set_string(&iter, 1, node.as_str().unwrap());

            if node.is_dir() {
                match fs::readdir(node) {
                    Ok(children) => {
                        for child in sort_paths(&children).iter() {
                            add_node(state, child, Some(&iter));
                        }
                    },
                    Err(e) => println!("Error updating tree: {}", e)
                }
            }
        }
    }

    iter.drop();
}

fn expand_nodes(
    state: &mut ::utils::State,
    tree: &mut gtk::TreeView,
    parent: Option<&gtk::TreeIter>)
{
    let mut iter = gtk::TreeIter::new().unwrap();

    if state.tree_model.iter_children(&mut iter, parent) {
        loop {
            if let Some(path_str) =
                state.tree_model.get_value(&iter, 1).get_string()
            {
                if let Some(selection_str) = state.selection.clone() {
                    if path_str == selection_str {
                        if let Some(path) = state.tree_model.get_path(&iter) {
                            tree.set_cursor(&path, None, false);
                        }
                    }
                }

                if state.expansions.contains(&path_str) {
                    if let Some(path) = state.tree_model.get_path(&iter) {
                        tree.expand_row(&path, false);
                    }
                    expand_nodes(state, tree, Some(&iter));
                }
            }

            if !state.tree_model.iter_next(&mut iter) {
                break;
            }
        }
    }

    iter.drop();
}

pub fn update_project_tree(
    state: &mut ::utils::State,
    tree: &mut gtk::TreeView)
{
    state.tree_store.clear();

    for path in sort_string_paths(&state.projects).iter() {
        add_node(state, path, None);
    }

    expand_nodes(state, tree, None);

    let mut iter = gtk::TreeIter::new().unwrap();
    if !state.tree_selection.get_selected(state.tree_model, &mut iter) {
        if let Some(path) = get_first_path(state) {
            tree.set_cursor(&path, None, false)
        }
    }
    iter.drop();

    update_project_buttons(state);
}
<commit_msg>Don't expand children if parent isn't expanded<commit_after>extern crate rgtk;

use rgtk::*;
use std::cmp::Ordering;
use std::collections::HashSet;
use std::io::fs;
use std::io::fs::PathExtensions;

fn path_sorter(a: &Path, b: &Path) -> Ordering {
    let leaf_a = a.filename_str();
    let leaf_b = b.filename_str();
    leaf_a.cmp(&leaf_b)
}

fn sort_paths(paths: &Vec<Path>) -> Vec<Path> {
    let mut paths_vec = paths.clone();
    paths_vec.sort_by(path_sorter);
    paths_vec
}

fn sort_string_paths(paths: &HashSet<String>) -> Vec<Path> {
    let mut paths_vec = Vec::new();
    for path in paths.iter() {
        paths_vec.push(Path::new(path));
    }
    paths_vec.sort_by(path_sorter);
    paths_vec
}

fn get_first_path(state: &::utils::State) -> Option<gtk::TreePath> {
    let mut iter = gtk::TreeIter::new().unwrap();
    let model = state.tree_store.get_model().unwrap();

    let path = if model.get_iter_first(&mut iter) {
        model.get_path(&iter)
    } else {
        None
    };

    iter.drop();
    path
}

pub fn update_project_buttons(state: &::utils::State) {
    if let Some(path_str) = ::utils::get_selected_path(state) {
        let is_project = state.projects.contains(&path_str);
        let path = Path::new(path_str);
        state.rename_button.set_sensitive(!path.is_dir());
        state.remove_button.set_sensitive(!path.is_dir() || is_project);
    } else {
        state.rename_button.set_sensitive(false);
        state.remove_button.set_sensitive(false);
    }
}

fn add_node(
    state: &::utils::State,
    node: &Path,
    parent: Option<&gtk::TreeIter>)
{
    let mut iter = gtk::TreeIter::new().unwrap();

    if let Some(leaf_str) = node.filename_str() {
        if !leaf_str.starts_with(".") {
            state.tree_store.append(&iter, parent);
            state.tree_store.set_string(&iter, 0, leaf_str);
            state.tree_store.set_string(&iter, 1, node.as_str().unwrap());

            if node.is_dir() {
                match fs::readdir(node) {
                    Ok(children) => {
                        for child in sort_paths(&children).iter() {
                            add_node(state, child, Some(&iter));
                        }
                    },
                    Err(e) => println!("Error updating tree: {}", e)
                }
            }
        }
    }

    iter.drop();
}

fn expand_nodes(
    state: &mut ::utils::State,
    tree: &mut gtk::TreeView,
    parent: Option<&gtk::TreeIter>)
{
    let mut iter = gtk::TreeIter::new().unwrap();

    if state.tree_model.iter_children(&mut iter, parent) {
        loop {
            if let Some(path_str) =
                state.tree_model.get_value(&iter, 1).get_string()
            {
                if let Some(selection_str) = state.selection.clone() {
                    if path_str == selection_str {
                        if let Some(path) = state.tree_model.get_path(&iter) {
                            tree.set_cursor(&path, None, false);
                        }
                    }
                }

                if state.expansions.contains(&path_str) {
                    if let Some(path) = state.tree_model.get_path(&iter) {
                        tree.expand_row(&path, false);
                        expand_nodes(state, tree, Some(&iter));
                    }
                }
            }

            if !state.tree_model.iter_next(&mut iter) {
                break;
            }
        }
    }

    iter.drop();
}

pub fn update_project_tree(
    state: &mut ::utils::State,
    tree: &mut gtk::TreeView)
{
    state.tree_store.clear();

    for path in sort_string_paths(&state.projects).iter() {
        add_node(state, path, None);
    }

    expand_nodes(state, tree, None);

    let mut iter = gtk::TreeIter::new().unwrap();
    if !state.tree_selection.get_selected(state.tree_model, &mut iter) {
        if let Some(path) = get_first_path(state) {
            tree.set_cursor(&path, None, false)
        }
    }
    iter.drop();

    update_project_buttons(state);
}
<|endoftext|>