<commit_before>// Copyright 2018 Pants project contributors (see CONTRIBUTORS.md).
// Licensed under the Apache License, Version 2.0 (see LICENSE).

#![deny(warnings)]
// Enable all clippy lints except for many of the pedantic ones. It's a shame this needs to be copied and pasted across crates, but there doesn't appear to be a way to include inner attributes from a common source.
#![deny(
  clippy::all,
  clippy::default_trait_access,
  clippy::expl_impl_clone_on_copy,
  clippy::if_not_else,
  clippy::needless_continue,
  clippy::unseparated_literal_suffix,
  // TODO: Falsely triggers for async/await:
  //   see https://github.com/rust-lang/rust-clippy/issues/5360
  // clippy::used_underscore_binding
)]
// It is often more clear to show that nothing is being moved.
#![allow(clippy::match_ref_pats)]
// Subjective style.
#![allow(
  clippy::len_without_is_empty,
  clippy::redundant_field_names,
  clippy::too_many_arguments
)]
// Default isn't as big a deal as people seem to think it is.
#![allow(clippy::new_without_default, clippy::new_ret_no_self)]
// Arc<Mutex> can be more clear than needing to grok Orderings:
#![allow(clippy::mutex_atomic)]

use serde_derive::Serialize;

/// A concrete data representation of a duration.
/// Unlike std::time::Duration, it doesn't hide how the time is stored as the purpose of this
/// `struct` is to expose it.
///
/// This type can be serialized with serde.
///
/// This type can be converted from and into a `std::time::Duration` as this should be the goto
/// data representation for a `Duration` when one isn't concerned about serialization.
///
/// It can be used to represent a timestamp (as a duration since the unix epoch) or simply a
/// duration between two arbitrary timestamps.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
pub struct Duration {
  /// How many seconds did this `Duration` last?
  pub secs: u64,
  /// How many sub-second nanoseconds did this `Duration` last?
  pub nanos: u32,
}

impl Duration {
  /// Construct a new duration with `secs` seconds and `nanos` nanoseconds
  pub fn new(secs: u64, nanos: u32) -> Self {
    Self { secs, nanos }
  }
}

impl From<std::time::Duration> for Duration {
  fn from(duration: std::time::Duration) -> Self {
    Self {
      secs: duration.as_secs(),
      nanos: duration.subsec_nanos(),
    }
  }
}

impl Into<std::time::Duration> for Duration {
  fn into(self) -> std::time::Duration {
    std::time::Duration::new(self.secs, self.nanos)
  }
}

/// A timespan
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
pub struct TimeSpan {
  /// Duration since the UNIX_EPOCH
  pub start: Duration,
  /// Duration since `start`
  pub duration: Duration,
}

impl TimeSpan {
  fn since_epoch(time: &std::time::SystemTime) -> std::time::Duration {
    time
      .duration_since(std::time::UNIX_EPOCH)
      .expect("Surely you're not before the unix epoch?")
  }

  /// Construct a TimeSpan that started at `start` and ends now.
  pub fn since(start: &std::time::SystemTime) -> TimeSpan {
    let start = Self::since_epoch(start);
    let duration = Self::since_epoch(&std::time::SystemTime::now()) - start;
    TimeSpan {
      start: start.into(),
      duration: duration.into(),
    }
  }

  /// Construct a TimeSpan that started at `start` and ends at `end`.
  pub fn from_start_and_end_systemtime(
    start: &std::time::SystemTime,
    end: &std::time::SystemTime,
  ) -> TimeSpan {
    let start = Self::since_epoch(start);
    let end = Self::since_epoch(end);
    let duration = match end.checked_sub(start) {
      Some(d) => d,
      None => {
        log::warn!("Invalid TimeSpan - start: {:?}, end: {:?}", start, end);
        std::time::Duration::new(0, 0)
      }
    };
    TimeSpan {
      start: start.into(),
      duration: duration.into(),
    }
  }

  fn std_duration_from_protobuf_timestamp(
    t: &protobuf::well_known_types::Timestamp,
  ) -> std::time::Duration {
    std::time::Duration::new(t.seconds as u64, t.nanos as u32)
  }

  /// Construct a `TimeSpan` given a start and an end `Timestamp` from protobuf
  pub fn from_start_and_end(
    start: &protobuf::well_known_types::Timestamp,
    end: &protobuf::well_known_types::Timestamp,
    time_span_description: &str,
  ) -> Result<Self, String> {
    let start = Self::std_duration_from_protobuf_timestamp(start);
    let end = Self::std_duration_from_protobuf_timestamp(end);
    let time_span = end.checked_sub(start).map(|duration| TimeSpan {
      start: start.into(),
      duration: duration.into(),
    });
    time_span.ok_or_else(|| {
      format!(
        "Got negative {} time: {:?} - {:?}",
        time_span_description, end, start
      )
    })
  }
}

#[cfg(test)]
mod tests;
<commit_msg>Add check for invalid concrete time dates (#10349)<commit_after>// Copyright 2018 Pants project contributors (see CONTRIBUTORS.md).
// Licensed under the Apache License, Version 2.0 (see LICENSE).

#![deny(warnings)]
// Enable all clippy lints except for many of the pedantic ones. It's a shame this needs to be copied and pasted across crates, but there doesn't appear to be a way to include inner attributes from a common source.
#![deny(
  clippy::all,
  clippy::default_trait_access,
  clippy::expl_impl_clone_on_copy,
  clippy::if_not_else,
  clippy::needless_continue,
  clippy::unseparated_literal_suffix,
  // TODO: Falsely triggers for async/await:
  //   see https://github.com/rust-lang/rust-clippy/issues/5360
  // clippy::used_underscore_binding
)]
// It is often more clear to show that nothing is being moved.
#![allow(clippy::match_ref_pats)]
// Subjective style.
#![allow(
  clippy::len_without_is_empty,
  clippy::redundant_field_names,
  clippy::too_many_arguments
)]
// Default isn't as big a deal as people seem to think it is.
#![allow(clippy::new_without_default, clippy::new_ret_no_self)]
// Arc<Mutex> can be more clear than needing to grok Orderings:
#![allow(clippy::mutex_atomic)]

use serde_derive::Serialize;

/// A concrete data representation of a duration.
/// Unlike std::time::Duration, it doesn't hide how the time is stored as the purpose of this
/// `struct` is to expose it.
///
/// This type can be serialized with serde.
///
/// This type can be converted from and into a `std::time::Duration` as this should be the goto
/// data representation for a `Duration` when one isn't concerned about serialization.
///
/// It can be used to represent a timestamp (as a duration since the unix epoch) or simply a
/// duration between two arbitrary timestamps.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
pub struct Duration {
  /// How many seconds did this `Duration` last?
  pub secs: u64,
  /// How many sub-second nanoseconds did this `Duration` last?
  pub nanos: u32,
}

impl Duration {
  /// Construct a new duration with `secs` seconds and `nanos` nanoseconds
  pub fn new(secs: u64, nanos: u32) -> Self {
    Self { secs, nanos }
  }
}

impl From<std::time::Duration> for Duration {
  fn from(duration: std::time::Duration) -> Self {
    Self {
      secs: duration.as_secs(),
      nanos: duration.subsec_nanos(),
    }
  }
}

impl Into<std::time::Duration> for Duration {
  fn into(self) -> std::time::Duration {
    std::time::Duration::new(self.secs, self.nanos)
  }
}

/// A timespan
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
pub struct TimeSpan {
  /// Duration since the UNIX_EPOCH
  pub start: Duration,
  /// Duration since `start`
  pub duration: Duration,
}

impl TimeSpan {
  fn since_epoch(time: &std::time::SystemTime) -> std::time::Duration {
    time
      .duration_since(std::time::UNIX_EPOCH)
      .expect("Surely you're not before the unix epoch?")
  }

  /// Construct a TimeSpan that started at `start` and ends now.
  pub fn since(start: &std::time::SystemTime) -> TimeSpan {
    let start = Self::since_epoch(start);
    let duration = Self::since_epoch(&std::time::SystemTime::now()) - start;
    TimeSpan {
      start: start.into(),
      duration: duration.into(),
    }
  }

  /// Construct a TimeSpan that started at `start` and ends at `end`.
  pub fn from_start_and_end_systemtime(
    start: &std::time::SystemTime,
    end: &std::time::SystemTime,
  ) -> TimeSpan {
    let start = Self::since_epoch(start);
    let end = Self::since_epoch(end);
    let duration = match end.checked_sub(start) {
      Some(d) => d,
      None => {
        log::debug!("Invalid TimeSpan - start: {:?}, end: {:?}", start, end);
        std::time::Duration::new(0, 0)
      }
    };
    TimeSpan {
      start: start.into(),
      duration: duration.into(),
    }
  }

  fn std_duration_from_protobuf_timestamp(
    t: &protobuf::well_known_types::Timestamp,
  ) -> std::time::Duration {
    std::time::Duration::new(t.seconds as u64, t.nanos as u32)
  }

  /// Construct a `TimeSpan` given a start and an end `Timestamp` from protobuf
  pub fn from_start_and_end(
    start: &protobuf::well_known_types::Timestamp,
    end: &protobuf::well_known_types::Timestamp,
    time_span_description: &str,
  ) -> Result<Self, String> {
    let start = Self::std_duration_from_protobuf_timestamp(start);
    let end = Self::std_duration_from_protobuf_timestamp(end);
    let time_span = end.checked_sub(start).map(|duration| TimeSpan {
      start: start.into(),
      duration: duration.into(),
    });
    time_span.ok_or_else(|| {
      format!(
        "Got negative {} time: {:?} - {:?}",
        time_span_description, end, start
      )
    })
  }
}

#[cfg(test)]
mod tests;
<|endoftext|>


<commit_before>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Implementation of Rust stack unwinding
//!
//! For background on exception handling and stack unwinding please see
//! "Exception Handling in LLVM" (llvm.org/docs/ExceptionHandling.html) and
//! documents linked from it.
//! These are also good reads:
//!     http://mentorembedded.github.io/cxx-abi/abi-eh.html
//!     http://monoinfinito.wordpress.com/series/exception-handling-in-c/
//!     http://www.airs.com/blog/index.php?s=exception+frames
//!
//! ## A brief summary
//!
//! Exception handling happens in two phases: a search phase and a cleanup phase.
//!
//! In both phases the unwinder walks stack frames from top to bottom using
//! information from the stack frame unwind sections of the current process's
//! modules ("module" here refers to an OS module, i.e. an executable or a
//! dynamic library).
//!
//! For each stack frame, it invokes the associated "personality routine", whose
//! address is also stored in the unwind info section.
//!
//! In the search phase, the job of a personality routine is to examine exception
//! object being thrown, and to decide whether it should be caught at that stack
//! frame.  Once the handler frame has been identified, cleanup phase begins.
//!
//! In the cleanup phase, personality routines invoke cleanup code associated
//! with their stack frames (i.e. destructors).  Once stack has been unwound down
//! to the handler frame level, unwinding stops and the last personality routine
//! transfers control to its catch block.
//!
//! ## Frame unwind info registration
//!
//! Each module has its own frame unwind info section (usually ".eh_frame"), and
//! unwinder needs to know about all of them in order for unwinding to be able to
//! cross module boundaries.
//!
//! On some platforms, like Linux, this is achieved by dynamically enumerating
//! currently loaded modules via the dl_iterate_phdr() API and finding all
//! .eh_frame sections.
//!
//! Others, like Windows, require modules to actively register their unwind info
//! sections by calling __register_frame_info() API at startup.  In the latter
//! case it is essential that there is only one copy of the unwinder runtime in
//! the process.  This is usually achieved by linking to the dynamic version of
//! the unwind runtime.
//!
//! Currently Rust uses unwind runtime provided by libgcc.

#![allow(dead_code)]
#![allow(unused_imports)]

use prelude::v1::*;

use any::Any;
use boxed;
use cmp;
use panicking::{self,PANIC_COUNT};
use fmt;
use intrinsics;
use mem;
use sync::atomic::{self, Ordering};
use sys_common::mutex::Mutex;

// The actual unwinding implementation is cfg'd here, and we've got two current
// implementations. One goes through SEH on Windows and the other goes through
// libgcc via the libunwind-like API.

// i686-pc-windows-msvc
#[cfg(all(windows, target_arch = "x86", target_env = "msvc"))]
#[path = "seh.rs"] #[doc(hidden)]
pub mod imp;

// x86_64-pc-windows-*
#[cfg(all(windows, target_arch = "x86_64"))]
#[path = "seh64_gnu.rs"] #[doc(hidden)]
pub mod imp;

// i686-pc-windows-gnu and all others
#[cfg(any(unix, all(windows, target_arch = "x86", target_env = "gnu")))]
#[path = "gcc.rs"] #[doc(hidden)]
pub mod imp;

/// Invoke a closure, capturing the cause of panic if one occurs.
///
/// This function will return `Ok(())` if the closure did not panic, and will
/// return `Err(cause)` if the closure panics. The `cause` returned is the
/// object with which panic was originally invoked.
///
/// This function also is unsafe for a variety of reasons:
///
/// * This is not safe to call in a nested fashion. The unwinding
///   interface for Rust is designed to have at most one try/catch block per
///   thread, not multiple. No runtime checking is currently performed to uphold
///   this invariant, so this function is not safe. A nested try/catch block
///   may result in corruption of the outer try/catch block's state, especially
///   if this is used within a thread itself.
///
/// * It is not sound to trigger unwinding while already unwinding. Rust threads
///   have runtime checks in place to ensure this invariant, but it is not
///   guaranteed that a rust thread is in place when invoking this function.
///   Unwinding twice can lead to resource leaks where some destructors are not
///   run.
pub unsafe fn try<F: FnOnce()>(f: F) -> Result<(), Box<Any + Send>> {
    let mut f = Some(f);
    return inner_try(try_fn::<F>, &mut f as *mut _ as *mut u8);

    // If an inner function were not used here, then this generic function `try`
    // uses the native symbol `rust_try`, for which the code is statically
    // linked into the standard library. This means that the DLL for the
    // standard library must have `rust_try` as an exposed symbol that
    // downstream crates can link against (because monomorphizations of `try` in
    // downstream crates will have a reference to the `rust_try` symbol).
    //
    // On MSVC this requires the symbol `rust_try` to be tagged with
    // `dllexport`, but it's easier to not have conditional `src/rt/rust_try.ll`
    // files and instead just have this non-generic shim the compiler can take
    // care of exposing correctly.
    unsafe fn inner_try(f: fn(*mut u8), data: *mut u8)
                        -> Result<(), Box<Any + Send>> {
        PANIC_COUNT.with(|s| {
            let prev = s.get();
            s.set(0);
            let ep = intrinsics::try(f, data);
            s.set(prev);
            if ep.is_null() {
                Ok(())
            } else {
                Err(imp::cleanup(ep))
            }
        })
    }

    fn try_fn<F: FnOnce()>(opt_closure: *mut u8) {
        let opt_closure = opt_closure as *mut Option<F>;
        unsafe { (*opt_closure).take().unwrap()(); }
    }

    extern {
        // Rust's try-catch
        // When f(...) returns normally, the return value is null.
        // When f(...) throws, the return value is a pointer to the caught
        // exception object.
        fn rust_try(f: extern fn(*mut u8),
                    data: *mut u8) -> *mut u8;
    }
}

/// Determines whether the current thread is unwinding because of panic.
pub fn panicking() -> bool {
    PANIC_COUNT.with(|s| s.get() != 0)
}

// An uninlined, unmangled function upon which to slap yer breakpoints
#[inline(never)]
#[no_mangle]
#[allow(private_no_mangle_fns)]
fn rust_panic(cause: Box<Any + Send + 'static>) -> ! {
    unsafe {
        imp::panic(cause)
    }
}

#[cfg(not(test))]
/// Entry point of panic from the libcore crate.
#[lang = "panic_fmt"]
#[unwind]
pub extern fn rust_begin_unwind(msg: fmt::Arguments,
                                file: &'static str, line: u32) -> ! {
    begin_unwind_fmt(msg, &(file, line))
}

/// The entry point for unwinding with a formatted message.
///
/// This is designed to reduce the amount of code required at the call
/// site as much as possible (so that `panic!()` has as low an impact
/// on (e.g.) the inlining of other functions as possible), by moving
/// the actual formatting into this shared place.
#[inline(never)] #[cold]
pub fn begin_unwind_fmt(msg: fmt::Arguments, file_line: &(&'static str, u32)) -> ! {
    use fmt::Write;

    // We do two allocations here, unfortunately. But (a) they're
    // required with the current scheme, and (b) we don't handle
    // panic + OOM properly anyway (see comment in begin_unwind
    // below).

    let mut s = String::new();
    let _ = s.write_fmt(msg);
    begin_unwind_inner(Box::new(s), file_line)
}

/// This is the entry point of unwinding for panic!() and assert!().
#[inline(never)] #[cold] // avoid code bloat at the call sites as much as possible
pub fn begin_unwind<M: Any + Send>(msg: M, file_line: &(&'static str, u32)) -> ! {
    // Note that this should be the only allocation performed in this code path.
    // Currently this means that panic!() on OOM will invoke this code path,
    // but then again we're not really ready for panic on OOM anyway. If
    // we do start doing this, then we should propagate this allocation to
    // be performed in the parent of this thread instead of the thread that's
    // panicking.

    // see below for why we do the `Any` coercion here.
    begin_unwind_inner(Box::new(msg), file_line)
}

/// The core of the unwinding.
///
/// This is non-generic to avoid instantiation bloat in other crates
/// (which makes compilation of small crates noticeably slower). (Note:
/// we need the `Any` object anyway, we're not just creating it to
/// avoid being generic.)
///
/// Doing this split took the LLVM IR line counts of `fn main() { panic!()
/// }` from ~1900/3700 (-O/no opts) to 180/590.
#[inline(never)] #[cold] // this is the slow path, please never inline this
fn begin_unwind_inner(msg: Box<Any + Send>,
                      file_line: &(&'static str, u32)) -> ! {
    let (file, line) = *file_line;

    // First, invoke the default panic handler.
    panicking::on_panic(&*msg, file, line);

    // Finally, perform the unwinding.
    rust_panic(msg);
}
<commit_msg>Fix MSVC stage0 with landing pads enabled<commit_after>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Implementation of Rust stack unwinding
//!
//! For background on exception handling and stack unwinding please see
//! "Exception Handling in LLVM" (llvm.org/docs/ExceptionHandling.html) and
//! documents linked from it.
//! These are also good reads:
//!     http://mentorembedded.github.io/cxx-abi/abi-eh.html
//!     http://monoinfinito.wordpress.com/series/exception-handling-in-c/
//!     http://www.airs.com/blog/index.php?s=exception+frames
//!
//! ## A brief summary
//!
//! Exception handling happens in two phases: a search phase and a cleanup phase.
//!
//! In both phases the unwinder walks stack frames from top to bottom using
//! information from the stack frame unwind sections of the current process's
//! modules ("module" here refers to an OS module, i.e. an executable or a
//! dynamic library).
//!
//! For each stack frame, it invokes the associated "personality routine", whose
//! address is also stored in the unwind info section.
//!
//! In the search phase, the job of a personality routine is to examine exception
//! object being thrown, and to decide whether it should be caught at that stack
//! frame.  Once the handler frame has been identified, cleanup phase begins.
//!
//! In the cleanup phase, personality routines invoke cleanup code associated
//! with their stack frames (i.e. destructors).  Once stack has been unwound down
//! to the handler frame level, unwinding stops and the last personality routine
//! transfers control to its catch block.
//!
//! ## Frame unwind info registration
//!
//! Each module has its own frame unwind info section (usually ".eh_frame"), and
//! unwinder needs to know about all of them in order for unwinding to be able to
//! cross module boundaries.
//!
//! On some platforms, like Linux, this is achieved by dynamically enumerating
//! currently loaded modules via the dl_iterate_phdr() API and finding all
//! .eh_frame sections.
//!
//! Others, like Windows, require modules to actively register their unwind info
//! sections by calling __register_frame_info() API at startup.  In the latter
//! case it is essential that there is only one copy of the unwinder runtime in
//! the process.  This is usually achieved by linking to the dynamic version of
//! the unwind runtime.
//!
//! Currently Rust uses unwind runtime provided by libgcc.

#![allow(dead_code)]
#![allow(unused_imports)]

use prelude::v1::*;

use any::Any;
use boxed;
use cmp;
use panicking::{self,PANIC_COUNT};
use fmt;
use intrinsics;
use mem;
use sync::atomic::{self, Ordering};
use sys_common::mutex::Mutex;

// The actual unwinding implementation is cfg'd here, and we've got two current
// implementations. One goes through SEH on Windows and the other goes through
// libgcc via the libunwind-like API.

// i686-pc-windows-msvc
#[cfg(all(windows, target_arch = "x86", target_env = "msvc"))]
#[path = "seh.rs"] #[doc(hidden)]
pub mod imp;

// SNAP: i686-pc-windows-gnu
#[cfg(all(stage0, windows, target_arch = "x86_64", target_env = "gnu"))]
#[path = "seh64_gnu.rs"] #[doc(hidden)]
pub mod imp;

// SNAP: x86_64-pc-windows-msvc
#[cfg(all(stage0, windows, target_arch = "x86_64", target_env = "msvc"))]
#[path = "seh.rs"] #[doc(hidden)]
pub mod imp;

// x86_64-pc-windows-*
#[cfg(all(not(stage0), windows, target_arch = "x86_64"))]
#[path = "seh64_gnu.rs"] #[doc(hidden)]
pub mod imp;

// i686-pc-windows-gnu and all others
#[cfg(any(unix, all(windows, target_arch = "x86", target_env = "gnu")))]
#[path = "gcc.rs"] #[doc(hidden)]
pub mod imp;

/// Invoke a closure, capturing the cause of panic if one occurs.
///
/// This function will return `Ok(())` if the closure did not panic, and will
/// return `Err(cause)` if the closure panics. The `cause` returned is the
/// object with which panic was originally invoked.
///
/// This function also is unsafe for a variety of reasons:
///
/// * This is not safe to call in a nested fashion. The unwinding
///   interface for Rust is designed to have at most one try/catch block per
///   thread, not multiple. No runtime checking is currently performed to uphold
///   this invariant, so this function is not safe. A nested try/catch block
///   may result in corruption of the outer try/catch block's state, especially
///   if this is used within a thread itself.
///
/// * It is not sound to trigger unwinding while already unwinding. Rust threads
///   have runtime checks in place to ensure this invariant, but it is not
///   guaranteed that a rust thread is in place when invoking this function.
///   Unwinding twice can lead to resource leaks where some destructors are not
///   run.
pub unsafe fn try<F: FnOnce()>(f: F) -> Result<(), Box<Any + Send>> {
    let mut f = Some(f);
    return inner_try(try_fn::<F>, &mut f as *mut _ as *mut u8);

    // If an inner function were not used here, then this generic function `try`
    // uses the native symbol `rust_try`, for which the code is statically
    // linked into the standard library. This means that the DLL for the
    // standard library must have `rust_try` as an exposed symbol that
    // downstream crates can link against (because monomorphizations of `try` in
    // downstream crates will have a reference to the `rust_try` symbol).
    //
    // On MSVC this requires the symbol `rust_try` to be tagged with
    // `dllexport`, but it's easier to not have conditional `src/rt/rust_try.ll`
    // files and instead just have this non-generic shim the compiler can take
    // care of exposing correctly.
    unsafe fn inner_try(f: fn(*mut u8), data: *mut u8)
                        -> Result<(), Box<Any + Send>> {
        PANIC_COUNT.with(|s| {
            let prev = s.get();
            s.set(0);
            let ep = intrinsics::try(f, data);
            s.set(prev);
            if ep.is_null() {
                Ok(())
            } else {
                Err(imp::cleanup(ep))
            }
        })
    }

    fn try_fn<F: FnOnce()>(opt_closure: *mut u8) {
        let opt_closure = opt_closure as *mut Option<F>;
        unsafe { (*opt_closure).take().unwrap()(); }
    }

    extern {
        // Rust's try-catch
        // When f(...) returns normally, the return value is null.
        // When f(...) throws, the return value is a pointer to the caught
        // exception object.
        fn rust_try(f: extern fn(*mut u8),
                    data: *mut u8) -> *mut u8;
    }
}

/// Determines whether the current thread is unwinding because of panic.
pub fn panicking() -> bool {
    PANIC_COUNT.with(|s| s.get() != 0)
}

// An uninlined, unmangled function upon which to slap yer breakpoints
#[inline(never)]
#[no_mangle]
#[allow(private_no_mangle_fns)]
fn rust_panic(cause: Box<Any + Send + 'static>) -> ! {
    unsafe {
        imp::panic(cause)
    }
}

#[cfg(not(test))]
/// Entry point of panic from the libcore crate.
#[lang = "panic_fmt"]
#[unwind]
pub extern fn rust_begin_unwind(msg: fmt::Arguments,
                                file: &'static str, line: u32) -> ! {
    begin_unwind_fmt(msg, &(file, line))
}

/// The entry point for unwinding with a formatted message.
///
/// This is designed to reduce the amount of code required at the call
/// site as much as possible (so that `panic!()` has as low an impact
/// on (e.g.) the inlining of other functions as possible), by moving
/// the actual formatting into this shared place.
#[inline(never)] #[cold]
pub fn begin_unwind_fmt(msg: fmt::Arguments, file_line: &(&'static str, u32)) -> ! {
    use fmt::Write;

    // We do two allocations here, unfortunately. But (a) they're
    // required with the current scheme, and (b) we don't handle
    // panic + OOM properly anyway (see comment in begin_unwind
    // below).

    let mut s = String::new();
    let _ = s.write_fmt(msg);
    begin_unwind_inner(Box::new(s), file_line)
}

/// This is the entry point of unwinding for panic!() and assert!().
#[inline(never)] #[cold] // avoid code bloat at the call sites as much as possible
pub fn begin_unwind<M: Any + Send>(msg: M, file_line: &(&'static str, u32)) -> ! {
    // Note that this should be the only allocation performed in this code path.
    // Currently this means that panic!() on OOM will invoke this code path,
    // but then again we're not really ready for panic on OOM anyway. If
    // we do start doing this, then we should propagate this allocation to
    // be performed in the parent of this thread instead of the thread that's
    // panicking.

    // see below for why we do the `Any` coercion here.
    begin_unwind_inner(Box::new(msg), file_line)
}

/// The core of the unwinding.
///
/// This is non-generic to avoid instantiation bloat in other crates
/// (which makes compilation of small crates noticeably slower). (Note:
/// we need the `Any` object anyway, we're not just creating it to
/// avoid being generic.)
///
/// Doing this split took the LLVM IR line counts of `fn main() { panic!()
/// }` from ~1900/3700 (-O/no opts) to 180/590.
#[inline(never)] #[cold] // this is the slow path, please never inline this
fn begin_unwind_inner(msg: Box<Any + Send>,
                      file_line: &(&'static str, u32)) -> ! {
    let (file, line) = *file_line;

    // First, invoke the default panic handler.
    panicking::on_panic(&*msg, file, line);

    // Finally, perform the unwinding.
    rust_panic(msg);
}
<|endoftext|>


<commit_before>//! Common types for room directory endpoints.

use std::fmt;

use js_int::UInt;
use ruma_identifiers::{MxcUri, RoomAliasId, RoomId, RoomName};
use ruma_serde::Outgoing;
use serde::{
    de::{Error, MapAccess, Visitor},
    ser::SerializeStruct,
    Deserialize, Deserializer, Serialize, Serializer,
};
use serde_json::Value as JsonValue;

/// A chunk of a room list response, describing one room.
///
/// To create an instance of this type, first create a `PublicRoomsChunkInit` and convert it via
/// `PublicRoomsChunk::from` / `.into()`.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct PublicRoomsChunk {
    /// Aliases of the room.
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub aliases: Vec<Box<RoomAliasId>>,

    /// The canonical alias of the room, if any.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub canonical_alias: Option<Box<RoomAliasId>>,

    /// The name of the room, if any.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub name: Option<Box<RoomName>>,

    /// The number of members joined to the room.
    pub num_joined_members: UInt,

    /// The ID of the room.
    pub room_id: Box<RoomId>,

    /// The topic of the room, if any.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub topic: Option<String>,

    /// Whether the room may be viewed by guest users without joining.
    pub world_readable: bool,

    /// Whether guest users may join the room and participate in it.
    ///
    /// If they can, they will be subject to ordinary power level rules like any other user.
    pub guest_can_join: bool,

    /// The URL for the room's avatar, if one is set.
    ///
    /// If you activate the `compat` feature, this field being an empty string in JSON will result
    /// in `None` here during deserialization.
    #[serde(skip_serializing_if = "Option::is_none")]
    #[cfg_attr(
        feature = "compat",
        serde(default, deserialize_with = "ruma_serde::empty_string_as_none")
    )]
    pub avatar_url: Option<Box<MxcUri>>,
}

/// Initial set of mandatory fields of `PublicRoomsChunk`.
///
/// This struct will not be updated even if additional fields are added to `PublicRoomsChunk` in a
/// new (non-breaking) release of the Matrix specification.
#[derive(Debug)]
#[allow(clippy::exhaustive_structs)]
pub struct PublicRoomsChunkInit {
    /// The number of members joined to the room.
    pub num_joined_members: UInt,

    /// The ID of the room.
    pub room_id: Box<RoomId>,

    /// Whether the room may be viewed by guest users without joining.
    pub world_readable: bool,

    /// Whether guest users may join the room and participate in it.
    ///
    /// If they can, they will be subject to ordinary power level rules like any other user.
    pub guest_can_join: bool,
}

impl From<PublicRoomsChunkInit> for PublicRoomsChunk {
    fn from(init: PublicRoomsChunkInit) -> Self {
        let PublicRoomsChunkInit { num_joined_members, room_id, world_readable, guest_can_join } =
            init;

        Self {
            aliases: Vec::new(),
            canonical_alias: None,
            name: None,
            num_joined_members,
            room_id,
            topic: None,
            world_readable,
            guest_can_join,
            avatar_url: None,
        }
    }
}

/// A filter for public rooms lists
#[derive(Clone, Debug, Default, Outgoing, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[incoming_derive(Default)]
pub struct Filter<'a> {
    /// A string to search for in the room metadata, e.g. name, topic, canonical alias etc.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub generic_search_term: Option<&'a str>,
}

impl Filter<'_> {
    /// Creates an empty `Filter`.
    pub fn new() -> Self {
        Default::default()
    }

    /// Returns `true` if the filter is empty.
    pub fn is_empty(&self) -> bool {
        self.generic_search_term.is_none()
    }
}

/// Information about which networks/protocols from application services on the
/// homeserver from which to request rooms.
#[derive(Clone, Debug, PartialEq, Eq, Outgoing)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[incoming_derive(Clone, PartialEq, Eq, !Deserialize)]
pub enum RoomNetwork<'a> {
    /// Return rooms from the Matrix network.
    Matrix,

    /// Return rooms from all the networks/protocols the homeserver knows about.
    All,

    /// Return rooms from a specific third party network/protocol.
    ThirdParty(&'a str),
}

impl<'a> Default for RoomNetwork<'a> {
    fn default() -> Self {
        RoomNetwork::Matrix
    }
}

impl<'a> Serialize for RoomNetwork<'a> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let mut state;
        match self {
            Self::Matrix => {
                state = serializer.serialize_struct("RoomNetwork", 0)?;
            }
            Self::All => {
                state = serializer.serialize_struct("RoomNetwork", 1)?;
                state.serialize_field("include_all_networks", &true)?;
            }
            Self::ThirdParty(network) => {
                state = serializer.serialize_struct("RoomNetwork", 1)?;
                state.serialize_field("third_party_instance_id", network)?;
            }
        }
        state.end()
    }
}

impl<'de> Deserialize<'de> for IncomingRoomNetwork {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserializer.deserialize_map(RoomNetworkVisitor)
    }
}

struct RoomNetworkVisitor;
impl<'de> Visitor<'de> for RoomNetworkVisitor {
    type Value = IncomingRoomNetwork;

    fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        formatter.write_str("Network selection")
    }

    fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
    where
        M: MapAccess<'de>,
    {
        let mut include_all_networks = false;
        let mut third_party_instance_id = None;
        while let Some((key, value)) = access.next_entry::<String, JsonValue>()? {
            match key.as_str() {
                "include_all_networks" => {
                    include_all_networks = match value.as_bool() {
                        Some(b) => b,
                        _ => false,
                    }
                }
                "third_party_instance_id" => {
                    third_party_instance_id = value.as_str().map(|v| v.to_owned())
                }
                _ => {}
            };
        }

        if include_all_networks {
            if third_party_instance_id.is_none() {
                Ok(IncomingRoomNetwork::All)
            } else {
                Err(M::Error::custom(
                    "`include_all_networks = true` and `third_party_instance_id` are mutually exclusive.",
                ))
            }
        } else {
            Ok(match third_party_instance_id {
                Some(network) => IncomingRoomNetwork::ThirdParty(network),
                None => IncomingRoomNetwork::Matrix,
            })
        }
    }
}

#[cfg(test)]
mod tests {
    use serde_json::{from_value as from_json_value, json, to_value as to_json_value};

    use super::{IncomingRoomNetwork, RoomNetwork};

    #[test]
    fn serialize_matrix_network_only() {
        let json = json!({});
        assert_eq!(to_json_value(RoomNetwork::Matrix).unwrap(), json);
    }

    #[test]
    fn deserialize_matrix_network_only() {
        let json = json!({ "include_all_networks": false });
        assert_eq!(
            from_json_value::<IncomingRoomNetwork>(json).unwrap(),
            IncomingRoomNetwork::Matrix
        );
    }

    #[test]
    fn serialize_default_network_is_empty() {
        let json = json!({});
        assert_eq!(to_json_value(RoomNetwork::default()).unwrap(), json);
    }

    #[test]
    fn deserialize_empty_network_is_default() {
        let json = json!({});
        assert_eq!(
            from_json_value::<IncomingRoomNetwork>(json).unwrap(),
            IncomingRoomNetwork::Matrix
        );
    }

    #[test]
    fn serialize_include_all_networks() {
        let json = json!({ "include_all_networks": true });
        assert_eq!(to_json_value(RoomNetwork::All).unwrap(), json);
    }

    #[test]
    fn deserialize_include_all_networks() {
        let json = json!({ "include_all_networks": true });
        assert_eq!(from_json_value::<IncomingRoomNetwork>(json).unwrap(), IncomingRoomNetwork::All);
    }

    #[test]
    fn serialize_third_party_network() {
        let json = json!({ "third_party_instance_id": "freenode" });
        assert_eq!(to_json_value(RoomNetwork::ThirdParty("freenode")).unwrap(), json);
    }

    #[test]
    fn deserialize_third_party_network() {
        let json = json!({ "third_party_instance_id": "freenode" });
        assert_eq!(
            from_json_value::<IncomingRoomNetwork>(json).unwrap(),
            IncomingRoomNetwork::ThirdParty("freenode".into())
        );
    }

    #[test]
    fn deserialize_include_all_networks_and_third_party_exclusivity() {
        let json = json!({ "include_all_networks": true, "third_party_instance_id": "freenode" });
        assert_eq!(
            from_json_value::<IncomingRoomNetwork>(json).unwrap_err().to_string().as_str(),
            "`include_all_networks = true` and `third_party_instance_id` are mutually exclusive."
        );
    }
}
<commit_msg>common: Add compatibility hack for empty-string canonical_alias<commit_after>//! Common types for room directory endpoints.

use std::fmt;

use js_int::UInt;
use ruma_identifiers::{MxcUri, RoomAliasId, RoomId, RoomName};
use ruma_serde::Outgoing;
use serde::{
    de::{Error, MapAccess, Visitor},
    ser::SerializeStruct,
    Deserialize, Deserializer, Serialize, Serializer,
};
use serde_json::Value as JsonValue;

/// A chunk of a room list response, describing one room.
///
/// To create an instance of this type, first create a `PublicRoomsChunkInit` and convert it via
/// `PublicRoomsChunk::from` / `.into()`.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct PublicRoomsChunk {
    /// Aliases of the room.
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub aliases: Vec<Box<RoomAliasId>>,

    /// The canonical alias of the room, if any.
    #[serde(skip_serializing_if = "Option::is_none")]
    #[cfg_attr(
        feature = "compat",
        serde(default, deserialize_with = "ruma_serde::empty_string_as_none")
    )]
    pub canonical_alias: Option<Box<RoomAliasId>>,

    /// The name of the room, if any.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub name: Option<Box<RoomName>>,

    /// The number of members joined to the room.
    pub num_joined_members: UInt,

    /// The ID of the room.
    pub room_id: Box<RoomId>,

    /// The topic of the room, if any.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub topic: Option<String>,

    /// Whether the room may be viewed by guest users without joining.
    pub world_readable: bool,

    /// Whether guest users may join the room and participate in it.
    ///
    /// If they can, they will be subject to ordinary power level rules like any other user.
    pub guest_can_join: bool,

    /// The URL for the room's avatar, if one is set.
    ///
    /// If you activate the `compat` feature, this field being an empty string in JSON will result
    /// in `None` here during deserialization.
    #[serde(skip_serializing_if = "Option::is_none")]
    #[cfg_attr(
        feature = "compat",
        serde(default, deserialize_with = "ruma_serde::empty_string_as_none")
    )]
    pub avatar_url: Option<Box<MxcUri>>,
}

/// Initial set of mandatory fields of `PublicRoomsChunk`.
///
/// This struct will not be updated even if additional fields are added to `PublicRoomsChunk` in a
/// new (non-breaking) release of the Matrix specification.
#[derive(Debug)]
#[allow(clippy::exhaustive_structs)]
pub struct PublicRoomsChunkInit {
    /// The number of members joined to the room.
    pub num_joined_members: UInt,

    /// The ID of the room.
    pub room_id: Box<RoomId>,

    /// Whether the room may be viewed by guest users without joining.
    pub world_readable: bool,

    /// Whether guest users may join the room and participate in it.
    ///
    /// If they can, they will be subject to ordinary power level rules like any other user.
    pub guest_can_join: bool,
}

impl From<PublicRoomsChunkInit> for PublicRoomsChunk {
    fn from(init: PublicRoomsChunkInit) -> Self {
        let PublicRoomsChunkInit { num_joined_members, room_id, world_readable, guest_can_join } =
            init;

        Self {
            aliases: Vec::new(),
            canonical_alias: None,
            name: None,
            num_joined_members,
            room_id,
            topic: None,
            world_readable,
            guest_can_join,
            avatar_url: None,
        }
    }
}

/// A filter for public rooms lists
#[derive(Clone, Debug, Default, Outgoing, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[incoming_derive(Default)]
pub struct Filter<'a> {
    /// A string to search for in the room metadata, e.g. name, topic, canonical alias etc.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub generic_search_term: Option<&'a str>,
}

impl Filter<'_> {
    /// Creates an empty `Filter`.
    pub fn new() -> Self {
        Default::default()
    }

    /// Returns `true` if the filter is empty.
    pub fn is_empty(&self) -> bool {
        self.generic_search_term.is_none()
    }
}

/// Information about which networks/protocols from application services on the
/// homeserver from which to request rooms.
#[derive(Clone, Debug, PartialEq, Eq, Outgoing)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[incoming_derive(Clone, PartialEq, Eq, !Deserialize)]
pub enum RoomNetwork<'a> {
    /// Return rooms from the Matrix network.
    Matrix,

    /// Return rooms from all the networks/protocols the homeserver knows about.
    All,

    /// Return rooms from a specific third party network/protocol.
    ThirdParty(&'a str),
}

impl<'a> Default for RoomNetwork<'a> {
    fn default() -> Self {
        RoomNetwork::Matrix
    }
}

impl<'a> Serialize for RoomNetwork<'a> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let mut state;
        match self {
            Self::Matrix => {
                state = serializer.serialize_struct("RoomNetwork", 0)?;
            }
            Self::All => {
                state = serializer.serialize_struct("RoomNetwork", 1)?;
                state.serialize_field("include_all_networks", &true)?;
            }
            Self::ThirdParty(network) => {
                state = serializer.serialize_struct("RoomNetwork", 1)?;
                state.serialize_field("third_party_instance_id", network)?;
            }
        }
        state.end()
    }
}

impl<'de> Deserialize<'de> for IncomingRoomNetwork {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserializer.deserialize_map(RoomNetworkVisitor)
    }
}

struct RoomNetworkVisitor;
impl<'de> Visitor<'de> for RoomNetworkVisitor {
    type Value = IncomingRoomNetwork;

    fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        formatter.write_str("Network selection")
    }

    fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
    where
        M: MapAccess<'de>,
    {
        let mut include_all_networks = false;
        let mut third_party_instance_id = None;
        while let Some((key, value)) = access.next_entry::<String, JsonValue>()? {
            match key.as_str() {
                "include_all_networks" => {
                    include_all_networks = match value.as_bool() {
                        Some(b) => b,
                        _ => false,
                    }
                }
                "third_party_instance_id" => {
                    third_party_instance_id = value.as_str().map(|v| v.to_owned())
                }
                _ => {}
            };
        }

        if include_all_networks {
            if third_party_instance_id.is_none() {
                Ok(IncomingRoomNetwork::All)
            } else {
                Err(M::Error::custom(
                    "`include_all_networks = true` and `third_party_instance_id` are mutually exclusive.",
                ))
            }
        } else {
            Ok(match third_party_instance_id {
                Some(network) => IncomingRoomNetwork::ThirdParty(network),
                None => IncomingRoomNetwork::Matrix,
            })
        }
    }
}

#[cfg(test)]
mod tests {
    use serde_json::{from_value as from_json_value, json, to_value as to_json_value};

    use super::{IncomingRoomNetwork, RoomNetwork};

    #[test]
    fn serialize_matrix_network_only() {
        let json = json!({});
        assert_eq!(to_json_value(RoomNetwork::Matrix).unwrap(), json);
    }

    #[test]
    fn deserialize_matrix_network_only() {
        let json = json!({ "include_all_networks": false });
        assert_eq!(
            from_json_value::<IncomingRoomNetwork>(json).unwrap(),
            IncomingRoomNetwork::Matrix
        );
    }

    #[test]
    fn serialize_default_network_is_empty() {
        let json = json!({});
        assert_eq!(to_json_value(RoomNetwork::default()).unwrap(), json);
    }

    #[test]
    fn deserialize_empty_network_is_default() {
        let json = json!({});
        assert_eq!(
            from_json_value::<IncomingRoomNetwork>(json).unwrap(),
            IncomingRoomNetwork::Matrix
        );
    }

    #[test]
    fn serialize_include_all_networks() {
        let json = json!({ "include_all_networks": true });
        assert_eq!(to_json_value(RoomNetwork::All).unwrap(), json);
    }

    #[test]
    fn deserialize_include_all_networks() {
        let json = json!({ "include_all_networks": true });
        assert_eq!(from_json_value::<IncomingRoomNetwork>(json).unwrap(), IncomingRoomNetwork::All);
    }

    #[test]
    fn serialize_third_party_network() {
        let json = json!({ "third_party_instance_id": "freenode" });
        assert_eq!(to_json_value(RoomNetwork::ThirdParty("freenode")).unwrap(), json);
    }

    #[test]
    fn deserialize_third_party_network() {
        let json = json!({ "third_party_instance_id": "freenode" });
        assert_eq!(
            from_json_value::<IncomingRoomNetwork>(json).unwrap(),
            IncomingRoomNetwork::ThirdParty("freenode".into())
        );
    }

    #[test]
    fn deserialize_include_all_networks_and_third_party_exclusivity() {
        let json = json!({ "include_all_networks": true, "third_party_instance_id": "freenode" });
        assert_eq!(
            from_json_value::<IncomingRoomNetwork>(json).unwrap_err().to_string().as_str(),
            "`include_all_networks = true` and `third_party_instance_id` are mutually exclusive."
        );
    }
}
<|endoftext|>


<commit_before>mod color;
mod base;
mod edge_draw;

pub use self::color::Color;
pub use self::base::{BaseDraw, Drawable, DrawErr};
pub use self::edge_draw::BorderDraw;

<commit_msg>Fixed module imports<commit_after>mod color;
mod base;
mod border_draw;

pub use self::color::Color;
pub use self::base::{BaseDraw, Drawable, DrawErr};
pub use self::border_draw::BorderDraw;

<|endoftext|>


<commit_before>// Copyright (C) 2019 Sajeer Ahamed <ahamedsajeer.15.15@cse.mrt.ac.lk>
// Copyright (C) 2019 Sebastian Dröge <sebastian@centricular.com>
//
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

//! Extracts release for [GStreamer](https://gstreamer.freedesktop.org) plugin metadata
//!
//! See [`get_info`](fn.get_info.html) for details.
//!
//! This function is supposed to be used as follows in the `build.rs` of a crate that implements a
//! plugin:
//!
//! ```rust,ignore
//! extern crate gst_plugin_version_helper;
//!
//! fn main() {
//!     gst_plugin_version_helper::get_info()
//! }
//! ```
//!
//! Inside `lib.rs` of the plugin, the information provided by `get_info` are usable as follows:
//!
//! ```rust,ignore
//! gst_plugin_define!(
//!     the_plugin_name,
//!     env!("CARGO_PKG_DESCRIPTION"),
//!     plugin_init,
//!     concat!(env!("CARGO_PKG_VERSION"), "-", env!("COMMIT_ID")),
//!     "The Plugin's License",
//!     env!("CARGO_PKG_NAME"),
//!     env!("CARGO_PKG_NAME"),
//!     env!("CARGO_PKG_REPOSITORY"),
//!     env!("BUILD_REL_DATE")
//! );
//! ```

use chrono::TimeZone;
use git2::{Commit, ObjectType, Repository};
use std::{fs, path};

/// Extracts release for GStreamer plugin metadata
///
/// Release information is first tried to be extracted from a git repository at the same
/// place as the `Cargo.toml`, or one directory up to allow for Cargo workspaces. If no
/// git repository is found, the information is extract from a `release.txt` in the same
/// directory as the `Cargo.toml`.
///
/// - If extracted from a git repository, sets the `COMMIT_ID` environment variable to the short
///   commit id of the latest commit and the `BUILD_REL_DATE` environment variable to the date of the
///   commit.
///
/// - If extracted from a `release.txt`, `COMMIT_ID` will be set to the string `RELEASE` and the
///   `BUILD_REL_DATE` variable will be set to the release date extracted from `release.txt`.
///
/// - If neither is possible, `COMMIT_ID` is set to the string `UNKNOWN` and `BUILD_REL_DATE` to the
///   current date.
///
/// ## `release.txt`
///
/// `release.txt` is only parsed if no git repository was found and is assumed to be in the format
///
/// ```txt
/// version_number
/// release_date
/// ```
///
/// for example
///
/// ```txt
/// 1.16.0
/// 2019-04-19
/// ```
///
/// If the version number from `Cargo.toml` is not equivalent to the one in `release.txt`, this
/// function will panic.
pub fn get_info() {
    let mut commit_id = "UNKNOWN".into();
    let mut commit_date = chrono::Utc::now().format("%Y-%m-%d").to_string();

    let crate_dir = path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
    let mut repo_dir = crate_dir.clone();

    // First check for a git repository in the manifest directory and if there
    // is none try one directory up in case we're in a Cargo workspace
    let repo = Repository::open(&repo_dir).or_else(move |_| {
        repo_dir.pop();
        Repository::open(&repo_dir)
    });

    let mut release_file = crate_dir.clone();
    release_file.push("release.txt");

    // If there is a git repository, extract the version information from there.
    // Otherwise use a 'release.txt' file as fallback, or report the current
    // date and an unknown revision.
    if let Ok(repo) = repo {
        let commit = find_last_commit(&repo).expect("Couldn't find last commit");
        commit_id = oid_to_short_sha(commit.id());
        let timestamp = commit.time().seconds();
        let dt = chrono::Utc.timestamp(timestamp, 0);
        commit_date = dt.format("%Y-%m-%d").to_string()
    } else if let Ok(release_file) = fs::File::open(release_file) {
        let mut cargo_toml = crate_dir.clone();
        cargo_toml.push("Cargo.toml");

        let cargo_toml = fs::File::open(cargo_toml).expect("Can't open Cargo.toml");
        commit_date = read_release_date(cargo_toml, release_file);
        commit_id = "RELEASE".into();
    }

    println!("cargo:rustc-env=COMMIT_ID={}", commit_id);
    println!("cargo:rustc-env=BUILD_REL_DATE={}", commit_date);
}

fn find_last_commit(repo: &Repository) -> Result<Commit, git2::Error> {
    let obj = repo.head()?.resolve()?.peel(ObjectType::Commit)?;
    obj.into_commit()
        .map_err(|_| git2::Error::from_str("Couldn't find commit"))
}

fn oid_to_short_sha(oid: git2::Oid) -> String {
    oid.to_string()[..8].to_string()
}

fn read_release_date(mut cargo_toml: fs::File, mut release_file: fs::File) -> String {
    use std::io::Read;

    let mut content = String::new();
    release_file
        .read_to_string(&mut content)
        .expect("Failed to read version file");

    let mut lines = content.lines();
    let version = lines.next().expect("Failed to read version number");
    let date = lines.next().expect("Failed to read release date");

    let mut cargo_toml_content = String::new();
    cargo_toml
        .read_to_string(&mut cargo_toml_content)
        .expect("Failed to read `Cargo.toml`");

    let cargo_toml = cargo_toml_content
        .parse::<toml::Value>()
        .expect("Failed to parse `Cargo.toml`");
    let package = cargo_toml["package"]
        .as_table()
        .expect("Failed to find 'package' section in `Cargo.toml`");
    let cargo_version = package["version"]
        .as_str()
        .expect("Failed to read version from `Cargo.toml`");

    if cargo_version != version {
        panic!(
            "Version from 'version.txt' `{}` different from 'Cargo.toml' `{}`",
            version, cargo_version
        );
    }

    date.into()
}
<commit_msg>version-helper: Take CARGO_MANIFEST_DIR at runtime, not at compile-time<commit_after>// Copyright (C) 2019 Sajeer Ahamed <ahamedsajeer.15.15@cse.mrt.ac.lk>
// Copyright (C) 2019 Sebastian Dröge <sebastian@centricular.com>
//
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

//! Extracts release for [GStreamer](https://gstreamer.freedesktop.org) plugin metadata
//!
//! See [`get_info`](fn.get_info.html) for details.
//!
//! This function is supposed to be used as follows in the `build.rs` of a crate that implements a
//! plugin:
//!
//! ```rust,ignore
//! extern crate gst_plugin_version_helper;
//!
//! fn main() {
//!     gst_plugin_version_helper::get_info()
//! }
//! ```
//!
//! Inside `lib.rs` of the plugin, the information provided by `get_info` are usable as follows:
//!
//! ```rust,ignore
//! gst_plugin_define!(
//!     the_plugin_name,
//!     env!("CARGO_PKG_DESCRIPTION"),
//!     plugin_init,
//!     concat!(env!("CARGO_PKG_VERSION"), "-", env!("COMMIT_ID")),
//!     "The Plugin's License",
//!     env!("CARGO_PKG_NAME"),
//!     env!("CARGO_PKG_NAME"),
//!     env!("CARGO_PKG_REPOSITORY"),
//!     env!("BUILD_REL_DATE")
//! );
//! ```

use chrono::TimeZone;
use git2::{Commit, ObjectType, Repository};
use std::{env, fs, path};

/// Extracts release for GStreamer plugin metadata
///
/// Release information is first tried to be extracted from a git repository at the same
/// place as the `Cargo.toml`, or one directory up to allow for Cargo workspaces. If no
/// git repository is found, the information is extract from a `release.txt` in the same
/// directory as the `Cargo.toml`.
///
/// - If extracted from a git repository, sets the `COMMIT_ID` environment variable to the short
///   commit id of the latest commit and the `BUILD_REL_DATE` environment variable to the date of the
///   commit.
///
/// - If extracted from a `release.txt`, `COMMIT_ID` will be set to the string `RELEASE` and the
///   `BUILD_REL_DATE` variable will be set to the release date extracted from `release.txt`.
///
/// - If neither is possible, `COMMIT_ID` is set to the string `UNKNOWN` and `BUILD_REL_DATE` to the
///   current date.
///
/// ## `release.txt`
///
/// `release.txt` is only parsed if no git repository was found and is assumed to be in the format
///
/// ```txt
/// version_number
/// release_date
/// ```
///
/// for example
///
/// ```txt
/// 1.16.0
/// 2019-04-19
/// ```
///
/// If the version number from `Cargo.toml` is not equivalent to the one in `release.txt`, this
/// function will panic.
pub fn get_info() {
    let mut commit_id = "UNKNOWN".into();
    let mut commit_date = chrono::Utc::now().format("%Y-%m-%d").to_string();

    let crate_dir =
        path::PathBuf::from(env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set"));
    let mut repo_dir = crate_dir.clone();

    // First check for a git repository in the manifest directory and if there
    // is none try one directory up in case we're in a Cargo workspace
    let repo = Repository::open(&repo_dir).or_else(move |_| {
        repo_dir.pop();
        Repository::open(&repo_dir)
    });

    let mut release_file = crate_dir.clone();
    release_file.push("release.txt");

    // If there is a git repository, extract the version information from there.
    // Otherwise use a 'release.txt' file as fallback, or report the current
    // date and an unknown revision.
    if let Ok(repo) = repo {
        let commit = find_last_commit(&repo).expect("Couldn't find last commit");
        commit_id = oid_to_short_sha(commit.id());
        let timestamp = commit.time().seconds();
        let dt = chrono::Utc.timestamp(timestamp, 0);
        commit_date = dt.format("%Y-%m-%d").to_string()
    } else if let Ok(release_file) = fs::File::open(release_file) {
        let mut cargo_toml = crate_dir.clone();
        cargo_toml.push("Cargo.toml");

        let cargo_toml = fs::File::open(cargo_toml).expect("Can't open Cargo.toml");
        commit_date = read_release_date(cargo_toml, release_file);
        commit_id = "RELEASE".into();
    }

    println!("cargo:rustc-env=COMMIT_ID={}", commit_id);
    println!("cargo:rustc-env=BUILD_REL_DATE={}", commit_date);
}

fn find_last_commit(repo: &Repository) -> Result<Commit, git2::Error> {
    let obj = repo.head()?.resolve()?.peel(ObjectType::Commit)?;
    obj.into_commit()
        .map_err(|_| git2::Error::from_str("Couldn't find commit"))
}

fn oid_to_short_sha(oid: git2::Oid) -> String {
    oid.to_string()[..8].to_string()
}

fn read_release_date(mut cargo_toml: fs::File, mut release_file: fs::File) -> String {
    use std::io::Read;

    let mut content = String::new();
    release_file
        .read_to_string(&mut content)
        .expect("Failed to read version file");

    let mut lines = content.lines();
    let version = lines.next().expect("Failed to read version number");
    let date = lines.next().expect("Failed to read release date");

    let mut cargo_toml_content = String::new();
    cargo_toml
        .read_to_string(&mut cargo_toml_content)
        .expect("Failed to read `Cargo.toml`");

    let cargo_toml = cargo_toml_content
        .parse::<toml::Value>()
        .expect("Failed to parse `Cargo.toml`");
    let package = cargo_toml["package"]
        .as_table()
        .expect("Failed to find 'package' section in `Cargo.toml`");
    let cargo_version = package["version"]
        .as_str()
        .expect("Failed to read version from `Cargo.toml`");

    if cargo_version != version {
        panic!(
            "Version from 'version.txt' `{}` different from 'Cargo.toml' `{}`",
            version, cargo_version
        );
    }

    date.into()
}
<|endoftext|>


<commit_before>//! This abstract syntax tree is *really* abstract.
//! It makes binding explicit, but everything else about the language is hidden inside `Node`;
//!  Their meaning comes from `Form`.

#![macro_use]

use crate::{
    beta::{Beta, ExportBeta},
    form::Form,
    name::*,
    util::mbe::EnvMBE,
};
use std::{fmt, iter, rc::Rc};

// TODO: This really ought to be an `Rc` around an `enum`
#[derive(Clone, PartialEq)]
pub enum AstContents {
    Trivial,
    /// Typically, a binder
    Atom(Name),
    VariableReference(Name),

    /// Shift environment to quote more (the `bool` indicates whether it's positive or negative)
    QuoteMore(Ast, bool),
    /// Shift environment to quote less (the `u8` indicates the number of steps out)
    QuoteLess(Ast, u8),

    /// A meaningful chunk of syntax, governed by a form, containing an environment,
    ///  potentially exporting some names.
    Node(std::rc::Rc<Form>, EnvMBE<Ast>, ExportBeta),

    /// For parsing purposes.
    IncompleteNode(EnvMBE<Ast>),
    /// For parsing purposes.
    Shape(Vec<Ast>),

    /// Variable binding
    ExtendEnv(Ast, Beta),
    /// Variable binding, affects all phases.
    /// This is weird, but needed for marcos, it seems.
    ExtendEnvPhaseless(Ast, Beta),
}

#[derive(Clone, PartialEq)]
pub struct LocatedAst {
    /// "contents"
    pub c: AstContents,
    pub file_id: usize,
    pub begin: usize,
    pub end: usize,
}

#[derive(Clone)]
pub struct Ast(pub Rc<LocatedAst>);

// For testing purposes, we want to ignore span information
impl PartialEq for Ast {
    fn eq(&self, other: &Ast) -> bool { self.c() == other.c() }
}

// Reification macros would totally work for this,
//  but it's worth having a special case in `Value` in order to make this faster.
impl crate::runtime::reify::Reifiable for Ast {
    fn ty_name() -> Name { n("Ast") }

    fn reify(&self) -> crate::runtime::eval::Value {
        crate::runtime::eval::Value::AbstractSyntax(self.clone())
    }

    fn reflect(v: &crate::runtime::eval::Value) -> Ast {
        extract!((v) crate::runtime::eval::Value::AbstractSyntax = (ref ast) => ast.clone())
    }
}

pub use self::AstContents::*;

impl fmt::Debug for Ast {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{:#?}", self.c()) }
}

impl fmt::Debug for AstContents {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Trivial => write!(f, "⨉"),
            Atom(n) => write!(f, "∘{}∘", n.print()),
            VariableReference(v) => write!(f, "{}", v.print()),
            Shape(v) => {
                write!(f, "(")?;
                let mut first = true;
                for elt in v {
                    if !first {
                        write!(f, " ")?
                    }
                    elt.fmt(f)?;
                    first = false;
                }
                write!(f, ")")
            }
            Node(form, body, export) => {
                write!(f, "{{ ({}); {:#?}", form.name.sp(), body)?;
                match *export {
                    crate::beta::ExportBeta::Nothing => {}
                    _ => write!(f, " ⇑{:#?}", export)?,
                }
                write!(f, "}}")
            }
            QuoteMore(body, pos) => {
                if *pos {
                    write!(f, "pos``{:#?}``", body)
                } else {
                    write!(f, "neg``{:#?}``", body)
                }
            }
            QuoteLess(body, depth) => write!(f, ",,({}){:#?},,", depth, body),
            IncompleteNode(body) => write!(f, "{{ INCOMPLETE; {:#?} }}", body),
            ExtendEnv(body, beta) => write!(f, "{:#?}↓{:#?}", body, beta),
            ExtendEnvPhaseless(body, beta) => write!(f, "{:#?}±↓{:#?}", body, beta),
        }
    }
}

// Warning: this assumes the core language! To properly display an `Ast`, you need the `SynEnv`.
impl fmt::Display for Ast {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.c()) }
}

impl fmt::Display for AstContents {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Atom(ref n) => write!(f, "{}", n.print()),
            VariableReference(ref v) => write!(f, "{}", v.print()),
            Node(ref form, ref body, _) => {
                let s = crate::unparse::unparse_mbe(
                    &form.grammar,
                    self,
                    body,
                    &crate::core_forms::get_core_forms(),
                );
                write!(f, "{}", s)
            }
            Shape(ref v) => {
                write!(f, "(")?;
                let mut first = true;
                for elt in v {
                    if !first {
                        write!(f, " ")?
                    }
                    elt.fmt(f)?;
                    first = false;
                }
                write!(f, ")")
            }
            ExtendEnv(ref body, _) => write!(f, "{}↓", body),
            ExtendEnvPhaseless(ref body, _) => write!(f, "{}±↓", body),
            QuoteMore(body, _) => {
                write!(f, "``{}``", body)
            }
            QuoteLess(body, _) => write!(f, ",,{},,", body),
            _ => write!(f, "{:#?}", self),
        }
    }
}

impl Ast {
    /// "Contents". Ignore location information and get the interesting bits
    pub fn c(&self) -> &AstContents { &self.0.c }

    /// Replace the contents.
    pub fn c_map(&self, f: &dyn Fn(&AstContents) -> AstContents) -> Ast { self.with_c(f(self.c())) }

    /// Keep the location information, but replace the contents.
    pub fn with_c(&self, c: AstContents) -> Ast {
        Ast(Rc::new(LocatedAst {
            c: c,
            file_id: self.0.file_id,
            begin: self.0.begin,
            end: self.0.end,
        }))
    }

    // TODO: this ought to at least warn if we're losing anything other than `Shape`
    pub fn flatten(&self) -> EnvMBE<Ast> {
        match self.c() {
            Trivial | Atom(_) => EnvMBE::new(),
            VariableReference(_) => EnvMBE::new(),
            Shape(ref v) => {
                let mut accum = EnvMBE::new();
                for sub_a in v {
                    accum = accum.combine_overriding(&sub_a.flatten())
                }
                accum
            }
            IncompleteNode(ref env) => env.clone(),
            Node(ref _f, ref _body, ref _export) => {
                // TODO: think about what should happen when
                //  `Scope` contains a `Scope` without an intervening `Named`
                panic!("I don't know what to do with {:#?}!", self)
            }
            QuoteMore(ref body, _) => body.flatten(),
            QuoteLess(ref body, _) => body.flatten(),
            ExtendEnv(ref body, _) | ExtendEnvPhaseless(ref body, _) => body.flatten(),
        }
    }

    // TODO: use `Mode::context_match` whenever possible
    pub fn destructure(
        &self,
        expd_form: std::rc::Rc<Form>,
    ) -> Option<crate::util::mbe::EnvMBE<Ast>> {
        if let Node(ref f, ref parts, _) = self.c() {
            if f == &expd_form {
                return Some(parts.clone());
            }
        }
        None
    }

    pub fn is_node(&self) -> bool {
        match self.c() {
            Node(_, _, _) => true,
            _ => false,
        }
    }

    // TODO: I think we have a lot of places where we ought to use this function:
    pub fn node_parts(&self) -> &EnvMBE<Ast> {
        match self.c() {
            Node(_, ref body, _) => body,
            _ => icp!(),
        }
    }

    pub fn maybe_node_parts(&self) -> Option<&EnvMBE<Ast>> {
        match self.c() {
            Node(_, ref body, _) => Some(body),
            _ => None,
        }
    }

    pub fn node_form(&self) -> &Form {
        match self.c() {
            Node(ref form, _, _) => form,
            _ => icp!(),
        }
    }

    pub fn free_vrs(&self) -> Vec<Name> {
        match self.c() {
            Trivial | Atom(_) => vec![],
            VariableReference(v) => vec![*v],
            Shape(_) | IncompleteNode(_) => unimplemented!("TODO"),
            QuoteLess(_, _) | QuoteMore(_, _) => unimplemented!("TODO"),
            // This one is actually encounterable by real-world code
            //  (if a ∀ somehow ends up underneath a `*` in syntax.)
            // And we need to take a LazyWalkReses to do this right.
            ExtendEnv(_, _) | ExtendEnvPhaseless(_, _) => unimplemented!("TODO"),
            Node(_, ref body, _) => body.map_reduce(
                &|a| a.free_vrs(),
                &|v0, v1| {
                    let mut res = v0.clone();
                    res.append(&mut v1.clone());
                    res
                },
                vec![],
            ),
        }
    }

    pub fn to_name(&self) -> Name {
        match self.c() {
            Atom(n) => *n,
            _ => icp!("{:#?} is not an atom", self),
        }
    }

    pub fn vr_to_name(&self) -> Name {
        match self.c() {
            VariableReference(n) => *n,
            _ => icp!("{:#?} is not an atom", self),
        }
    }

    pub fn orig_str<'a, 'b>(&'a self, prog: &'b str) -> &'b str { &prog[self.0.begin..self.0.end] }
}

// This is used by combine::many, which is used by the Star parser
impl iter::FromIterator<Ast> for Ast {
    fn from_iter<I: IntoIterator<Item = Ast>>(i: I) -> Self {
        raw_ast!(IncompleteNode(EnvMBE::new_from_anon_repeat(
            i.into_iter().map(|a| a.flatten()).collect()
        )))
    }
}

// This is also sort of a test of MBE, since we need `Ast`s to make them with the macros
//
// Suppose we have the following series of `Ast`s:
// [b = 8] [a = [1 2], b = 8] [a = [3 4 5], b = 8]
//
// We should turn them into the following `Ast`
// [a = [[] [1 2] [3 4 5]], b = [8 8 8]]
#[test]
fn combine_from_kleene_star() {
    use std::iter::FromIterator;

    let parse_parts = vec![
        ast!({ - "a" => [], "b" => "8.0"}),
        ast!({ - "a" => ["1", "2"], "b" => "8.1"}),
        ast!({ - "a" => ["1", "2", "3"], "b" => "8.2"}),
    ];
    let parsed = Ast::from_iter(parse_parts);

    let mut expected_mbe = mbe!("a" => [@"triple" [], ["1", "2"], ["1", "2", "3"]],
             "b" => [@"triple" "8.0", "8.1", "8.2"]);
    expected_mbe.anonimize_repeat(n("triple"));

    assert_eq!(parsed.c(), &IncompleteNode(expected_mbe));
}

#[test]
fn star_construction() {
    let env = mbe!( "a" => ["1", "2"]);

    assert_eq!(
        ast!( { - "x" => [* env =>("a") env : (, env.get_leaf_or_panic(&n("a")).clone())]} ),
        ast!( { - "x" => ["1", "2"] })
    );

    let env = mbe!( "a" => [@"duo" "1", "2"], "b" => [@"duo" "11", "22"]);

    assert_eq!(
        ast!( { - "x" => [* env =>("a", "b") env :
                            ((, env.get_leaf_or_panic(&n("b")).clone())
                             (, env.get_leaf_or_panic(&n("a")).clone()))]} ),
        ast!( { - "x" => [("11" "1"), ("22" "2")] })
    );
}

#[test]
fn mbe_r_and_r_roundtrip() {
    use crate::runtime::reify::Reifiable;
    let mbe1 = mbe!( "a" => [@"duo" "1", "2"], "b" => [@"duo" "11", "22"]);
    assert_eq!(mbe1, EnvMBE::<Ast>::reflect(&mbe1.reify()));
}
<commit_msg>Remove some unused code.<commit_after>//! This abstract syntax tree is *really* abstract.
//! It makes binding explicit, but everything else about the language is hidden inside `Node`;
//!  Their meaning comes from `Form`.

#![macro_use]

use crate::{
    beta::{Beta, ExportBeta},
    form::Form,
    name::*,
    util::mbe::EnvMBE,
};
use std::{fmt, rc::Rc};

// TODO: This really ought to be an `Rc` around an `enum`
#[derive(Clone, PartialEq)]
pub enum AstContents {
    Trivial,
    /// Typically, a binder
    Atom(Name),
    VariableReference(Name),

    /// Shift environment to quote more (the `bool` indicates whether it's positive or negative)
    QuoteMore(Ast, bool),
    /// Shift environment to quote less (the `u8` indicates the number of steps out)
    QuoteLess(Ast, u8),

    /// A meaningful chunk of syntax, governed by a form, containing an environment,
    ///  potentially exporting some names.
    Node(std::rc::Rc<Form>, EnvMBE<Ast>, ExportBeta),

    /// For parsing purposes.
    IncompleteNode(EnvMBE<Ast>),
    /// For parsing purposes.
    Shape(Vec<Ast>),

    /// Variable binding
    ExtendEnv(Ast, Beta),
    /// Variable binding, affects all phases.
    /// This is weird, but needed for marcos, it seems.
    ExtendEnvPhaseless(Ast, Beta),
}

#[derive(Clone, PartialEq)]
pub struct LocatedAst {
    /// "contents"
    pub c: AstContents,
    pub file_id: usize,
    pub begin: usize,
    pub end: usize,
}

#[derive(Clone)]
pub struct Ast(pub Rc<LocatedAst>);

// For testing purposes, we want to ignore span information
impl PartialEq for Ast {
    fn eq(&self, other: &Ast) -> bool { self.c() == other.c() }
}

// Reification macros would totally work for this,
//  but it's worth having a special case in `Value` in order to make this faster.
impl crate::runtime::reify::Reifiable for Ast {
    fn ty_name() -> Name { n("Ast") }

    fn reify(&self) -> crate::runtime::eval::Value {
        crate::runtime::eval::Value::AbstractSyntax(self.clone())
    }

    fn reflect(v: &crate::runtime::eval::Value) -> Ast {
        extract!((v) crate::runtime::eval::Value::AbstractSyntax = (ref ast) => ast.clone())
    }
}

pub use self::AstContents::*;

impl fmt::Debug for Ast {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{:#?}", self.c()) }
}

impl fmt::Debug for AstContents {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Trivial => write!(f, "⨉"),
            Atom(n) => write!(f, "∘{}∘", n.print()),
            VariableReference(v) => write!(f, "{}", v.print()),
            Shape(v) => {
                write!(f, "(")?;
                let mut first = true;
                for elt in v {
                    if !first {
                        write!(f, " ")?
                    }
                    elt.fmt(f)?;
                    first = false;
                }
                write!(f, ")")
            }
            Node(form, body, export) => {
                write!(f, "{{ ({}); {:#?}", form.name.sp(), body)?;
                match *export {
                    crate::beta::ExportBeta::Nothing => {}
                    _ => write!(f, " ⇑{:#?}", export)?,
                }
                write!(f, "}}")
            }
            QuoteMore(body, pos) => {
                if *pos {
                    write!(f, "pos``{:#?}``", body)
                } else {
                    write!(f, "neg``{:#?}``", body)
                }
            }
            QuoteLess(body, depth) => write!(f, ",,({}){:#?},,", depth, body),
            IncompleteNode(body) => write!(f, "{{ INCOMPLETE; {:#?} }}", body),
            ExtendEnv(body, beta) => write!(f, "{:#?}↓{:#?}", body, beta),
            ExtendEnvPhaseless(body, beta) => write!(f, "{:#?}±↓{:#?}", body, beta),
        }
    }
}

// Warning: this assumes the core language! To properly display an `Ast`, you need the `SynEnv`.
impl fmt::Display for Ast {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.c()) }
}

impl fmt::Display for AstContents {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Atom(ref n) => write!(f, "{}", n.print()),
            VariableReference(ref v) => write!(f, "{}", v.print()),
            Node(ref form, ref body, _) => {
                let s = crate::unparse::unparse_mbe(
                    &form.grammar,
                    self,
                    body,
                    &crate::core_forms::get_core_forms(),
                );
                write!(f, "{}", s)
            }
            Shape(ref v) => {
                write!(f, "(")?;
                let mut first = true;
                for elt in v {
                    if !first {
                        write!(f, " ")?
                    }
                    elt.fmt(f)?;
                    first = false;
                }
                write!(f, ")")
            }
            ExtendEnv(ref body, _) => write!(f, "{}↓", body),
            ExtendEnvPhaseless(ref body, _) => write!(f, "{}±↓", body),
            QuoteMore(body, _) => {
                write!(f, "``{}``", body)
            }
            QuoteLess(body, _) => write!(f, ",,{},,", body),
            _ => write!(f, "{:#?}", self),
        }
    }
}

impl Ast {
    /// "Contents". Ignore location information and get the interesting bits
    pub fn c(&self) -> &AstContents { &self.0.c }

    /// Replace the contents.
    pub fn c_map(&self, f: &dyn Fn(&AstContents) -> AstContents) -> Ast { self.with_c(f(self.c())) }

    /// Keep the location information, but replace the contents.
    pub fn with_c(&self, c: AstContents) -> Ast {
        Ast(Rc::new(LocatedAst {
            c: c,
            file_id: self.0.file_id,
            begin: self.0.begin,
            end: self.0.end,
        }))
    }

    // TODO: this ought to at least warn if we're losing anything other than `Shape`
    pub fn flatten(&self) -> EnvMBE<Ast> {
        match self.c() {
            Trivial | Atom(_) => EnvMBE::new(),
            VariableReference(_) => EnvMBE::new(),
            Shape(ref v) => {
                let mut accum = EnvMBE::new();
                for sub_a in v {
                    accum = accum.combine_overriding(&sub_a.flatten())
                }
                accum
            }
            IncompleteNode(ref env) => env.clone(),
            Node(ref _f, ref _body, ref _export) => {
                // TODO: think about what should happen when
                //  `Scope` contains a `Scope` without an intervening `Named`
                panic!("I don't know what to do with {:#?}!", self)
            }
            QuoteMore(ref body, _) => body.flatten(),
            QuoteLess(ref body, _) => body.flatten(),
            ExtendEnv(ref body, _) | ExtendEnvPhaseless(ref body, _) => body.flatten(),
        }
    }

    // TODO: use `Mode::context_match` whenever possible
    pub fn destructure(
        &self,
        expd_form: std::rc::Rc<Form>,
    ) -> Option<crate::util::mbe::EnvMBE<Ast>> {
        if let Node(ref f, ref parts, _) = self.c() {
            if f == &expd_form {
                return Some(parts.clone());
            }
        }
        None
    }

    pub fn is_node(&self) -> bool {
        match self.c() {
            Node(_, _, _) => true,
            _ => false,
        }
    }

    // TODO: I think we have a lot of places where we ought to use this function:
    pub fn node_parts(&self) -> &EnvMBE<Ast> {
        match self.c() {
            Node(_, ref body, _) => body,
            _ => icp!(),
        }
    }

    pub fn maybe_node_parts(&self) -> Option<&EnvMBE<Ast>> {
        match self.c() {
            Node(_, ref body, _) => Some(body),
            _ => None,
        }
    }

    pub fn node_form(&self) -> &Form {
        match self.c() {
            Node(ref form, _, _) => form,
            _ => icp!(),
        }
    }

    pub fn free_vrs(&self) -> Vec<Name> {
        match self.c() {
            Trivial | Atom(_) => vec![],
            VariableReference(v) => vec![*v],
            Shape(_) | IncompleteNode(_) => unimplemented!("TODO"),
            QuoteLess(_, _) | QuoteMore(_, _) => unimplemented!("TODO"),
            // This one is actually encounterable by real-world code
            //  (if a ∀ somehow ends up underneath a `*` in syntax.)
            // And we need to take a LazyWalkReses to do this right.
            ExtendEnv(_, _) | ExtendEnvPhaseless(_, _) => unimplemented!("TODO"),
            Node(_, ref body, _) => body.map_reduce(
                &|a| a.free_vrs(),
                &|v0, v1| {
                    let mut res = v0.clone();
                    res.append(&mut v1.clone());
                    res
                },
                vec![],
            ),
        }
    }

    pub fn to_name(&self) -> Name {
        match self.c() {
            Atom(n) => *n,
            _ => icp!("{:#?} is not an atom", self),
        }
    }

    pub fn vr_to_name(&self) -> Name {
        match self.c() {
            VariableReference(n) => *n,
            _ => icp!("{:#?} is not an atom", self),
        }
    }

    pub fn orig_str<'a, 'b>(&'a self, prog: &'b str) -> &'b str { &prog[self.0.begin..self.0.end] }
}

#[test]
fn star_construction() {
    let env = mbe!( "a" => ["1", "2"]);

    assert_eq!(
        ast!( { - "x" => [* env =>("a") env : (, env.get_leaf_or_panic(&n("a")).clone())]} ),
        ast!( { - "x" => ["1", "2"] })
    );

    let env = mbe!( "a" => [@"duo" "1", "2"], "b" => [@"duo" "11", "22"]);

    assert_eq!(
        ast!( { - "x" => [* env =>("a", "b") env :
                            ((, env.get_leaf_or_panic(&n("b")).clone())
                             (, env.get_leaf_or_panic(&n("a")).clone()))]} ),
        ast!( { - "x" => [("11" "1"), ("22" "2")] })
    );
}

#[test]
fn mbe_r_and_r_roundtrip() {
    use crate::runtime::reify::Reifiable;
    let mbe1 = mbe!( "a" => [@"duo" "1", "2"], "b" => [@"duo" "11", "22"]);
    assert_eq!(mbe1, EnvMBE::<Ast>::reflect(&mbe1.reify()));
}
<|endoftext|>


<commit_before>use std::fs;
use std::io;
use std::path::Path;
use needed::Needed;
use regex::Regex;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use telegram;
use std::default::Default;
use std::io::{Read, Write};
use rustc_serialize::json;
use log::{LogLevel, Logger};

// Data of one FlatShare
#[derive(Default, RustcEncodable, RustcDecodable)]
struct FlatShare {
    needed: Needed,
}

// Maps a Telegram 'ChatID' to the corresponding flatshare data
pub type FlatMap = HashMap<telegram::Integer, FlatShare>;

/// Type for building a bot easily
pub struct BotBuilder<'a> {
    token: String,
    logfile: Option<&'a Path>,
    loglevel: LogLevel,
    data_dir: String,
}

#[allow(dead_code)]
impl<'a> BotBuilder<'a> {
    /// Specifies a logfile to log into. By default the bot does not log
    /// into a file.
    pub fn with_logfile<'b>(self, path: &'b Path) -> BotBuilder<'b> {
        BotBuilder{
            token: self.token,
            logfile: Some(path),
            loglevel: self.loglevel,
            data_dir: self.data_dir,
        }
    }

    /// Specifies the log level of the bot. All messages with level higher or
    /// equal to the specified level will be logged.
    pub fn with_loglevel(mut self, lvl: LogLevel) -> Self {
        self.loglevel = lvl;
        self
    }

    /// Specify the directory where the flatshare data lives.
    pub fn with_data_dir(mut self, dir: String) -> Self {
        self.data_dir = dir;
        self
    }

    /// Create a bot out of the given configuration.
    pub fn build(self) -> telegram::Result<MartiniBot> {
        // Create and test the api.
        let mut api = telegram::Bot::new(self.token);
        let me = try!(api.get_me());

        // Try to open the logfile in writing-append mode if specified
        let file = match self.logfile {
            Some(path) => {
                Some(try!(fs::OpenOptions::new()
                    .write(true)
                    .append(true)
                    .create(true)
                    .open(path)))
            },
            None => None,
        };

        // Check if the data directory exists. Create it otherwise.
        if let Err(e) = fs::create_dir(&Path::new(&*self.data_dir)) {
            if e.kind() != io::ErrorKind::AlreadyExists {
                return Err(e.into());
            }
        }

        Ok(MartiniBot {
            api: Arc::new(Mutex::new(api)),
            me: me,
            flats: FlatMap::new(),
            logger: Logger {
                logfile: file,
                loglevel: self.loglevel,
            },
            data_dir: self.data_dir,
        })
    }
}

/// Type that handles the main bot tasks.
pub struct MartiniBot {
    api: Arc<Mutex<telegram::Bot>>,
    me: telegram::User,
    flats: FlatMap,
    logger: Logger,
    data_dir: String,
}

impl MartiniBot {
    /// This is needed to create a bot. Returns a builder for further
    /// configuration. Call `build()` to get the bot.
    pub fn from_token(token: String) -> BotBuilder<'static> {
        BotBuilder {
            token: token,
            logfile: None,
            loglevel: LogLevel::Info,
            data_dir: "data/".into(),
        }
    }

    /// Returns the telegram user of the bot.
    pub fn me(&self) -> telegram::User {
        self.me.clone()
    }

    pub fn log(&mut self, lvl: LogLevel, msg: &str) {
        self.logger.log(lvl, msg);
    }

    /// Writes the data of one flat into a file in the data_dir.
    fn write_flat(&mut self, cid: telegram::Integer) {
        // Create filename and path: Use chat id as filename.
        let fname = format!("{}{}.json", self.data_dir, cid);
        let p = &Path::new(&*fname);

        // We want to overwrite everything and create the file if necessary.
        let file = fs::OpenOptions::new()
            .write(true)
            .truncate(true)
            .create(true)
            .open(p);

        let mut file = match file {
            Ok(f) => f,
            Err(e) => {
                log!(self, Error: "Could not open file (w) '{}': {}", fname, e);
                return;
            }
        };

        let res = write!(file, "{}\n", json::as_pretty_json(self.flat(cid)));
        match res {
            Ok(_) =>
                log!(self, Debug: "Wrote file '{}'", fname),
            Err(e) =>
                log!(self, Warning: "Could not write file '{}': {}", fname, e),
        }
    }

    /// Tries to read flatshare data from file. Returns `None` if file is not
    /// present.
    fn read_flat(&mut self, cid: telegram::Integer) -> Option<FlatShare> {
        // Create filename and path: Use chat id as filename.
        let fname = format!("{}{}.json", self.data_dir, cid);
        let p = &Path::new(&*fname);

        let file = fs::File::open(p);

        let mut file = match file {
            Ok(f) => f,
            Err(e) => {
                log!(self, Debug: "Could not open file (r) '{}': {}", fname, e);
                return None;
            }
        };

        let mut content = String::new();
        if let Err(e) = file.read_to_string(&mut content) {
            log!(self, Warning: "Could not read file '{}': {}", fname, e);
            return None;
        }

        match json::decode(&*content) {
            Ok(f) => {
                log!(self, Debug: "Read file '{}'", fname);
                Some(f)
            },
            Err(e) => {
                log!(self, Warning: "Could not decode file '{}': {}", fname, e);
                None
            }
        }
    }

    /// Shortcut to get one flat from the map.
    fn flat(&mut self, cid: telegram::Integer) -> &mut FlatShare {
        if !self.flats.contains_key(&cid) {
            let new = self.read_flat(cid).unwrap_or(FlatShare::default());
            self.flats.insert(cid, new);
        }
        self.flats.get_mut(&cid).unwrap()
    }

    pub fn run(&mut self) {
        // Fetch new updates via long poll method
        let api = self.api.clone();
        let res = api.lock().unwrap().long_poll(None, |api, u| {
            self.handle(api, u)
        });
        if let Err(e) = res {
            log!(self, Error: "An error occured: {}", e);
        }
    }

    fn handle(&mut self,
              api: &mut telegram::Bot,
              u: telegram::Update)
        -> telegram::Result<()>
    {
        use telegram::types::*;

        // If the received update does not contain a text message: Return.
        let m = match u.message {
            Some(m) => m,
            None => return Ok(()),
        };
        let t = match m.msg {
            MessageType::Text(t) => t,
            _ => return Ok(()),
        };

        // A nice formatted name.
        let name = if let Some(last) = m.from.last_name {
            format!("{} {}", m.from.first_name, last)
        } else {
            m.from.first_name
        };

        let cid = m.chat.id();

        // Log received text message.
        let room = match m.chat {
            Chat::Group(ref g) => format!("{}#'{}'", g.id, g.title),
            Chat::User(_) => "private".into(),
        };
        log!(self, Debug: "<{} @ {}> {}", name, room, t);

        // Parse command
        let command = Regex::new(r"^/\w+").unwrap();

        if t.starts_with(&command) {
            let arg = t.trim_left_matches(&command);
            match t.splitn(2, " ").next().unwrap() {
                "/need" => {
                    let msg = self.flat(cid).needed.handle_need(arg.into());
                    try!(api.send_message(cid, msg, None, None, None));
                },
                "/got" => {
                    let msg = self.flat(cid).needed.handle_got(arg.into());
                    try!(api.send_message(cid, msg, None, None, None));
                }
                command => {
                    log!(self, Warning: "Unknown command '{}'", command);
                }
            }

        }

        // Update file
        if self.flats.contains_key(&cid) {
            self.write_flat(cid);
        }

        Ok(())
    }
}
<commit_msg>Updated bot for telegram-bot 0.2.0<commit_after>use std::fs;
use std::io;
use std::path::Path;
use needed::Needed;
use regex::Regex;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use telegram;
use std::default::Default;
use std::io::{Read, Write};
use rustc_serialize::json;
use log::{LogLevel, Logger};

// Data of one FlatShare
#[derive(Default, RustcEncodable, RustcDecodable)]
struct FlatShare {
    needed: Needed,
}

// Maps a Telegram 'ChatID' to the corresponding flatshare data
pub type FlatMap = HashMap<telegram::Integer, FlatShare>;

/// Type for building a bot easily
pub struct BotBuilder<'a> {
    token: String,
    logfile: Option<&'a Path>,
    loglevel: LogLevel,
    data_dir: String,
}

#[allow(dead_code)]
impl<'a> BotBuilder<'a> {
    /// Specifies a logfile to log into. By default the bot does not log
    /// into a file.
    pub fn with_logfile<'b>(self, path: &'b Path) -> BotBuilder<'b> {
        BotBuilder{
            token: self.token,
            logfile: Some(path),
            loglevel: self.loglevel,
            data_dir: self.data_dir,
        }
    }

    /// Specifies the log level of the bot. All messages with level higher or
    /// equal to the specified level will be logged.
    pub fn with_loglevel(mut self, lvl: LogLevel) -> Self {
        self.loglevel = lvl;
        self
    }

    /// Specify the directory where the flatshare data lives.
    pub fn with_data_dir(mut self, dir: String) -> Self {
        self.data_dir = dir;
        self
    }

    /// Create a bot out of the given configuration.
    pub fn build(self) -> telegram::Result<MartiniBot> {
        // Create and test the api.
        let mut api = telegram::Api::from_token(&self.token).unwrap();
        let me = try!(api.get_me());

        // Try to open the logfile in writing-append mode if specified
        let file = match self.logfile {
            Some(path) => {
                Some(try!(fs::OpenOptions::new()
                    .write(true)
                    .append(true)
                    .create(true)
                    .open(path)))
            },
            None => None,
        };

        // Check if the data directory exists. Create it otherwise.
        if let Err(e) = fs::create_dir(&Path::new(&*self.data_dir)) {
            if e.kind() != io::ErrorKind::AlreadyExists {
                return Err(e.into());
            }
        }

        Ok(MartiniBot {
            api: Arc::new(Mutex::new(api)),
            me: me,
            flats: FlatMap::new(),
            logger: Logger {
                logfile: file,
                loglevel: self.loglevel,
            },
            data_dir: self.data_dir,
        })
    }
}

/// Type that handles the main bot tasks.
pub struct MartiniBot {
    api: Arc<Mutex<telegram::Api>>,
    me: telegram::User,
    flats: FlatMap,
    logger: Logger,
    data_dir: String,
}

impl MartiniBot {
    /// This is needed to create a bot. Returns a builder for further
    /// configuration. Call `build()` to get the bot.
    pub fn from_token(token: String) -> BotBuilder<'static> {
        BotBuilder {
            token: token,
            logfile: None,
            loglevel: LogLevel::Info,
            data_dir: "data/".into(),
        }
    }

    /// Returns the telegram user of the bot.
    pub fn me(&self) -> telegram::User {
        self.me.clone()
    }

    pub fn log(&mut self, lvl: LogLevel, msg: &str) {
        self.logger.log(lvl, msg);
    }

    /// Writes the data of one flat into a file in the data_dir.
    fn write_flat(&mut self, cid: telegram::Integer) {
        // Create filename and path: Use chat id as filename.
        let fname = format!("{}{}.json", self.data_dir, cid);
        let p = &Path::new(&*fname);

        // We want to overwrite everything and create the file if necessary.
        let file = fs::OpenOptions::new()
            .write(true)
            .truncate(true)
            .create(true)
            .open(p);

        let mut file = match file {
            Ok(f) => f,
            Err(e) => {
                log!(self, Error: "Could not open file (w) '{}': {}", fname, e);
                return;
            }
        };

        let res = write!(file, "{}\n", json::as_pretty_json(self.flat(cid)));
        match res {
            Ok(_) =>
                log!(self, Debug: "Wrote file '{}'", fname),
            Err(e) =>
                log!(self, Warning: "Could not write file '{}': {}", fname, e),
        }
    }

    /// Tries to read flatshare data from file. Returns `None` if file is not
    /// present.
    fn read_flat(&mut self, cid: telegram::Integer) -> Option<FlatShare> {
        // Create filename and path: Use chat id as filename.
        let fname = format!("{}{}.json", self.data_dir, cid);
        let p = &Path::new(&*fname);

        let file = fs::File::open(p);

        let mut file = match file {
            Ok(f) => f,
            Err(e) => {
                log!(self, Debug: "Could not open file (r) '{}': {}", fname, e);
                return None;
            }
        };

        let mut content = String::new();
        if let Err(e) = file.read_to_string(&mut content) {
            log!(self, Warning: "Could not read file '{}': {}", fname, e);
            return None;
        }

        match json::decode(&*content) {
            Ok(f) => {
                log!(self, Debug: "Read file '{}'", fname);
                Some(f)
            },
            Err(e) => {
                log!(self, Warning: "Could not decode file '{}': {}", fname, e);
                None
            }
        }
    }

    /// Shortcut to get one flat from the map.
    fn flat(&mut self, cid: telegram::Integer) -> &mut FlatShare {
        if !self.flats.contains_key(&cid) {
            let new = self.read_flat(cid).unwrap_or(FlatShare::default());
            self.flats.insert(cid, new);
        }
        self.flats.get_mut(&cid).unwrap()
    }

    pub fn run(&mut self) {
        // Fetch new updates via long poll method
        let mut listener = self.api.lock().unwrap().listener(telegram::ListeningMethod::LongPoll(None));

        let res = listener.listen(|u| {
            self.handle(u)
        });

        if let Err(e) = res {
            log!(self, Error: "An error occured: {}", e);
        }
    }

    fn handle(&mut self,
              u: telegram::Update)
              -> telegram::Result<telegram::ListeningAction>
    {
        use telegram::types::*;

        // If the received update does not contain a text message: Return.
        let m = match u.message {
            Some(m) => m,
            None => return Ok(telegram::ListeningAction::Continue),
        };
        let t = match m.msg {
            MessageType::Text(t) => t,
            _ => return Ok(telegram::ListeningAction::Continue),
        };

        // A nice formatted name.
        let name = if let Some(last) = m.from.last_name {
            format!("{} {}", m.from.first_name, last)
        } else {
            m.from.first_name
        };

        let cid = m.chat.id();

        // Log received text message.
        let room = match m.chat {
            Chat::Group(ref g) => format!("{}#'{}'", g.id, g.title),
            Chat::User(_) => "private".into(),
        };
        log!(self, Debug: "<{} @ {}> {}", name, room, t);

        // Parse command
        let command = Regex::new(r"^/\w+").unwrap();

        if t.starts_with(&command) {
            let arg = t.trim_left_matches(&command);
            match t.splitn(2, " ").next().unwrap() {
                "/need" => {
                    let msg = self.flat(cid).needed.handle_need(arg.into());
                    try!(self.api.lock().unwrap().send_message(cid, msg, None, None, None));
                },
                "/got" => {
                    let msg = self.flat(cid).needed.handle_got(arg.into());
                    try!(self.api.lock().unwrap().send_message(cid, msg, None, None, None));
                }
                command => {
                    log!(self, Warning: "Unknown command '{}'", command);
                }
            }

        }

        // Update file
        if self.flats.contains_key(&cid) {
            self.write_flat(cid);
        }

        Ok(telegram::ListeningAction::Continue)
    }
}
<|endoftext|>