<commit_before>//! # Exception handler system

use memory::MemoryController;
use x86_64::PrivilegeLevel;
use x86_64::structures::tss::TaskStateSegment;
use x86_64::structures::idt::Idt;
use spin::Once;

mod gdt;
mod ipi;
mod irq;
mod exceptions;

const DOUBLE_FAULT_IST_INDEX: usize = 0;

// The IDT is allocated statically to ensure that this stays in memory until the end of the kernel
// execution.
lazy_static! {
    static ref IDT: Idt = {
        // create a new IDT structure
        let mut idt = Idt::new();

        // Set up exceptions
        idt.divide_by_zero.set_handler_fn(exceptions::divide_by_zero);
        idt.debug.set_handler_fn(exceptions::debug);
        idt.non_maskable_interrupt.set_handler_fn(exceptions::non_maskable);
        idt.overflow.set_handler_fn(exceptions::overflow);
        idt.breakpoint.set_handler_fn(exceptions::breakpoint);
        idt.bound_range_exceeded.set_handler_fn(exceptions::bound_range_exceeded);
        idt.invalid_opcode.set_handler_fn(exceptions::invalid_opcode);
        idt.device_not_available.set_handler_fn(exceptions::device_not_available);
        unsafe {
            idt.double_fault.set_handler_fn(exceptions::double_fault)
                .set_stack_index(DOUBLE_FAULT_IST_INDEX as u16);
        }
        // 9 the coprocessor_segment_overrun is a discontinued exception
        idt.invalid_tss.set_handler_fn(exceptions::invalid_tss);
        idt.segment_not_present.set_handler_fn(exceptions::segment_not_present);
        idt.stack_segment_fault.set_handler_fn(exceptions::stack_segment_fault);
        idt.general_protection_fault.set_handler_fn(exceptions::general_protection_fault);
        idt.page_fault.set_handler_fn(exceptions::page_fault);
        // 15 reserved
        idt.x87_floating_point.set_handler_fn(exceptions::x87_floating_point);
        idt.alignment_check.set_handler_fn(exceptions::alignment_check);
        idt.machine_check.set_handler_fn(exceptions::machine_check);
        idt.simd_floating_point.set_handler_fn(exceptions::simd_floating_point);
        idt.virtualization.set_handler_fn(exceptions::virtualization);
        // 21 through 29 reserved
        idt.security_exception.set_handler_fn(exceptions::security_exception);
        // 31 reserved

        // set timer interrupt
        idt.interrupts[0x40].set_handler_fn(irq::timer).set_privilege_level(PrivilegeLevel::Ring3);

        // set IPI handler
        // TODO implement this properly. For this this is just a null interrupt.
        idt.interrupts[64].set_handler_fn(ipi::ipi);

        idt
    };
}

static TSS: Once<TaskStateSegment> = Once::new();
static GDT: Once<gdt::Gdt> = Once::new();

/// Initialize the IDT
pub fn init(memory_controller: &mut MemoryController) {
    use x86_64::instructions::segmentation::set_cs;
    use x86_64::instructions::tables::load_tss;
    use x86_64::VirtualAddress;
    use x86_64::structures::gdt::SegmentSelector;

    // allocate a double fault stack
    let double_fault_stack = memory_controller.alloc_stack(1).expect("could not allocate double fault stack");

    // configure the task state segment
    let tss = TSS.call_once(|| {
        let mut tss = TaskStateSegment::new();
        tss.interrupt_stack_table[DOUBLE_FAULT_IST_INDEX] = VirtualAddress(double_fault_stack.top());
        tss
    });

    // configure GDT
    let mut code_selector = SegmentSelector(0);
    let mut tss_selector = SegmentSelector(0);
    let gdt = GDT.call_once(|| {
        let mut gdt = gdt::Gdt::new();
        code_selector = gdt.add_entry(gdt::Descriptor::kernel_code_segment());
        tss_selector = gdt.add_entry(gdt::Descriptor::tss_segment(&tss));
        gdt
    });
    gdt.load();

    unsafe {
        // reload code segment register
        set_cs(code_selector);

        // load TSS
        load_tss(tss_selector);
    }

    // load the IDT table into the CPU
    IDT.load();
}
<commit_msg>#30 Context: Add some arch functions needed for the context switch<commit_after>//! # Exception handler system

use memory::MemoryController;
use x86_64::PrivilegeLevel;
use x86_64::structures::tss::TaskStateSegment;
use x86_64::structures::idt::Idt;
use spin::Once;

mod gdt;
mod ipi;
mod irq;
mod exceptions;

const DOUBLE_FAULT_IST_INDEX: usize = 0;

// The IDT is allocated statically to ensure that this stays in memory until the end of the kernel
// execution.
lazy_static! {
    static ref IDT: Idt = {
        // create a new IDT structure
        let mut idt = Idt::new();

        // Set up exceptions
        idt.divide_by_zero.set_handler_fn(exceptions::divide_by_zero);
        idt.debug.set_handler_fn(exceptions::debug);
        idt.non_maskable_interrupt.set_handler_fn(exceptions::non_maskable);
        idt.overflow.set_handler_fn(exceptions::overflow);
        idt.breakpoint.set_handler_fn(exceptions::breakpoint);
        idt.bound_range_exceeded.set_handler_fn(exceptions::bound_range_exceeded);
        idt.invalid_opcode.set_handler_fn(exceptions::invalid_opcode);
        idt.device_not_available.set_handler_fn(exceptions::device_not_available);
        unsafe {
            idt.double_fault.set_handler_fn(exceptions::double_fault)
                .set_stack_index(DOUBLE_FAULT_IST_INDEX as u16);
        }
        // 9 the coprocessor_segment_overrun is a discontinued exception
        idt.invalid_tss.set_handler_fn(exceptions::invalid_tss);
        idt.segment_not_present.set_handler_fn(exceptions::segment_not_present);
        idt.stack_segment_fault.set_handler_fn(exceptions::stack_segment_fault);
        idt.general_protection_fault.set_handler_fn(exceptions::general_protection_fault);
        idt.page_fault.set_handler_fn(exceptions::page_fault);
        // 15 reserved
        idt.x87_floating_point.set_handler_fn(exceptions::x87_floating_point);
        idt.alignment_check.set_handler_fn(exceptions::alignment_check);
        idt.machine_check.set_handler_fn(exceptions::machine_check);
        idt.simd_floating_point.set_handler_fn(exceptions::simd_floating_point);
        idt.virtualization.set_handler_fn(exceptions::virtualization);
        // 21 through 29 reserved
        idt.security_exception.set_handler_fn(exceptions::security_exception);
        // 31 reserved

        // set timer interrupt
        idt.interrupts[0x40].set_handler_fn(irq::timer).set_privilege_level(PrivilegeLevel::Ring3);

        // set IPI handler
        // TODO implement this properly. For this this is just a null interrupt.
        idt.interrupts[64].set_handler_fn(ipi::ipi);

        idt
    };
}

static TSS: Once<TaskStateSegment> = Once::new();
static GDT: Once<gdt::Gdt> = Once::new();

/// Initialize the IDT
pub fn init(memory_controller: &mut MemoryController) {
    use x86_64::instructions::segmentation::set_cs;
    use x86_64::instructions::tables::load_tss;
    use x86_64::VirtualAddress;
    use x86_64::structures::gdt::SegmentSelector;

    // allocate a double fault stack
    let double_fault_stack = memory_controller.alloc_stack(1).expect("could not allocate double fault stack");

    // configure the task state segment
    let tss = TSS.call_once(|| {
        let mut tss = TaskStateSegment::new();
        tss.interrupt_stack_table[DOUBLE_FAULT_IST_INDEX] = VirtualAddress(double_fault_stack.top());
        tss
    });

    // configure GDT
    let mut code_selector = SegmentSelector(0);
    let mut tss_selector = SegmentSelector(0);
    let gdt = GDT.call_once(|| {
        let mut gdt = gdt::Gdt::new();
        code_selector = gdt.add_entry(gdt::Descriptor::kernel_code_segment());
        tss_selector = gdt.add_entry(gdt::Descriptor::tss_segment(&tss));
        gdt
    });
    gdt.load();

    unsafe {
        // reload code segment register
        set_cs(code_selector);

        // load TSS
        load_tss(tss_selector);
    }

    // load the IDT table into the CPU
    IDT.load();
}

/// Clear interrupts.
#[inline(always)]
pub unsafe fn disable() {
    asm!("cli" : : : : "intel", "volatile");
}

/// Set interrupts.
#[inline(always)]
pub unsafe fn enable() {
    asm!("sti" : : : : "intel", "volatile");
}

/// Set interrupt and halt.
///
/// This will wait for the next interrupt.
#[inline(always)]
pub unsafe fn enable_and_halt() {
    asm!("sti
        hlt" : : : : "intel", "volatile");
}

/// Set interrupts and nop.
///
/// This will enable interrupts and allow the IF flag to be processed.
/// Simply enabling interrupts does not guarantee that they will trigger, use this instead!
#[inline(always)]
pub unsafe fn enable_and_nop() {
    asm!("sti
        nop" : : : : "intel" "volatile");
}

/// Pause instruction
#[inline(always)]
pub fn pause() {
    unsafe { asm!("pause" : : : : "intel", "volatile"); }
}
<|endoftext|>


<commit_before>use std::iter::IntoIterator;
use std::str::FromStr;

use crate::error::Result;
use crate::map::Map;
use crate::source::AsyncSource;
use crate::{config::Config, path::Expression, source::Source, value::Value};

/// A configuration builder
///
/// It registers ordered sources of configuration to later build consistent [`Config`] from them.
/// Configuration sources it defines are defaults, [`Source`]s and overrides.
///
/// Defaults are alaways loaded first and can be overwritten by any of two other sources.
/// Overrides are always loaded last, thus cannot be overridden.
/// Both can be only set explicitly key by key in code
/// using [`set_default`](Self::set_default) or [`set_override`](Self::set_override).
///
/// An intermediate category, [`Source`], set groups of keys at once implicitly using data coming from external sources
/// like files, environment variables or others that one implements. Defining a [`Source`] is as simple as implementing
/// a trait for a struct.
///
/// Adding sources, setting defaults and overrides does not invoke any I/O nor builds a config.
/// It happens on demand when [`build`](Self::build) (or its alternative) is called.
/// Therefore all errors, related to any of the [`Source`] will only show up then.
///
/// # Sync and async builder
///
/// [`ConfigBuilder`] uses type parameter to keep track of builder state.
///
/// In [`DefaultState`] builder only supports [`Source`]s
///
/// In [`AsyncState`] it supports both [`Source`]s and [`AsyncSource`]s at the price of building using `async fn`.
///
/// # Examples
///
/// ```rust
/// # use config::*;
/// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// let mut builder = Config::builder()
///     .set_default("default", "1")?
///     .add_source(File::new("config/settings", FileFormat::Json))
/// //  .add_async_source(...)
///     .set_override("override", "1")?;
///
/// match builder.build() {
///     Ok(config) => {
///         // use your config
///     },
///     Err(e) => {
///         // something went wrong
///     }
/// }
/// # Ok(())
/// # }
/// ```
///
/// If any [`AsyncSource`] is used, the builder will transition to [`AsyncState`].
/// In such case, it is required to _await_ calls to [`build`](Self::build) and its non-consuming sibling.
///
/// Calls can be not chained as well
/// ```rust
/// # use std::error::Error;
/// # use config::*;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// let mut builder = Config::builder();
/// builder = builder.set_default("default", "1")?;
/// builder = builder.add_source(File::new("config/settings", FileFormat::Json));
/// builder = builder.add_source(File::new("config/settings.prod", FileFormat::Json));
/// builder = builder.set_override("override", "1")?;
/// # Ok(())
/// # }
/// ```
///
/// Calling [`Config::builder`](Config::builder) yields builder in the default state.
/// If having an asynchronous state as the initial state is desired, _turbofish_ notation needs to be used.
/// ```rust
/// # use config::{*, builder::AsyncState};
/// let mut builder = ConfigBuilder::<AsyncState>::default();
/// ```
///
/// If for some reason acquiring builder in default state is required without calling [`Config::builder`](Config::builder)
/// it can also be achieved.
/// ```rust
/// # use config::{*, builder::DefaultState};
/// let mut builder = ConfigBuilder::<DefaultState>::default();
/// ```
#[derive(Debug, Clone, Default)]
pub struct ConfigBuilder<St: BuilderState> {
    defaults: Map<Expression, Value>,
    overrides: Map<Expression, Value>,
    state: St,
}

/// Represents [`ConfigBuilder`] state.
pub trait BuilderState {}

/// Represents data specific to builder in default, sychronous state, without support for async.
#[derive(Debug, Default)]
pub struct DefaultState {
    sources: Vec<Box<dyn Source + Send + Sync>>,
}

/// The asynchronous configuration builder.
///
/// Similar to a [`ConfigBuilder`] it maintains a set of defaults, a set of sources, and overrides.
///
/// Defaults do not override anything, sources override defaults, and overrides override anything else.
/// Within those three groups order of adding them at call site matters - entities added later take precedence.
///
/// For more detailed description and examples see [`ConfigBuilder`].
/// [`AsyncConfigBuilder`] is just an extension of it that takes async functions into account.
///
/// To obtain a [`Config`] call [`build`](AsyncConfigBuilder::build) or [`build_cloned`](AsyncConfigBuilder::build_cloned)
///
/// # Example
/// Since this library does not implement any [`AsyncSource`] an example in rustdocs cannot be given.
/// Detailed explanation about why such a source is not implemented is in [`AsyncSource`]'s documentation.
///
/// Refer to [`ConfigBuilder`] for similar API sample usage or to the examples folder of the crate, where such a source is implemented.
#[derive(Debug, Clone, Default)]
pub struct AsyncConfigBuilder {}

/// Represents data specific to builder in asychronous state, with support for async.
#[derive(Debug, Default)]
pub struct AsyncState {
    sources: Vec<SourceType>,
}

#[derive(Debug, Clone)]
enum SourceType {
    Sync(Box<dyn Source + Send + Sync>),
    Async(Box<dyn AsyncSource + Send + Sync>),
}

impl BuilderState for DefaultState {}
impl BuilderState for AsyncState {}

impl<St: BuilderState> ConfigBuilder<St> {
    // operations allowed in any state

    /// Set a default `value` at `key`
    ///
    /// This value can be overwritten by any [`Source`], [`AsyncSource`] or override.
    ///
    /// # Errors
    ///
    /// Fails if `Expression::from_str(key)` fails.
    pub fn set_default<S, T>(mut self, key: S, value: T) -> Result<Self>
    where
        S: AsRef<str>,
        T: Into<Value>,
    {
        self.defaults
            .insert(Expression::from_str(key.as_ref())?, value.into());
        Ok(self)
    }

    /// Set an override
    ///
    /// This function sets an overwrite value. It will not be altered by any default, [`Source`] nor [`AsyncSource`]
    ///
    /// # Errors
    ///
    /// Fails if `Expression::from_str(key)` fails.
    pub fn set_override<S, T>(mut self, key: S, value: T) -> Result<Self>
    where
        S: AsRef<str>,
        T: Into<Value>,
    {
        self.overrides
            .insert(Expression::from_str(key.as_ref())?, value.into());
        Ok(self)
    }

    /// Sets an override if value is Some(_)
    ///
    /// This function sets an overwrite value if Some(_) is passed. If None is passed, this function does nothing.
    /// It will not be altered by any default, [`Source`] nor [`AsyncSource`]
    ///
    /// # Errors
    ///
    /// Fails if `Expression::from_str(key)` fails.
    pub fn set_override_option<S, T>(mut self, key: S, value: Option<T>) -> Result<Self>
    where
        S: AsRef<str>,
        T: Into<Value>,
    {
        if let Some(value) = value {
            self.overrides
                .insert(Expression::from_str(key.as_ref())?, value.into());
        }
        Ok(self)
    }
}

impl ConfigBuilder<DefaultState> {
    // operations allowed in sync state

    /// Registers new [`Source`] in this builder.
    ///
    /// Calling this method does not invoke any I/O. [`Source`] is only saved in internal register for later use.
    #[must_use]
    pub fn add_source<T>(mut self, source: T) -> Self
    where
        T: Source + Send + Sync + 'static,
    {
        self.state.sources.push(Box::new(source));
        self
    }

    /// Registers new [`AsyncSource`] in this builder and forces transition to [`AsyncState`].
    ///
    /// Calling this method does not invoke any I/O. [`AsyncSource`] is only saved in internal register for later use.
    pub fn add_async_source<T>(self, source: T) -> ConfigBuilder<AsyncState>
    where
        T: AsyncSource + Send + Sync + 'static,
    {
        let async_state = ConfigBuilder {
            state: AsyncState {
                sources: self
                    .state
                    .sources
                    .into_iter()
                    .map(|s| SourceType::Sync(s))
                    .collect(),
            },
            defaults: self.defaults,
            overrides: self.overrides,
        };

        async_state.add_async_source(source)
    }

    /// Reads all registered [`Source`]s.
    ///
    /// This is the method that invokes all I/O operations.
    /// For a non consuming alternative see [`build_cloned`](Self::build_cloned)
    ///
    /// # Errors
    /// If source collection fails, be it technical reasons or related to inability to read data as `Config` for different reasons,
    /// this method returns error.
    pub fn build(self) -> Result<Config> {
        Self::build_internal(self.defaults, self.overrides, &self.state.sources)
    }

    /// Reads all registered [`Source`]s.
    ///
    /// Similar to [`build`](Self::build), but it does not take ownership of `ConfigBuilder` to allow later reuse.
    /// Internally it clones data to achieve it.
    ///
    /// # Errors
    /// If source collection fails, be it technical reasons or related to inability to read data as `Config` for different reasons,
    /// this method returns error.
    pub fn build_cloned(&self) -> Result<Config> {
        Self::build_internal(
            self.defaults.clone(),
            self.overrides.clone(),
            &self.state.sources,
        )
    }

    fn build_internal(
        defaults: Map<Expression, Value>,
        overrides: Map<Expression, Value>,
        sources: &[Box<dyn Source + Send + Sync>],
    ) -> Result<Config> {
        let mut cache: Value = Map::<String, Value>::new().into();

        // Add defaults
        for (key, val) in defaults {
            key.set(&mut cache, val);
        }

        // Add sources
        sources.collect_to(&mut cache)?;

        // Add overrides
        for (key, val) in overrides {
            key.set(&mut cache, val);
        }

        Ok(Config::new(cache))
    }
}

impl ConfigBuilder<AsyncState> {
    // operations allowed in async state

    /// Registers new [`Source`] in this builder.
    ///
    /// Calling this method does not invoke any I/O. [`Source`] is only saved in internal register for later use.
    #[must_use]
    pub fn add_source<T>(mut self, source: T) -> Self
    where
        T: Source + Send + Sync + 'static,
    {
        self.state.sources.push(SourceType::Sync(Box::new(source)));
        self
    }

    /// Registers new [`AsyncSource`] in this builder.
    ///
    /// Calling this method does not invoke any I/O. [`AsyncSource`] is only saved in internal register for later use.
    #[must_use]
    pub fn add_async_source<T>(mut self, source: T) -> Self
    where
        T: AsyncSource + Send + Sync + 'static,
    {
        self.state.sources.push(SourceType::Async(Box::new(source)));
        self
    }

    /// Reads all registered defaults, [`Source`]s, [`AsyncSource`]s and overrides.
    ///
    /// This is the method that invokes all I/O operations.
    /// For a non consuming alternative see [`build_cloned`](Self::build_cloned)
    ///
    /// # Errors
    /// If source collection fails, be it technical reasons or related to inability to read data as `Config` for different reasons,
    /// this method returns error.
    pub async fn build(self) -> Result<Config> {
        Self::build_internal(self.defaults, self.overrides, &self.state.sources).await
    }

    /// Reads all registered defaults, [`Source`]s, [`AsyncSource`]s and overrides.
    ///
    /// Similar to [`build`](Self::build), but it does not take ownership of `ConfigBuilder` to allow later reuse.
    /// Internally it clones data to achieve it.
    ///
    /// # Errors
    /// If source collection fails, be it technical reasons or related to inability to read data as `Config` for different reasons,
    /// this method returns error.
    pub async fn build_cloned(&self) -> Result<Config> {
        Self::build_internal(
            self.defaults.clone(),
            self.overrides.clone(),
            &self.state.sources,
        )
        .await
    }

    async fn build_internal(
        defaults: Map<Expression, Value>,
        overrides: Map<Expression, Value>,
        sources: &[SourceType],
    ) -> Result<Config> {
        let mut cache: Value = Map::<String, Value>::new().into();

        // Add defaults
        for (key, val) in defaults {
            key.set(&mut cache, val);
        }

        for source in sources.iter() {
            match source {
                SourceType::Sync(source) => source.collect_to(&mut cache)?,
                SourceType::Async(source) => source.collect_to(&mut cache).await?,
            }
        }

        // Add overrides
        for (key, val) in overrides {
            key.set(&mut cache, val);
        }

        Ok(Config::new(cache))
    }
}
<commit_msg>Fixed type in ConfigBuilder docs<commit_after>use std::iter::IntoIterator;
use std::str::FromStr;

use crate::error::Result;
use crate::map::Map;
use crate::source::AsyncSource;
use crate::{config::Config, path::Expression, source::Source, value::Value};

/// A configuration builder
///
/// It registers ordered sources of configuration to later build consistent [`Config`] from them.
/// Configuration sources it defines are defaults, [`Source`]s and overrides.
///
/// Defaults are always loaded first and can be overwritten by any of two other sources.
/// Overrides are always loaded last, thus cannot be overridden.
/// Both can be only set explicitly key by key in code
/// using [`set_default`](Self::set_default) or [`set_override`](Self::set_override).
///
/// An intermediate category, [`Source`], set groups of keys at once implicitly using data coming from external sources
/// like files, environment variables or others that one implements. Defining a [`Source`] is as simple as implementing
/// a trait for a struct.
///
/// Adding sources, setting defaults and overrides does not invoke any I/O nor builds a config.
/// It happens on demand when [`build`](Self::build) (or its alternative) is called.
/// Therefore all errors, related to any of the [`Source`] will only show up then.
///
/// # Sync and async builder
///
/// [`ConfigBuilder`] uses type parameter to keep track of builder state.
///
/// In [`DefaultState`] builder only supports [`Source`]s
///
/// In [`AsyncState`] it supports both [`Source`]s and [`AsyncSource`]s at the price of building using `async fn`.
///
/// # Examples
///
/// ```rust
/// # use config::*;
/// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// let mut builder = Config::builder()
///     .set_default("default", "1")?
///     .add_source(File::new("config/settings", FileFormat::Json))
/// //  .add_async_source(...)
///     .set_override("override", "1")?;
///
/// match builder.build() {
///     Ok(config) => {
///         // use your config
///     },
///     Err(e) => {
///         // something went wrong
///     }
/// }
/// # Ok(())
/// # }
/// ```
///
/// If any [`AsyncSource`] is used, the builder will transition to [`AsyncState`].
/// In such case, it is required to _await_ calls to [`build`](Self::build) and its non-consuming sibling.
///
/// Calls can be not chained as well
/// ```rust
/// # use std::error::Error;
/// # use config::*;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// let mut builder = Config::builder();
/// builder = builder.set_default("default", "1")?;
/// builder = builder.add_source(File::new("config/settings", FileFormat::Json));
/// builder = builder.add_source(File::new("config/settings.prod", FileFormat::Json));
/// builder = builder.set_override("override", "1")?;
/// # Ok(())
/// # }
/// ```
///
/// Calling [`Config::builder`](Config::builder) yields builder in the default state.
/// If having an asynchronous state as the initial state is desired, _turbofish_ notation needs to be used.
/// ```rust
/// # use config::{*, builder::AsyncState};
/// let mut builder = ConfigBuilder::<AsyncState>::default();
/// ```
///
/// If for some reason acquiring builder in default state is required without calling [`Config::builder`](Config::builder)
/// it can also be achieved.
/// ```rust
/// # use config::{*, builder::DefaultState};
/// let mut builder = ConfigBuilder::<DefaultState>::default();
/// ```
#[derive(Debug, Clone, Default)]
pub struct ConfigBuilder<St: BuilderState> {
    defaults: Map<Expression, Value>,
    overrides: Map<Expression, Value>,
    state: St,
}

/// Represents [`ConfigBuilder`] state.
pub trait BuilderState {}

/// Represents data specific to builder in default, sychronous state, without support for async.
#[derive(Debug, Default)]
pub struct DefaultState {
    sources: Vec<Box<dyn Source + Send + Sync>>,
}

/// The asynchronous configuration builder.
///
/// Similar to a [`ConfigBuilder`] it maintains a set of defaults, a set of sources, and overrides.
///
/// Defaults do not override anything, sources override defaults, and overrides override anything else.
/// Within those three groups order of adding them at call site matters - entities added later take precedence.
///
/// For more detailed description and examples see [`ConfigBuilder`].
/// [`AsyncConfigBuilder`] is just an extension of it that takes async functions into account.
///
/// To obtain a [`Config`] call [`build`](AsyncConfigBuilder::build) or [`build_cloned`](AsyncConfigBuilder::build_cloned)
///
/// # Example
/// Since this library does not implement any [`AsyncSource`] an example in rustdocs cannot be given.
/// Detailed explanation about why such a source is not implemented is in [`AsyncSource`]'s documentation.
///
/// Refer to [`ConfigBuilder`] for similar API sample usage or to the examples folder of the crate, where such a source is implemented.
#[derive(Debug, Clone, Default)]
pub struct AsyncConfigBuilder {}

/// Represents data specific to builder in asychronous state, with support for async.
#[derive(Debug, Default)]
pub struct AsyncState {
    sources: Vec<SourceType>,
}

#[derive(Debug, Clone)]
enum SourceType {
    Sync(Box<dyn Source + Send + Sync>),
    Async(Box<dyn AsyncSource + Send + Sync>),
}

impl BuilderState for DefaultState {}
impl BuilderState for AsyncState {}

impl<St: BuilderState> ConfigBuilder<St> {
    // operations allowed in any state

    /// Set a default `value` at `key`
    ///
    /// This value can be overwritten by any [`Source`], [`AsyncSource`] or override.
    ///
    /// # Errors
    ///
    /// Fails if `Expression::from_str(key)` fails.
    pub fn set_default<S, T>(mut self, key: S, value: T) -> Result<Self>
    where
        S: AsRef<str>,
        T: Into<Value>,
    {
        self.defaults
            .insert(Expression::from_str(key.as_ref())?, value.into());
        Ok(self)
    }

    /// Set an override
    ///
    /// This function sets an overwrite value. It will not be altered by any default, [`Source`] nor [`AsyncSource`]
    ///
    /// # Errors
    ///
    /// Fails if `Expression::from_str(key)` fails.
    pub fn set_override<S, T>(mut self, key: S, value: T) -> Result<Self>
    where
        S: AsRef<str>,
        T: Into<Value>,
    {
        self.overrides
            .insert(Expression::from_str(key.as_ref())?, value.into());
        Ok(self)
    }

    /// Sets an override if value is Some(_)
    ///
    /// This function sets an overwrite value if Some(_) is passed. If None is passed, this function does nothing.
    /// It will not be altered by any default, [`Source`] nor [`AsyncSource`]
    ///
    /// # Errors
    ///
    /// Fails if `Expression::from_str(key)` fails.
    pub fn set_override_option<S, T>(mut self, key: S, value: Option<T>) -> Result<Self>
    where
        S: AsRef<str>,
        T: Into<Value>,
    {
        if let Some(value) = value {
            self.overrides
                .insert(Expression::from_str(key.as_ref())?, value.into());
        }
        Ok(self)
    }
}

impl ConfigBuilder<DefaultState> {
    // operations allowed in sync state

    /// Registers new [`Source`] in this builder.
    ///
    /// Calling this method does not invoke any I/O. [`Source`] is only saved in internal register for later use.
    #[must_use]
    pub fn add_source<T>(mut self, source: T) -> Self
    where
        T: Source + Send + Sync + 'static,
    {
        self.state.sources.push(Box::new(source));
        self
    }

    /// Registers new [`AsyncSource`] in this builder and forces transition to [`AsyncState`].
    ///
    /// Calling this method does not invoke any I/O. [`AsyncSource`] is only saved in internal register for later use.
    pub fn add_async_source<T>(self, source: T) -> ConfigBuilder<AsyncState>
    where
        T: AsyncSource + Send + Sync + 'static,
    {
        let async_state = ConfigBuilder {
            state: AsyncState {
                sources: self
                    .state
                    .sources
                    .into_iter()
                    .map(|s| SourceType::Sync(s))
                    .collect(),
            },
            defaults: self.defaults,
            overrides: self.overrides,
        };

        async_state.add_async_source(source)
    }

    /// Reads all registered [`Source`]s.
    ///
    /// This is the method that invokes all I/O operations.
    /// For a non consuming alternative see [`build_cloned`](Self::build_cloned)
    ///
    /// # Errors
    /// If source collection fails, be it technical reasons or related to inability to read data as `Config` for different reasons,
    /// this method returns error.
    pub fn build(self) -> Result<Config> {
        Self::build_internal(self.defaults, self.overrides, &self.state.sources)
    }

    /// Reads all registered [`Source`]s.
    ///
    /// Similar to [`build`](Self::build), but it does not take ownership of `ConfigBuilder` to allow later reuse.
    /// Internally it clones data to achieve it.
    ///
    /// # Errors
    /// If source collection fails, be it technical reasons or related to inability to read data as `Config` for different reasons,
    /// this method returns error.
    pub fn build_cloned(&self) -> Result<Config> {
        Self::build_internal(
            self.defaults.clone(),
            self.overrides.clone(),
            &self.state.sources,
        )
    }

    fn build_internal(
        defaults: Map<Expression, Value>,
        overrides: Map<Expression, Value>,
        sources: &[Box<dyn Source + Send + Sync>],
    ) -> Result<Config> {
        let mut cache: Value = Map::<String, Value>::new().into();

        // Add defaults
        for (key, val) in defaults {
            key.set(&mut cache, val);
        }

        // Add sources
        sources.collect_to(&mut cache)?;

        // Add overrides
        for (key, val) in overrides {
            key.set(&mut cache, val);
        }

        Ok(Config::new(cache))
    }
}

impl ConfigBuilder<AsyncState> {
    // operations allowed in async state

    /// Registers new [`Source`] in this builder.
    ///
    /// Calling this method does not invoke any I/O. [`Source`] is only saved in internal register for later use.
    #[must_use]
    pub fn add_source<T>(mut self, source: T) -> Self
    where
        T: Source + Send + Sync + 'static,
    {
        self.state.sources.push(SourceType::Sync(Box::new(source)));
        self
    }

    /// Registers new [`AsyncSource`] in this builder.
    ///
    /// Calling this method does not invoke any I/O. [`AsyncSource`] is only saved in internal register for later use.
    #[must_use]
    pub fn add_async_source<T>(mut self, source: T) -> Self
    where
        T: AsyncSource + Send + Sync + 'static,
    {
        self.state.sources.push(SourceType::Async(Box::new(source)));
        self
    }

    /// Reads all registered defaults, [`Source`]s, [`AsyncSource`]s and overrides.
    ///
    /// This is the method that invokes all I/O operations.
    /// For a non consuming alternative see [`build_cloned`](Self::build_cloned)
    ///
    /// # Errors
    /// If source collection fails, be it technical reasons or related to inability to read data as `Config` for different reasons,
    /// this method returns error.
    pub async fn build(self) -> Result<Config> {
        Self::build_internal(self.defaults, self.overrides, &self.state.sources).await
    }

    /// Reads all registered defaults, [`Source`]s, [`AsyncSource`]s and overrides.
    ///
    /// Similar to [`build`](Self::build), but it does not take ownership of `ConfigBuilder` to allow later reuse.
    /// Internally it clones data to achieve it.
    ///
    /// # Errors
    /// If source collection fails, be it technical reasons or related to inability to read data as `Config` for different reasons,
    /// this method returns error.
    pub async fn build_cloned(&self) -> Result<Config> {
        Self::build_internal(
            self.defaults.clone(),
            self.overrides.clone(),
            &self.state.sources,
        )
        .await
    }

    async fn build_internal(
        defaults: Map<Expression, Value>,
        overrides: Map<Expression, Value>,
        sources: &[SourceType],
    ) -> Result<Config> {
        let mut cache: Value = Map::<String, Value>::new().into();

        // Add defaults
        for (key, val) in defaults {
            key.set(&mut cache, val);
        }

        for source in sources.iter() {
            match source {
                SourceType::Sync(source) => source.collect_to(&mut cache)?,
                SourceType::Async(source) => source.collect_to(&mut cache).await?,
            }
        }

        // Add overrides
        for (key, val) in overrides {
            key.set(&mut cache, val);
        }

        Ok(Config::new(cache))
    }
}
<|endoftext|>


<commit_before>use format::method::Method;
use format::frame::Frame;
use std::collections::VecDeque;
use generated::Class;

#[derive(Clone,Debug,PartialEq)]
pub struct Channel {
  pub id:    u16,
  pub state: ChannelState,
  pub queue: VecDeque<LocalFrame>,
}

#[derive(Clone,Debug,PartialEq,Eq)]
pub enum ChannelState {
  Waiting,
  Error,
}

#[derive(Clone,Debug,PartialEq)]
pub enum LocalFrame {
  Method(Class),
  Header,
  Heartbeat,
  Body(Vec<u8>)
}

impl Channel {
  pub fn new(channel_id: u16) -> Channel {
    Channel {
      id: channel_id,
      state: ChannelState::Waiting,
      queue: VecDeque::new(),
    }
  }

  pub fn global() -> Channel {
    Channel {
      id: 0,
      state: ChannelState::Waiting,
      queue: VecDeque::new(),
    }
  }

  pub fn received_method(&mut self, m: Class) {
    println!("channel[{}] received {:?}", self.id, m);
    //FIXME: handle method here instead of queuing
    self.queue.push_back(LocalFrame::Method(m));
  }
}
<commit_msg>use the generated channel state<commit_after>use format::method::Method;
use format::frame::Frame;
use std::collections::VecDeque;
use generated::Class;
use api::ChannelState;

#[derive(Clone,Debug,PartialEq)]
pub struct Channel {
  pub id:    u16,
  pub state: ChannelState,
  pub queue: VecDeque<LocalFrame>,
}

/*
#[derive(Clone,Debug,PartialEq,Eq)]
pub enum ChannelState {
  Waiting,
  Error,
}
*/

#[derive(Clone,Debug,PartialEq)]
pub enum LocalFrame {
  Method(Class),
  Header,
  Heartbeat,
  Body(Vec<u8>)
}

impl Channel {
  pub fn new(channel_id: u16) -> Channel {
    Channel {
      id: channel_id,
      state: ChannelState::Initial,
      queue: VecDeque::new(),
    }
  }

  pub fn global() -> Channel {
    Channel {
      id: 0,
      state: ChannelState::Initial,
      queue: VecDeque::new(),
    }
  }

  pub fn received_method(&mut self, m: Class) {
    println!("channel[{}] received {:?}", self.id, m);
    //FIXME: handle method here instead of queuing
    self.queue.push_back(LocalFrame::Method(m));
  }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::fmt;

use ast::*;
use ast;
use interpreter::InterpreterError;

#[derive(Copy, Clone, Debug)]
pub enum Type {
    Number,
    Bool,
    Any,
}

impl From<ast::Literal> for Type {
    fn from(from: ast::Literal) -> Self {
        match from {
            ast::Literal::Integer(_) => Type::Number,
            ast::Literal::Float(_) => Type::Number,
            ast::Literal::Bool(_) => Type::Bool,
        }
    }
}

impl fmt::Display for Type {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Type::Number => write!(f, "Number"),
            Type::Bool => write!(f, "Bool"),
            Type::Any => write!(f, "Any"),
        }
    }
}

pub struct TypeEnvironment {
    symbol_tables: Vec<HashMap<String, Type>>,
}

impl TypeEnvironment {
    pub fn new() -> TypeEnvironment {
        TypeEnvironment { symbol_tables: Vec::new() }
    }

    pub fn start_scope(&mut self) {
        self.symbol_tables.push(HashMap::new());
    }

    pub fn end_scope(&mut self) {
        self.symbol_tables.pop();
    }

    pub fn declare(&mut self, variable: &Variable, typ: &Type) {
        match *variable {
            Variable::Identifier(_, ref id) => {
                self.symbol_tables.last_mut().unwrap().insert(id.clone(), *typ);
            }
        };
    }

    pub fn set(&mut self, identifier: &String, typ: Type) -> Result<(), InterpreterError> {
        for table in self.symbol_tables.iter_mut().rev() {
            // TODO: Entry API
            if table.contains_key(identifier) {
                table.insert(identifier.clone(), typ);
                return Ok(());
            }
        }
        Err(InterpreterError::UndeclaredAssignment(identifier.clone()))
    }

    pub fn get_type(&mut self, identifier: &String) -> Result<Type, InterpreterError> {
        for table in self.symbol_tables.iter().rev() {
            if let Some(typ) = table.get(identifier) {
                return Ok(*typ);
            }
        }
        Err(InterpreterError::ReferenceError(identifier.clone()))
    }
}

pub fn check_program(ast: &Vec<Statement>) -> Result<(), Vec<InterpreterError>> {
    let mut env = TypeEnvironment::new();
    env.start_scope();
    let result = check_statements(ast, &mut env);
    env.end_scope();
    result
}

pub fn check_statements(ast: &Vec<Statement>,
                        env: &mut TypeEnvironment)
                        -> Result<(), Vec<InterpreterError>> {
    let mut errors = Vec::new();
    for statement in ast.iter() {
        if let Err(mut e) = check_statement(statement, env) {
            errors.append(&mut e);
        }
    }
    if errors.len() == 0 {
        Ok(())
    } else {
        Err(errors)
    }
}

pub fn check_statement(s: &Statement,
                       env: &mut TypeEnvironment)
                       -> Result<(), Vec<InterpreterError>> {
    let mut errors = Vec::new();
    match *s {
        Statement::VariableDeclaration(ref variable, ref expr) => {
            let checked_type = match check_expr(expr, env) {
                Ok(t) => t,
                Err(mut e) => {
                    errors.append(&mut e);
                    Type::Any
                }
            };
            env.declare(variable, &checked_type);
            // println!("{:?} => {}", variable, checked_type);
        }
        Statement::Assignment(ref lhs_expr, ref expr) => {
            let checked_type = match check_expr(expr, env) {
                Ok(t) => t,
                Err(mut e) => {
                    errors.append(&mut e);
                    Type::Any
                }
            };
            match *lhs_expr {
                LhsExpr::Identifier(ref id) => {
                    if let Err(e) = env.set(id, checked_type) {
                        errors.push(e);
                    }
                }
            };
        }
        Statement::Block(ref statements) => {
            env.start_scope();
            if let Err(mut e) = check_statements(statements, env) {
                errors.append(&mut e);
            }
            env.end_scope();
        }
        Statement::Expression(ref expr) => {
            if let Err(mut e) = check_expr(expr, env) {
                errors.append(&mut e);
            }
        }
        Statement::IfThen(ref if_expr, ref then_block) => {
            if let Err(mut e) = check_expr(if_expr, env) {
                errors.append(&mut e);
            }
            if let Err(mut e) = check_statement(then_block, env) {
                errors.append(&mut e);
            }
        }
        Statement::IfThenElse(ref if_expr, ref then_block, ref else_block) => {
            if let Err(mut e) = check_expr(if_expr, env) {
                errors.append(&mut e);
            }
            if let Err(mut e) = check_statement(then_block, env) {
                errors.append(&mut e);
            }
            if let Err(mut e) = check_statement(else_block, env) {
                errors.append(&mut e);
            }
        }
        Statement::Empty => {}
    };
    if errors.len() == 0 {
        Ok(())
    } else {
        Err(errors)
    }
}

fn check_expr(expr: &Expr, env: &mut TypeEnvironment) -> Result<Type, Vec<InterpreterError>> {
    match *expr {
        Expr::Literal(ref x) => Ok(Type::from(x.clone())),
        Expr::Identifier(ref id) => {
            match env.get_type(&id) {
                Ok(t) => Ok(t),
                Err(e) => Err(vec![e]),
            }
        }
        Expr::UnaryExpression(ref op, ref expr) => {
            match check_expr(expr, env) {
                Ok(t) => {
                    match *op {
                        UnaryOp::Minus => {
                            match check_unary_minus_for_type(t) {
                                Ok(t) => Ok(t),
                                Err(e) => Err(vec![e]),
                            }
                        }
                    }
                }
                Err(e) => Err(e),
            }
        }
        Expr::UnaryLogicalExpression(ref op, ref expr) => {
            match check_expr(expr, env) {
                Ok(_) => {
                    match *op {
                        LogicalUnaryOp::Not => Ok(Type::Bool),
                    }
                }
                Err(e) => Err(e),
            }
        }
        Expr::BinaryExpression(ref expr1, ref op, ref expr2) => {
            let mut errors = Vec::new();
            let checked_type_1 = match check_expr(expr1, env) {
                Ok(t) => t,
                Err(mut e) => {
                    errors.append(&mut e);
                    Type::Any
                }
            };
            let checked_type_2 = match check_expr(expr2, env) {
                Ok(t) => t,
                Err(mut e) => {
                    errors.append(&mut e);
                    Type::Any
                }
            };
            use ast::BinaryOp::*;
            let result = match *op {
                ref op @ Add |
                ref op @ Sub |
                ref op @ Mul |
                ref op @ Div |
                ref op @ FloorDiv => {
                    check_binary_arithmetic_for_types(op.clone(), checked_type_1, checked_type_2)
                }
                ref op @ LessThan |
                ref op @ LessThanOrEqual |
                ref op @ GreaterThan |
                ref op @ GreaterThanOrEqual => {
                    check_binary_comparison_for_types(op.clone(), checked_type_1, checked_type_2)
                }
                StrictEquals => Ok(Type::Bool),
            };
            match result {
                Err(e) => {
                    errors.push(e);
                    Err(errors)
                }
                Ok(t) => {
                    if errors.len() == 0 {
                        Ok(t)
                    } else {
                        Err(errors)
                    }
                }
            }
        }
        Expr::BinaryLogicalExpression(ref expr1, ref op, ref expr2) => {
            let mut errors = Vec::new();
            match *op {
                LogicalBinaryOp::LogicalAnd |
                LogicalBinaryOp::LogicalOr => {
                    if let Err(mut e) = check_expr(expr1, env) {
                        errors.append(&mut e);
                    };
                    if let Err(mut e) = check_expr(expr2, env) {
                        errors.append(&mut e);
                    };
                    if errors.len() == 0 {
                        Ok(Type::Bool)
                    } else {
                        Err(errors)
                    }
                }
            }
        }
    }
}

fn check_unary_minus_for_type(typ: Type) -> Result<Type, InterpreterError> {
    match typ {
        Type::Number => Ok(Type::Number),
        Type::Any => Ok(Type::Any),
        _ => Err(InterpreterError::UnaryTypeError(UnaryOp::Minus, typ)),
    }
}

fn check_binary_arithmetic_for_types(op: BinaryOp,
                                     t1: Type,
                                     t2: Type)
                                     -> Result<Type, InterpreterError> {
    match (t1, t2) {
        (Type::Number, Type::Number) => Ok(Type::Number),
        (Type::Any, _) => Ok(Type::Any),
        (_, Type::Any) => Ok(Type::Any),
        _ => Err(InterpreterError::BinaryTypeError(op, t1, t2)),
    }
}

fn check_binary_comparison_for_types(op: BinaryOp,
                                     t1: Type,
                                     t2: Type)
                                     -> Result<Type, InterpreterError> {
    match (t1, t2) {
        (Type::Number, Type::Number) => Ok(Type::Bool),
        (Type::Any, _) => Ok(Type::Any),
        (_, Type::Any) => Ok(Type::Any),
        _ => Err(InterpreterError::BinaryTypeError(op, t1, t2)),
    }
}
<commit_msg>Handle if/then/else block paths better in type checker<commit_after>use std::collections::HashMap;
use std::collections::HashSet;
use std::fmt;

use ast::*;
use ast;
use interpreter::InterpreterError;

#[derive(Copy, Clone, Debug, PartialEq)]
pub enum Type {
    Number,
    Bool,
    Any,
}

impl From<ast::Literal> for Type {
    fn from(from: ast::Literal) -> Self {
        match from {
            ast::Literal::Integer(_) => Type::Number,
            ast::Literal::Float(_) => Type::Number,
            ast::Literal::Bool(_) => Type::Bool,
        }
    }
}

impl fmt::Display for Type {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Type::Number => write!(f, "Number"),
            Type::Bool => write!(f, "Bool"),
            Type::Any => write!(f, "Any"),
        }
    }
}

#[derive(Clone)]
pub struct TypeEnvironment {
    pub symbol_tables: Vec<HashMap<String, Type>>,
}

impl TypeEnvironment {
    pub fn new() -> TypeEnvironment {
        TypeEnvironment { symbol_tables: Vec::new() }
    }

    pub fn start_scope(&mut self) {
        self.symbol_tables.push(HashMap::new());
    }

    pub fn end_scope(&mut self) {
        self.symbol_tables.pop();
    }

    pub fn declare(&mut self, variable: &Variable, typ: &Type) {
        match *variable {
            Variable::Identifier(_, ref id) => {
                self.symbol_tables.last_mut().unwrap().insert(id.clone(), *typ);
            }
        };
    }

    pub fn set(&mut self, identifier: &String, typ: Type) -> Result<(), InterpreterError> {
        for table in self.symbol_tables.iter_mut().rev() {
            // TODO: Entry API
            if table.contains_key(identifier) {
                table.insert(identifier.clone(), typ);
                return Ok(());
            }
        }
        Err(InterpreterError::UndeclaredAssignment(identifier.clone()))
    }

    pub fn get_type(&mut self, identifier: &String) -> Result<Type, InterpreterError> {
        for table in self.symbol_tables.iter().rev() {
            if let Some(typ) = table.get(identifier) {
                return Ok(*typ);
            }
        }
        Err(InterpreterError::ReferenceError(identifier.clone()))
    }

    pub fn get_all_keys(&self) -> HashSet<String> {
        let mut keys = HashSet::new();
        for table in self.symbol_tables.iter() {
            for key in table.keys() {
                keys.insert(key.clone());
            }
        }
        keys
    }
}

pub fn check_program(ast: &Vec<Statement>) -> Result<(), Vec<InterpreterError>> {
    let mut env = TypeEnvironment::new();
    env.start_scope();
    let result = check_statements(ast, &mut env);
    env.end_scope();
    result
}

pub fn check_statements(ast: &Vec<Statement>,
                        env: &mut TypeEnvironment)
                        -> Result<(), Vec<InterpreterError>> {
    let mut errors = Vec::new();
    for statement in ast.iter() {
        if let Err(mut e) = check_statement(statement, env) {
            errors.append(&mut e);
        }
    }
    if errors.len() == 0 {
        Ok(())
    } else {
        Err(errors)
    }
}

pub fn check_statement(s: &Statement,
                       env: &mut TypeEnvironment)
                       -> Result<(), Vec<InterpreterError>> {
    let mut errors = Vec::new();
    match *s {
        Statement::VariableDeclaration(ref variable, ref expr) => {
            let checked_type = match check_expr(expr, env) {
                Ok(t) => t,
                Err(mut e) => {
                    errors.append(&mut e);
                    Type::Any
                }
            };
            env.declare(variable, &checked_type);
            // println!("{:?} => {}", variable, checked_type);
        }
        Statement::Assignment(ref lhs_expr, ref expr) => {
            let checked_type = match check_expr(expr, env) {
                Ok(t) => t,
                Err(mut e) => {
                    errors.append(&mut e);
                    Type::Any
                }
            };
            match *lhs_expr {
                LhsExpr::Identifier(ref id) => {
                    if let Err(e) = env.set(id, checked_type) {
                        errors.push(e);
                    }
                }
            };
        }
        Statement::Block(ref statements) => {
            env.start_scope();
            if let Err(mut e) = check_statements(statements, env) {
                errors.append(&mut e);
            }
            env.end_scope();
        }
        Statement::Expression(ref expr) => {
            if let Err(mut e) = check_expr(expr, env) {
                errors.append(&mut e);
            }
        }
        Statement::IfThen(ref if_expr, ref then_block) => {
            if let Err(mut e) = check_expr(if_expr, env) {
                errors.append(&mut e);
            }
            if let Err(mut e) = check_statement(then_block, env) {
                errors.append(&mut e);
            }
        }
        Statement::IfThenElse(ref if_expr, ref then_block, ref else_block) => {
            let mut then_env = env.clone();
            let mut else_env = env.clone();
            if let Err(mut e) = check_expr(if_expr, env) {
                errors.append(&mut e);
            }
            if let Err(mut e) = check_statement(then_block, &mut then_env) {
                errors.append(&mut e);
            }
            if let Err(mut e) = check_statement(else_block, &mut else_env) {
                errors.append(&mut e);
            }

            for name in then_env.get_all_keys() {
                let then_type = then_env.get_type(&name).unwrap();
                if else_env.get_type(&name).unwrap() != then_type {
                    env.set(&name, Type::Any).unwrap();
                } else {
                    env.set(&name, then_type).unwrap();
                }
            }
        }
        Statement::Empty => {}
    };
    if errors.len() == 0 {
        Ok(())
    } else {
        Err(errors)
    }
}

fn check_expr(expr: &Expr, env: &mut TypeEnvironment) -> Result<Type, Vec<InterpreterError>> {
    match *expr {
        Expr::Literal(ref x) => Ok(Type::from(x.clone())),
        Expr::Identifier(ref id) => {
            match env.get_type(&id) {
                Ok(t) => Ok(t),
                Err(e) => Err(vec![e]),
            }
        }
        Expr::UnaryExpression(ref op, ref expr) => {
            match check_expr(expr, env) {
                Ok(t) => {
                    match *op {
                        UnaryOp::Minus => {
                            match check_unary_minus_for_type(t) {
                                Ok(t) => Ok(t),
                                Err(e) => Err(vec![e]),
                            }
                        }
                    }
                }
                Err(e) => Err(e),
            }
        }
        Expr::UnaryLogicalExpression(ref op, ref expr) => {
            match check_expr(expr, env) {
                Ok(_) => {
                    match *op {
                        LogicalUnaryOp::Not => Ok(Type::Bool),
                    }
                }
                Err(e) => Err(e),
            }
        }
        Expr::BinaryExpression(ref expr1, ref op, ref expr2) => {
            let mut errors = Vec::new();
            let checked_type_1 = match check_expr(expr1, env) {
                Ok(t) => t,
                Err(mut e) => {
                    errors.append(&mut e);
                    Type::Any
                }
            };
            let checked_type_2 = match check_expr(expr2, env) {
                Ok(t) => t,
                Err(mut e) => {
                    errors.append(&mut e);
                    Type::Any
                }
            };
            use ast::BinaryOp::*;
            let result = match *op {
                ref op @ Add |
                ref op @ Sub |
                ref op @ Mul |
                ref op @ Div |
                ref op @ FloorDiv => {
                    check_binary_arithmetic_for_types(op.clone(), checked_type_1, checked_type_2)
                }
                ref op @ LessThan |
                ref op @ LessThanOrEqual |
                ref op @ GreaterThan |
                ref op @ GreaterThanOrEqual => {
                    check_binary_comparison_for_types(op.clone(), checked_type_1, checked_type_2)
                }
                StrictEquals => Ok(Type::Bool),
            };
            match result {
                Err(e) => {
                    errors.push(e);
                    Err(errors)
                }
                Ok(t) => {
                    if errors.len() == 0 {
                        Ok(t)
                    } else {
                        Err(errors)
                    }
                }
            }
        }
        Expr::BinaryLogicalExpression(ref expr1, ref op, ref expr2) => {
            let mut errors = Vec::new();
            match *op {
                LogicalBinaryOp::LogicalAnd |
                LogicalBinaryOp::LogicalOr => {
                    if let Err(mut e) = check_expr(expr1, env) {
                        errors.append(&mut e);
                    };
                    if let Err(mut e) = check_expr(expr2, env) {
                        errors.append(&mut e);
                    };
                    if errors.len() == 0 {
                        Ok(Type::Bool)
                    } else {
                        Err(errors)
                    }
                }
            }
        }
    }
}

fn check_unary_minus_for_type(typ: Type) -> Result<Type, InterpreterError> {
    match typ {
        Type::Number => Ok(Type::Number),
        Type::Any => Ok(Type::Any),
        _ => Err(InterpreterError::UnaryTypeError(UnaryOp::Minus, typ)),
    }
}

fn check_binary_arithmetic_for_types(op: BinaryOp,
                                     t1: Type,
                                     t2: Type)
                                     -> Result<Type, InterpreterError> {
    match (t1, t2) {
        (Type::Number, Type::Number) => Ok(Type::Number),
        (Type::Any, _) => Ok(Type::Any),
        (_, Type::Any) => Ok(Type::Any),
        _ => Err(InterpreterError::BinaryTypeError(op, t1, t2)),
    }
}

fn check_binary_comparison_for_types(op: BinaryOp,
                                     t1: Type,
                                     t2: Type)
                                     -> Result<Type, InterpreterError> {
    match (t1, t2) {
        (Type::Number, Type::Number) => Ok(Type::Bool),
        (Type::Any, _) => Ok(Type::Any),
        (_, Type::Any) => Ok(Type::Any),
        _ => Err(InterpreterError::BinaryTypeError(op, t1, t2)),
    }
}
<|endoftext|>


<commit_before>extern crate quick_protobuf;
extern crate protobuf;
extern crate rand;
extern crate time;

use std::default::Default;
use std::fs::File;
use std::path::Path;

use rand::Rng;
use rand::StdRng;
use rand::SeedableRng;

use protobuf::Message;
use protobuf::MessageStatic;
use perftest_data::PerftestData;

use quick_protobuf::{Reader, Writer};
use quick_protobuf::message::{MessageRead, MessageWrite};
use perftest_data_quick::PerftestData as QuickPerftestData;

mod perftest_data;
mod perftest_data_quick;

fn measure<R, F: FnMut() -> R>(iter: u64, mut f: F) -> (u64, R) {
    let start = time::precise_time_ns();
    let r = f();
    ((time::precise_time_ns() - start) / iter, r)
}

struct TestRunner {
    data_size: u32,
    selected: Option<String>,
    any_matched: bool,
}

impl TestRunner {
    fn run_test<M : Message + MessageStatic>(&self, data: &[M]) -> [u64; 4] {

        let mut a = [0; 4];

        let mut rng: StdRng = SeedableRng::from_seed(&[10, 20, 30, 40][..]);
        let mut random_data: Vec<M> = Vec::new();

        let mut total_size = 0;
        while total_size < self.data_size {
            let ref item = data[rng.gen_range(0, data.len())];
            random_data.push(item.clone());
            total_size += item.compute_size();
        }

        let mut buf = Vec::new();
        a[0] = measure(random_data.len() as u64, || {
            for m in &random_data {
                m.write_length_delimited_to_writer(&mut buf).unwrap();
            }
        }).0;

        let (ns, read_data) = measure(random_data.len() as u64, || {
            let mut r = Vec::new();
            let mut coded_input_stream = protobuf::CodedInputStream::from_bytes(&buf);
            while !coded_input_stream.eof().unwrap() {
                r.push(protobuf::parse_length_delimited_from::<M>(&mut coded_input_stream).unwrap());
            }
            r
        });
        a[1] = ns;

        assert_eq!(random_data, &*read_data);

        a[2] = measure(random_data.len() as u64, || {
            let mut coded_input_stream = protobuf::CodedInputStream::from_bytes(&buf);
            while !coded_input_stream.eof().unwrap() {
                protobuf::parse_length_delimited_from::<M>(&mut coded_input_stream).unwrap();
            }
        }).0;

        let (ns, merged) = measure(random_data.len() as u64, || {
            let mut coded_input_stream = protobuf::CodedInputStream::from_bytes(&buf);
            let mut msg: M = Default::default();
            let mut count = 0;
            while !coded_input_stream.eof().unwrap() {
                msg.clear();
                coded_input_stream.merge_message(&mut msg).unwrap();
                count += 1;
            }
            count
        });

        a[3] = ns;

        assert_eq!(random_data.len(), merged);

        a
    }

    fn test<M : Message + MessageStatic>(&mut self, data: &[M]) -> [u64; 4] {
        let a = self.run_test(data);
        self.any_matched = true;
        a
    }

    fn quick_run_test<M : MessageRead + MessageWrite + Clone + PartialEq + ::std::fmt::Debug>(&self, data: &[M]) -> [u64; 4] {

        let mut b = [0; 4];

        let mut rng: StdRng = SeedableRng::from_seed(&[10, 20, 30, 40][..]);
        let mut random_data: Vec<M> = Vec::new();

        let mut total_size = 0;
        while total_size < self.data_size {
            let ref item = data[rng.gen_range(0, data.len())];
            random_data.push(item.clone());
            total_size += item.get_size() as u32;
        }

        let mut buf = Vec::new();
        b[0] = measure(random_data.len() as u64, || {
            let mut w = Writer::new(&mut buf);
            for m in &random_data {
                w.write_message(m).unwrap();
            }
        }).0;

        let (ns, read_data) = measure(random_data.len() as u64, || {
            let mut r = Vec::new();
            let len = buf.len();
            let mut buf = &*buf;
            let mut reader = Reader::from_reader(&mut buf, len);
            while !reader.is_eof() {
                r.push(reader.read_message().unwrap());
            }
            r
        });
        b[1] = ns;

        assert_eq!(random_data, &*read_data);

        b[2] = measure(random_data.len() as u64, || {
            let len = buf.len();
            let mut buf = &*buf;
            let mut reader = Reader::from_reader(&mut buf, len);
            while !reader.is_eof() {
                reader.read_message::<M>().unwrap();
            }
        }).0;

        b
    }

    fn quick_test<M : MessageRead + MessageWrite + Clone + PartialEq + ::std::fmt::Debug>(&mut self, data: &[M]) -> [u64; 4]{
        let b = self.quick_run_test(data);
        self.any_matched = true;
        b
    }

    fn check(&self) {
        if !self.any_matched {
            let name = self.selected.as_ref().map(|s| &s[..]).unwrap_or("bug");
            panic!("no tests found with name {}", name);
        }
    }
}

fn print_results(name: &str, a: &[u64], b: &[u64], print_header: bool) {
    let labels = ["write", "read", "read no vec", "read reuse"];

    if print_header {
        println!("{:>15} {:>15} {:>15} {:>8}", "labels", "rust-protobuf", "quick-protobuf", "");
        println!("{:>15} {:>15} {:>15} {:>8}", "", "ns/iter", "ns/iter", "%");
    }
    println!("");
    println!("{}", name);
    for i in 0..3 {
        println!("{:>15} {:>15} {:>15} {:>8.1}", labels[i], a[i], b[i], 100. - b[i] as f32 / a[i] as f32 * 100.);
    }
    let i = 3;
    println!("{:>15} {:>15} {:>15} {:>8}", labels[i], a[i], "", "NA");
}

fn main() {
    let args = std::env::args().collect::<Vec<_>>();
    if args.len() > 3 { panic!("usage: {} [data_size] [test]", args[0]) }
    let data_size = args.iter().nth(1).map(|x| x.parse().unwrap()).unwrap_or(1000000);
    let selected = args.iter().nth(2).cloned();

    let mut runner = TestRunner {
        selected: selected,
        any_matched: false,
        data_size: data_size,
    };

    let mut is = File::open(&Path::new("perftest_data.pbbin")).unwrap();
    let test_data = protobuf::parse_from_reader::<PerftestData>(&mut is).unwrap();
    let test_data_quick = QuickPerftestData::from_file("perftest_data.pbbin").unwrap();

    let a = runner.test(test_data.get_test1());
    let b = runner.quick_test(&test_data_quick.test1);
    print_results("test1", &a, &b, true);

    let a = runner.test(test_data.get_test_repeated_bool());
    let b = runner.quick_test(&test_data_quick.test_repeated_bool);
    print_results("test_repeated_bool", &a, &b, false);

    let a = runner.test(test_data.get_test_repeated_packed_int32());
    let b = runner.quick_test(&test_data_quick.test_repeated_packed_int32);
    print_results("test_repeated_packed_int32", &a, &b, false);

    let a = runner.test(test_data.get_test_repeated_messages());
    let b = runner.quick_test(&test_data_quick.test_repeated_messages);
    print_results("test_repeated_messages", &a, &b, false);

    let a = runner.test(test_data.get_test_optional_messages());
    let b = runner.quick_test(&test_data_quick.test_optional_messages);
    print_results("test_optional_messages", &a, &b, false);

    let a = runner.test(test_data.get_test_strings());
    let b = runner.quick_test(&test_data_quick.test_strings);
    print_results("test_strings", &a, &b, false);

    let a = runner.test(test_data.get_test_small_bytearrays());
    let b = runner.quick_test(&test_data_quick.test_small_bytearrays);
    print_results("test_small_bytearrays", &a, &b, false);

    let a = runner.test(test_data.get_test_large_bytearrays());
    let b = runner.quick_test(&test_data_quick.test_large_bytearrays);
    print_results("test_large_bytearrays", &a, &b, false);

    runner.check();
}
<commit_msg>Avoid buffering when writing to Vec<commit_after>extern crate quick_protobuf;
extern crate protobuf;
extern crate rand;
extern crate time;

use std::default::Default;
use std::fs::File;
use std::path::Path;

use rand::Rng;
use rand::StdRng;
use rand::SeedableRng;

use protobuf::Message;
use protobuf::MessageStatic;
use perftest_data::PerftestData;

use quick_protobuf::{Reader, Writer};
use quick_protobuf::message::{MessageRead, MessageWrite};
use perftest_data_quick::PerftestData as QuickPerftestData;

mod perftest_data;
mod perftest_data_quick;

fn measure<R, F: FnMut() -> R>(iter: u64, mut f: F) -> (u64, R) {
    let start = time::precise_time_ns();
    let r = f();
    ((time::precise_time_ns() - start) / iter, r)
}

struct TestRunner {
    data_size: u32,
    selected: Option<String>,
    any_matched: bool,
}

impl TestRunner {
    fn run_test<M : Message + MessageStatic>(&self, data: &[M]) -> [u64; 4] {

        let mut a = [0; 4];

        let mut rng: StdRng = SeedableRng::from_seed(&[10, 20, 30, 40][..]);
        let mut random_data: Vec<M> = Vec::new();

        let mut total_size = 0;
        while total_size < self.data_size {
            let ref item = data[rng.gen_range(0, data.len())];
            random_data.push(item.clone());
            total_size += item.compute_size();
        }

        let mut buf = Vec::new();
        a[0] = measure(random_data.len() as u64, || {
            for m in &random_data {
                m.write_length_delimited_to_vec(&mut buf).unwrap();
            }
        }).0;

        let (ns, read_data) = measure(random_data.len() as u64, || {
            let mut r = Vec::new();
            let mut coded_input_stream = protobuf::CodedInputStream::from_bytes(&buf);
            while !coded_input_stream.eof().unwrap() {
                r.push(protobuf::parse_length_delimited_from::<M>(&mut coded_input_stream).unwrap());
            }
            r
        });
        a[1] = ns;

        assert_eq!(random_data, &*read_data);

        a[2] = measure(random_data.len() as u64, || {
            let mut coded_input_stream = protobuf::CodedInputStream::from_bytes(&buf);
            while !coded_input_stream.eof().unwrap() {
                protobuf::parse_length_delimited_from::<M>(&mut coded_input_stream).unwrap();
            }
        }).0;

        let (ns, merged) = measure(random_data.len() as u64, || {
            let mut coded_input_stream = protobuf::CodedInputStream::from_bytes(&buf);
            let mut msg: M = Default::default();
            let mut count = 0;
            while !coded_input_stream.eof().unwrap() {
                msg.clear();
                coded_input_stream.merge_message(&mut msg).unwrap();
                count += 1;
            }
            count
        });

        a[3] = ns;

        assert_eq!(random_data.len(), merged);

        a
    }

    fn test<M : Message + MessageStatic>(&mut self, data: &[M]) -> [u64; 4] {
        let a = self.run_test(data);
        self.any_matched = true;
        a
    }

    fn quick_run_test<M : MessageRead + MessageWrite + Clone + PartialEq + ::std::fmt::Debug>(&self, data: &[M]) -> [u64; 4] {

        let mut b = [0; 4];

        let mut rng: StdRng = SeedableRng::from_seed(&[10, 20, 30, 40][..]);
        let mut random_data: Vec<M> = Vec::new();

        let mut total_size = 0;
        while total_size < self.data_size {
            let ref item = data[rng.gen_range(0, data.len())];
            random_data.push(item.clone());
            total_size += item.get_size() as u32;
        }

        let mut buf = Vec::new();
        b[0] = measure(random_data.len() as u64, || {
            let mut w = Writer::new(&mut buf);
            for m in &random_data {
                w.write_message(m).unwrap();
            }
        }).0;

        let (ns, read_data) = measure(random_data.len() as u64, || {
            let mut r = Vec::new();
            let len = buf.len();
            let mut buf = &*buf;
            let mut reader = Reader::from_reader(&mut buf, len);
            while !reader.is_eof() {
                r.push(reader.read_message().unwrap());
            }
            r
        });
        b[1] = ns;

        assert_eq!(random_data, &*read_data);

        b[2] = measure(random_data.len() as u64, || {
            let len = buf.len();
            let mut buf = &*buf;
            let mut reader = Reader::from_reader(&mut buf, len);
            while !reader.is_eof() {
                reader.read_message::<M>().unwrap();
            }
        }).0;

        b
    }

    fn quick_test<M : MessageRead + MessageWrite + Clone + PartialEq + ::std::fmt::Debug>(&mut self, data: &[M]) -> [u64; 4]{
        let b = self.quick_run_test(data);
        self.any_matched = true;
        b
    }

    fn check(&self) {
        if !self.any_matched {
            let name = self.selected.as_ref().map(|s| &s[..]).unwrap_or("bug");
            panic!("no tests found with name {}", name);
        }
    }
}

fn print_results(name: &str, a: &[u64], b: &[u64], print_header: bool) {
    let labels = ["write", "read", "read no vec", "read reuse"];

    if print_header {
        println!("{:>15} {:>15} {:>15} {:>8}", "labels", "rust-protobuf", "quick-protobuf", "");
        println!("{:>15} {:>15} {:>15} {:>8}", "", "ns/iter", "ns/iter", "%");
    }
    println!("");
    println!("{}", name);
    for i in 0..3 {
        println!("{:>15} {:>15} {:>15} {:>8.1}", labels[i], a[i], b[i], 100. - b[i] as f32 / a[i] as f32 * 100.);
    }
    let i = 3;
    println!("{:>15} {:>15} {:>15} {:>8}", labels[i], a[i], "", "NA");
}

fn main() {
    let args = std::env::args().collect::<Vec<_>>();
    if args.len() > 3 { panic!("usage: {} [data_size] [test]", args[0]) }
    let data_size = args.iter().nth(1).map(|x| x.parse().unwrap()).unwrap_or(1000000);
    let selected = args.iter().nth(2).cloned();

    let mut runner = TestRunner {
        selected: selected,
        any_matched: false,
        data_size: data_size,
    };

    let mut is = File::open(&Path::new("perftest_data.pbbin")).unwrap();
    let test_data = protobuf::parse_from_reader::<PerftestData>(&mut is).unwrap();
    let test_data_quick = QuickPerftestData::from_file("perftest_data.pbbin").unwrap();

    let a = runner.test(test_data.get_test1());
    let b = runner.quick_test(&test_data_quick.test1);
    print_results("test1", &a, &b, true);

    let a = runner.test(test_data.get_test_repeated_bool());
    let b = runner.quick_test(&test_data_quick.test_repeated_bool);
    print_results("test_repeated_bool", &a, &b, false);

    let a = runner.test(test_data.get_test_repeated_packed_int32());
    let b = runner.quick_test(&test_data_quick.test_repeated_packed_int32);
    print_results("test_repeated_packed_int32", &a, &b, false);

    let a = runner.test(test_data.get_test_repeated_messages());
    let b = runner.quick_test(&test_data_quick.test_repeated_messages);
    print_results("test_repeated_messages", &a, &b, false);

    let a = runner.test(test_data.get_test_optional_messages());
    let b = runner.quick_test(&test_data_quick.test_optional_messages);
    print_results("test_optional_messages", &a, &b, false);

    let a = runner.test(test_data.get_test_strings());
    let b = runner.quick_test(&test_data_quick.test_strings);
    print_results("test_strings", &a, &b, false);

    let a = runner.test(test_data.get_test_small_bytearrays());
    let b = runner.quick_test(&test_data_quick.test_small_bytearrays);
    print_results("test_small_bytearrays", &a, &b, false);

    let a = runner.test(test_data.get_test_large_bytearrays());
    let b = runner.quick_test(&test_data_quick.test_large_bytearrays);
    print_results("test_large_bytearrays", &a, &b, false);

    runner.check();
}
<|endoftext|>


<commit_before>#![crate_type = "bin"]

extern crate gtk;
extern crate glib;
extern crate sysinfo;

use gtk::{Orientation, SortColumn, TreeModel, TreeSortable, Widget};
use gtk::prelude::*;

use sysinfo::*;

use std::cell::{Cell, RefCell};
use std::collections::HashMap;
use std::rc::Rc;
use std::cmp::Ordering;

macro_rules! set_sort {
    ($id:expr, $columns:expr) => {{
        $columns.get($id).unwrap().set_sort_column_id($id);
    }};
    ($id:expr, $left_tree:expr, $val:ty, $columns:expr) => {{
        let model = $left_tree.get_model().unwrap()
                              .downcast::<TreeSortable>().unwrap();
        model.set_sort_func(SortColumn::Index($id), |m, it1, it2| {
            let it1 : $val = m.clone().upcast::<TreeModel>().get_value(it1, $id).get().unwrap_or(String::new());
            let it2 : $val = m.clone().upcast::<TreeModel>().get_value(it2, $id).get().unwrap_or(String::new());
            it1.to_lowercase().cmp(&it2.to_uppercase())
        });
        $columns.get($id).unwrap().set_sort_column_id($id);
    }};
    // ultra super ugly
    ($id:expr, $left_tree:expr, $val:ty, $columns:expr, $fill:expr) => {{
        let model = $left_tree.get_model().unwrap()
                              .downcast::<TreeSortable>().unwrap();
        model.set_sort_func(SortColumn::Index($id), |m, it1, it2| {
            let it1 : $val = m.clone().upcast::<TreeModel>().get_value(it1, $id).get().unwrap_or("0".to_owned());
            let it2 : $val = m.clone().upcast::<TreeModel>().get_value(it2, $id).get().unwrap_or("0".to_owned());
            let it1 : f32 = it1.parse().unwrap();
            let it2 : f32 = it2.parse().unwrap();
            if it1.lt(&it2) {
                Ordering::Less
            } else if it1.gt(&it2) {
                Ordering::Greater
            } else {
                Ordering::Equal
            }
        });
        $columns.get($id).unwrap().set_sort_column_id($id);
    }};
}

struct NoteBook {
    notebook: gtk::Notebook,
    tabs: Vec<gtk::Box>,
}

impl NoteBook {
    fn new() -> NoteBook {
        NoteBook {
            notebook: gtk::Notebook::new(),
            tabs: Vec::new(),
        }
    }

    fn create_tab<'a>(&mut self, title: &'a str, widget: &Widget) -> Option<u32> {
        let label = gtk::Label::new(Some(title));
        let tab = gtk::Box::new(Orientation::Horizontal, 0);

        tab.pack_start(&label, false, false, 0);
        tab.show_all();

        let index = self.notebook.append_page(widget, Some(&tab));

        self.tabs.push(tab);

        Some(index)
    }
}

#[allow(dead_code)]
struct Procs {
    left_tree: gtk::TreeView,
    scroll: gtk::ScrolledWindow,
    current_pid: Rc<Cell<Option<i64>>>,
    kill_button: gtk::Button,
    vertical_layout: gtk::Box,
    list_store: gtk::ListStore,
    columns: Vec<gtk::TreeViewColumn>,
}

impl Procs {
    pub fn new<'a>(proc_list: &HashMap<usize, Process>, note: &mut NoteBook) -> Procs {
        let left_tree = gtk::TreeView::new();
        let scroll = gtk::ScrolledWindow::new(None, None);
        let current_pid = Rc::new(Cell::new(None));
        let kill_button = gtk::Button::new_with_label("End task");
        let current_pid1 = current_pid.clone();
        let kill_button1 = kill_button.clone();

        scroll.set_min_content_height(800);
        scroll.set_min_content_width(600);

        let mut columns : Vec<gtk::TreeViewColumn> = Vec::new();

        append_column("pid", &mut columns, &left_tree);
        append_column("process name", &mut columns, &left_tree);
        append_column("cpu usage", &mut columns, &left_tree);
        append_column("memory usage (in kB)", &mut columns, &left_tree);

        let mut list_store = gtk::ListStore::new(&[glib::Type::I64, glib::Type::String,
                                                   glib::Type::String, glib::Type::U32]);
        for (_, pro) in proc_list {
            create_and_fill_model(&mut list_store, pro.pid, &pro.cmd, &pro.name, pro.cpu_usage,
                                  pro.memory);
        }

        left_tree.set_model(Some(&list_store));
        left_tree.set_headers_visible(true);
        scroll.add(&left_tree);
        let vertical_layout = gtk::Box::new(gtk::Orientation::Vertical, 0);

        left_tree.connect_cursor_changed(move |tree_view| {
            let selection = tree_view.get_selection();
            if let Some((model, iter)) = selection.get_selected() {
                let pid = Some(model.get_value(&iter, 0).get().unwrap());
                current_pid1.set(pid);
                kill_button1.set_sensitive(true);
            } else {
                current_pid1.set(None);
                kill_button1.set_sensitive(false);
            }
        });
        set_sort!(0, columns);
        set_sort!(3, columns);
        set_sort!(1, left_tree, String, columns);
        set_sort!(2, left_tree, String, columns, 1);
        kill_button.set_sensitive(false);

        vertical_layout.add(&scroll);
        vertical_layout.add(&kill_button);
        let vertical_layout : Widget = vertical_layout.upcast();

        note.create_tab("Process list", &vertical_layout);
        Procs {
            left_tree: left_tree,
            scroll: scroll,
            current_pid: current_pid,
            kill_button: kill_button.clone(),
            vertical_layout: vertical_layout.downcast::<gtk::Box>().unwrap(),
            list_store: list_store,
            columns: columns,
        }
    }
}

fn append_column(title: &str, v: &mut Vec<gtk::TreeViewColumn>, left_tree: &gtk::TreeView) {
    let l = v.len();
    let renderer = gtk::CellRendererText::new();

    v.push(gtk::TreeViewColumn::new());
    let tmp = v.get_mut(l).unwrap();

    tmp.set_title(title);
    tmp.set_resizable(true);
    tmp.pack_start(&renderer, true);
    tmp.add_attribute(&renderer, "text", l as i32);
    tmp.set_clickable(true);
    left_tree.append_column(&tmp);
}

fn create_and_fill_model(list_store: &mut gtk::ListStore, pid: i64, cmdline: &str, name: &str,
                         cpu: f32, memory: u64) {
    if cmdline.len() < 1 {
        return;
    }
    list_store.insert_with_values(None,
                                  &[0, 1, 2, 3],
                                  &[&pid,
                                    &name,
                                    &format!("{:.1}", cpu),
                                    &memory]);
}

fn update_window(list: &mut gtk::ListStore, system: &Rc<RefCell<sysinfo::System>>,
                 info: &mut DisplaySysInfo) {
    system.borrow_mut().refresh_all();
    let mut entries : HashMap<usize, Process> = system.borrow().get_process_list().clone();
    let mut nb = list.iter_n_children(None);

    info.update_ram_display(&system.borrow());
    info.update_process_display(&system.borrow());

    let mut i = 0;
    while i < nb {
        if let Some(mut iter) = list.iter_nth_child(None, i) {
            if let Some(pid) = list.get_value(&iter, 0).get::<i64>() {
                match entries.get(&(pid as usize)) {
                    Some(p) => {
                        list.set(&iter,
                                 &[2, 3],
                                 &[&format!("{:.1}", p.cpu_usage), &p.memory]);
                    }
                    None => {
                        list.remove(&mut iter);
                        nb = list.iter_n_children(None);
                        continue
                    }
                }
                entries.remove(&(pid as usize));
                i += 1;
            }
        } else {
            i += 1;
        }
    }
    for (_, pro) in entries {
        create_and_fill_model(list, pro.pid, &pro.cmd, &pro.name, pro.cpu_usage, pro.memory);
    }
}

#[allow(dead_code)]
struct DisplaySysInfo {
    procs : Rc<RefCell<Vec<gtk::ProgressBar>>>,
    ram : gtk::ProgressBar,
    swap : gtk::ProgressBar,
    vertical_layout : gtk::Box,
}

impl DisplaySysInfo {
    pub fn new(sys1: Rc<RefCell<sysinfo::System>>, note: &mut NoteBook) -> DisplaySysInfo {
        let vertical_layout = gtk::Box::new(gtk::Orientation::Vertical, 0);
        let mut procs = Vec::new();
        let ram = gtk::ProgressBar::new();
        let swap = gtk::ProgressBar::new();

        ram.set_show_text(true);
        swap.set_show_text(true);
        vertical_layout.set_spacing(5);

        let mut i = 0;
        let mut total = false;

        vertical_layout.pack_start(&gtk::Label::new(Some("Memory usage")), false, false, 15);
        vertical_layout.add(&ram);
        vertical_layout.pack_start(&gtk::Label::new(Some("Swap usage")), false, false, 15);
        vertical_layout.add(&swap);
        vertical_layout.pack_start(&gtk::Label::new(Some("Total CPU usage")), false, false, 15);
        for pro in sys1.borrow().get_processor_list() {
            if total {
                procs.push(gtk::ProgressBar::new());
                let p : &gtk::ProgressBar = &procs[i];
                let l = gtk::Label::new(Some(&format!("{}", i)));
                let horizontal_layout = gtk::Box::new(gtk::Orientation::Horizontal, 0);

                p.set_text(Some(&format!("{:.2} %", pro.get_cpu_usage() * 100.)));
                p.set_show_text(true);
                p.set_fraction(pro.get_cpu_usage() as f64);
                horizontal_layout.pack_start(&l, false, false, 5);
                horizontal_layout.pack_start(p, true, true, 5);
                vertical_layout.add(&horizontal_layout);
            } else {
                procs.push(gtk::ProgressBar::new());
                let p : &gtk::ProgressBar = &procs[i];

                p.set_text(Some(&format!("{:.2} %", pro.get_cpu_usage() * 100.)));
                p.set_show_text(true);
                p.set_fraction(pro.get_cpu_usage() as f64);

                vertical_layout.add(p);
                vertical_layout.pack_start(&gtk::Label::new(Some("Process usage")), false,
                                           false, 15);
                total = true;
            }
            i += 1;
        }

        let vertical_layout : Widget = vertical_layout.upcast();
        note.create_tab("System usage", &vertical_layout);
        let vertical_layout : gtk::Box = vertical_layout.downcast::<gtk::Box>().unwrap();

        let mut tmp = DisplaySysInfo {
            procs: Rc::new(RefCell::new(procs)),
            ram: ram,
            swap: swap,
            vertical_layout: vertical_layout,
        };
        tmp.update_ram_display(&sys1.borrow());
        tmp
    }

    pub fn update_ram_display(&mut self, sys: &sysinfo::System) {
        let total = sys.get_total_memory();
        let used = sys.get_used_memory();
        let disp = if total < 100000 {
            format!("{} / {}KB", used, total)
        } else if total < 10000000 {
            format!("{} / {}MB", used / 1000, total / 1000)
        } else if total < 10000000000 {
            format!("{} / {}GB", used / 1000000, total / 1000000)
        } else {
            format!("{} / {}TB", used / 1000000000, total / 1000000000)
        };

        self.ram.set_text(Some(&disp));
        self.ram.set_fraction(used as f64 / total as f64);

        let total = sys.get_total_swap();
        let used = total - sys.get_used_swap();
        let disp = if total < 100000 {
            format!("{} / {}KB", used, total)
        } else if total < 10000000 {
            format!("{} / {}MB", used / 1000, total / 1000)
        } else if total < 10000000000 {
            format!("{} / {}GB", used / 1000000, total / 1000000)
        } else {
            format!("{} / {}TB", used / 1000000000, total / 1000000000)
        };

        self.swap.set_text(Some(&disp));
        self.swap.set_fraction(used as f64 / total as f64);
    }

    pub fn update_process_display(&mut self, sys: &sysinfo::System) {
        let v = &*self.procs.borrow_mut();
        let mut i = 0;

        for pro in sys.get_processor_list() {
            v[i].set_text(Some(&format!("{:.1} %", pro.get_cpu_usage() * 100.)));
            v[i].set_show_text(true);
            v[i].set_fraction(pro.get_cpu_usage() as f64);
            i += 1;
        }
    }
}

fn main() {
    gtk::init().expect("GTK couldn't start normally");

    let window = gtk::Window::new(gtk::WindowType::Toplevel);
    let sys = Rc::new(RefCell::new(sysinfo::System::new()));
    let mut note = NoteBook::new();
    let mut procs = Procs::new((*sys.borrow()).get_process_list(), &mut note);
    let current_pid = procs.current_pid.clone();
    let sys1 = sys.clone();

    window.set_title("Process viewer");
    window.set_position(gtk::WindowPosition::Center);

    window.connect_delete_event(|_, _| {
        gtk::main_quit();
        Inhibit(true)
    });

    sys.borrow_mut().refresh_all();
    procs.kill_button.connect_clicked(move |_| {
        let sys = sys1.borrow();
        if let Some(process) = current_pid.get().and_then(|pid| sys.get_process(pid)) {
            process.kill(Signal::Kill);
        }
    });

    let mut display_tab = DisplaySysInfo::new(sys.clone(), &mut note);

    gtk::timeout_add(1500, move || {
        // first part, deactivate sorting
        let sorted = procs.list_store.get_sort_column_id();
        procs.list_store.set_unsorted();

        // we update the tree view
        update_window(&mut procs.list_store, &sys, &mut display_tab);

        // we re-enable the sorting
        if let Some((col, order)) = sorted {
            procs.list_store.set_sort_column_id(col, order);
        }
        glib::Continue(true)
    });

    window.add(&note.notebook);
    window.show_all();
    gtk::main();
}
<commit_msg>Store lowercase process name and float CPU usage in the model<commit_after>#![crate_type = "bin"]

extern crate gtk;
extern crate glib;
extern crate sysinfo;

use gtk::{Orientation, Type, Widget};
use gtk::prelude::*;

use sysinfo::*;

use std::cell::{Cell, RefCell};
use std::collections::HashMap;
use std::rc::Rc;

struct NoteBook {
    notebook: gtk::Notebook,
    tabs: Vec<gtk::Box>,
}

impl NoteBook {
    fn new() -> NoteBook {
        NoteBook {
            notebook: gtk::Notebook::new(),
            tabs: Vec::new(),
        }
    }

    fn create_tab<'a>(&mut self, title: &'a str, widget: &Widget) -> Option<u32> {
        let label = gtk::Label::new(Some(title));
        let tab = gtk::Box::new(Orientation::Horizontal, 0);

        tab.pack_start(&label, false, false, 0);
        tab.show_all();

        let index = self.notebook.append_page(widget, Some(&tab));

        self.tabs.push(tab);

        Some(index)
    }
}

#[allow(dead_code)]
struct Procs {
    left_tree: gtk::TreeView,
    scroll: gtk::ScrolledWindow,
    current_pid: Rc<Cell<Option<i64>>>,
    kill_button: gtk::Button,
    vertical_layout: gtk::Box,
    list_store: gtk::ListStore,
    columns: Vec<gtk::TreeViewColumn>,
}

impl Procs {
    pub fn new<'a>(proc_list: &HashMap<usize, Process>, note: &mut NoteBook) -> Procs {
        let left_tree = gtk::TreeView::new();
        let scroll = gtk::ScrolledWindow::new(None, None);
        let current_pid = Rc::new(Cell::new(None));
        let kill_button = gtk::Button::new_with_label("End task");
        let current_pid1 = current_pid.clone();
        let kill_button1 = kill_button.clone();

        scroll.set_min_content_height(800);
        scroll.set_min_content_width(600);

        let mut columns : Vec<gtk::TreeViewColumn> = Vec::new();

        append_column("pid", &mut columns, &left_tree);
        append_column("process name", &mut columns, &left_tree);
        append_column("cpu usage", &mut columns, &left_tree);
        append_column("memory usage (in kB)", &mut columns, &left_tree);
        // sort the name column by name_lowercase
        columns[1].set_sort_column_id(4);
        // sort the CPU column by CPU_f32
        columns[2].set_sort_column_id(5);

        let mut list_store = gtk::ListStore::new(&[Type::I64,       // pid
                                                   Type::String,    // name
                                                   Type::String,    // CPU
                                                   Type::U32,       // mem
                                                   Type::String,    // name_lowercase
                                                   Type::F32,       // CPU_f32
                                                  ]);
        for (_, pro) in proc_list {
            create_and_fill_model(&mut list_store, pro.pid, &pro.cmd, &pro.name, pro.cpu_usage,
                                  pro.memory);
        }

        left_tree.set_model(Some(&list_store));
        left_tree.set_headers_visible(true);
        scroll.add(&left_tree);
        let vertical_layout = gtk::Box::new(gtk::Orientation::Vertical, 0);

        left_tree.connect_cursor_changed(move |tree_view| {
            let selection = tree_view.get_selection();
            if let Some((model, iter)) = selection.get_selected() {
                let pid = Some(model.get_value(&iter, 0).get().unwrap());
                current_pid1.set(pid);
                kill_button1.set_sensitive(true);
            } else {
                current_pid1.set(None);
                kill_button1.set_sensitive(false);
            }
        });
        kill_button.set_sensitive(false);

        vertical_layout.add(&scroll);
        vertical_layout.add(&kill_button);
        let vertical_layout : Widget = vertical_layout.upcast();

        note.create_tab("Process list", &vertical_layout);
        Procs {
            left_tree: left_tree,
            scroll: scroll,
            current_pid: current_pid,
            kill_button: kill_button.clone(),
            vertical_layout: vertical_layout.downcast::<gtk::Box>().unwrap(),
            list_store: list_store,
            columns: columns,
        }
    }
}

fn append_column(title: &str, v: &mut Vec<gtk::TreeViewColumn>, left_tree: &gtk::TreeView) {
    let id = v.len() as i32;
    let renderer = gtk::CellRendererText::new();

    let column = gtk::TreeViewColumn::new();
    column.set_title(title);
    column.set_resizable(true);
    column.pack_start(&renderer, true);
    column.add_attribute(&renderer, "text", id);
    column.set_clickable(true);
    column.set_sort_column_id(id);
    left_tree.append_column(&column);
    v.push(column);
}

fn create_and_fill_model(list_store: &mut gtk::ListStore, pid: i64, cmdline: &str, name: &str,
                         cpu: f32, memory: u64) {
    if cmdline.len() < 1 {
        return;
    }
    list_store.insert_with_values(None,
                                  &[0, 1, 2, 3, 4, 5],
                                  &[&pid,
                                    &name,
                                    &format!("{:.1}", cpu),
                                    &memory,
                                    &name.to_lowercase(),
                                    &cpu
                                   ]);
}

fn update_window(list: &mut gtk::ListStore, system: &Rc<RefCell<sysinfo::System>>,
                 info: &mut DisplaySysInfo) {
    system.borrow_mut().refresh_all();
    let mut entries : HashMap<usize, Process> = system.borrow().get_process_list().clone();
    let mut nb = list.iter_n_children(None);

    info.update_ram_display(&system.borrow());
    info.update_process_display(&system.borrow());

    let mut i = 0;
    while i < nb {
        if let Some(mut iter) = list.iter_nth_child(None, i) {
            if let Some(pid) = list.get_value(&iter, 0).get::<i64>() {
                match entries.get(&(pid as usize)) {
                    Some(p) => {
                        list.set(&iter,
                                 &[2, 3, 5],
                                 &[&format!("{:.1}", p.cpu_usage), &p.memory, &p.cpu_usage]);
                    }
                    None => {
                        list.remove(&mut iter);
                        nb = list.iter_n_children(None);
                        continue
                    }
                }
                entries.remove(&(pid as usize));
                i += 1;
            }
        } else {
            i += 1;
        }
    }
    for (_, pro) in entries {
        create_and_fill_model(list, pro.pid, &pro.cmd, &pro.name, pro.cpu_usage, pro.memory);
    }
}

#[allow(dead_code)]
struct DisplaySysInfo {
    procs : Rc<RefCell<Vec<gtk::ProgressBar>>>,
    ram : gtk::ProgressBar,
    swap : gtk::ProgressBar,
    vertical_layout : gtk::Box,
}

impl DisplaySysInfo {
    pub fn new(sys1: Rc<RefCell<sysinfo::System>>, note: &mut NoteBook) -> DisplaySysInfo {
        let vertical_layout = gtk::Box::new(gtk::Orientation::Vertical, 0);
        let mut procs = Vec::new();
        let ram = gtk::ProgressBar::new();
        let swap = gtk::ProgressBar::new();

        ram.set_show_text(true);
        swap.set_show_text(true);
        vertical_layout.set_spacing(5);

        let mut i = 0;
        let mut total = false;

        vertical_layout.pack_start(&gtk::Label::new(Some("Memory usage")), false, false, 15);
        vertical_layout.add(&ram);
        vertical_layout.pack_start(&gtk::Label::new(Some("Swap usage")), false, false, 15);
        vertical_layout.add(&swap);
        vertical_layout.pack_start(&gtk::Label::new(Some("Total CPU usage")), false, false, 15);
        for pro in sys1.borrow().get_processor_list() {
            if total {
                procs.push(gtk::ProgressBar::new());
                let p : &gtk::ProgressBar = &procs[i];
                let l = gtk::Label::new(Some(&format!("{}", i)));
                let horizontal_layout = gtk::Box::new(gtk::Orientation::Horizontal, 0);

                p.set_text(Some(&format!("{:.2} %", pro.get_cpu_usage() * 100.)));
                p.set_show_text(true);
                p.set_fraction(pro.get_cpu_usage() as f64);
                horizontal_layout.pack_start(&l, false, false, 5);
                horizontal_layout.pack_start(p, true, true, 5);
                vertical_layout.add(&horizontal_layout);
            } else {
                procs.push(gtk::ProgressBar::new());
                let p : &gtk::ProgressBar = &procs[i];

                p.set_text(Some(&format!("{:.2} %", pro.get_cpu_usage() * 100.)));
                p.set_show_text(true);
                p.set_fraction(pro.get_cpu_usage() as f64);

                vertical_layout.add(p);
                vertical_layout.pack_start(&gtk::Label::new(Some("Process usage")), false,
                                           false, 15);
                total = true;
            }
            i += 1;
        }

        let vertical_layout : Widget = vertical_layout.upcast();
        note.create_tab("System usage", &vertical_layout);
        let vertical_layout : gtk::Box = vertical_layout.downcast::<gtk::Box>().unwrap();

        let mut tmp = DisplaySysInfo {
            procs: Rc::new(RefCell::new(procs)),
            ram: ram,
            swap: swap,
            vertical_layout: vertical_layout,
        };
        tmp.update_ram_display(&sys1.borrow());
        tmp
    }

    pub fn update_ram_display(&mut self, sys: &sysinfo::System) {
        let total = sys.get_total_memory();
        let used = sys.get_used_memory();
        let disp = if total < 100000 {
            format!("{} / {}KB", used, total)
        } else if total < 10000000 {
            format!("{} / {}MB", used / 1000, total / 1000)
        } else if total < 10000000000 {
            format!("{} / {}GB", used / 1000000, total / 1000000)
        } else {
            format!("{} / {}TB", used / 1000000000, total / 1000000000)
        };

        self.ram.set_text(Some(&disp));
        self.ram.set_fraction(used as f64 / total as f64);

        let total = sys.get_total_swap();
        let used = total - sys.get_used_swap();
        let disp = if total < 100000 {
            format!("{} / {}KB", used, total)
        } else if total < 10000000 {
            format!("{} / {}MB", used / 1000, total / 1000)
        } else if total < 10000000000 {
            format!("{} / {}GB", used / 1000000, total / 1000000)
        } else {
            format!("{} / {}TB", used / 1000000000, total / 1000000000)
        };

        self.swap.set_text(Some(&disp));
        self.swap.set_fraction(used as f64 / total as f64);
    }

    pub fn update_process_display(&mut self, sys: &sysinfo::System) {
        let v = &*self.procs.borrow_mut();
        let mut i = 0;

        for pro in sys.get_processor_list() {
            v[i].set_text(Some(&format!("{:.1} %", pro.get_cpu_usage() * 100.)));
            v[i].set_show_text(true);
            v[i].set_fraction(pro.get_cpu_usage() as f64);
            i += 1;
        }
    }
}

fn main() {
    gtk::init().expect("GTK couldn't start normally");

    let window = gtk::Window::new(gtk::WindowType::Toplevel);
    let sys = Rc::new(RefCell::new(sysinfo::System::new()));
    let mut note = NoteBook::new();
    let mut procs = Procs::new((*sys.borrow()).get_process_list(), &mut note);
    let current_pid = procs.current_pid.clone();
    let sys1 = sys.clone();

    window.set_title("Process viewer");
    window.set_position(gtk::WindowPosition::Center);

    window.connect_delete_event(|_, _| {
        gtk::main_quit();
        Inhibit(true)
    });

    sys.borrow_mut().refresh_all();
    procs.kill_button.connect_clicked(move |_| {
        let sys = sys1.borrow();
        if let Some(process) = current_pid.get().and_then(|pid| sys.get_process(pid)) {
            process.kill(Signal::Kill);
        }
    });

    let mut display_tab = DisplaySysInfo::new(sys.clone(), &mut note);

    gtk::timeout_add(1500, move || {
        // first part, deactivate sorting
        let sorted = procs.list_store.get_sort_column_id();
        procs.list_store.set_unsorted();

        // we update the tree view
        update_window(&mut procs.list_store, &sys, &mut display_tab);

        // we re-enable the sorting
        if let Some((col, order)) = sorted {
            procs.list_store.set_sort_column_id(col, order);
        }
        glib::Continue(true)
    });

    window.add(&note.notebook);
    window.show_all();
    gtk::main();
}
<|endoftext|>