<commit_before>//! System and Hardware Clocks

/// Internal Oscillator
pub const HSI: u32 = 8_000_000;
/// External clock
pub const HSE: u32 = 8_000_000;

use stm32f103xx::{Rcc, Flash, rcc};

/// Board clock speeds
pub struct ClockSpeeds{
    /// System Clock
    pub sysclk: u32,
    /// AHB peripheral clock
    pub hclk: u32,
    /// Low speed bus
    pub apb1: u32,
    /// high speed bus
    pub apb2: u32,
}

impl ClockSpeeds {
    /// Get clock speeds
    pub fn get(rcc: &Rcc) -> ClockSpeeds {
        let sysclk = match rcc.cfgr.read().sws(){
            rcc::cfgr::SwsR::Hse => HSE,
            rcc::cfgr::SwsR::Pll => Self::get_pll_speed(rcc),
            _ => HSI,
        };

        let hclk = match rcc.cfgr.read().hpre() {
            rcc::cfgr::HpreR::Div2 => sysclk / 2,
            rcc::cfgr::HpreR::Div4 => sysclk / 4,
            rcc::cfgr::HpreR::Div8 => sysclk / 8,
            rcc::cfgr::HpreR::Div16 => sysclk / 16,
            rcc::cfgr::HpreR::Div64 => sysclk / 64,
            rcc::cfgr::HpreR::Div128 => sysclk / 128,
            rcc::cfgr::HpreR::Div256 => sysclk / 256,
            rcc::cfgr::HpreR::Div512 => sysclk / 512,
            _ => sysclk,
        };

        let apb1 = match rcc.cfgr.read().ppre1() {
            rcc::cfgr::Ppre1R::Div2 => hclk / 2,
            rcc::cfgr::Ppre1R::Div4 => hclk / 4,
            rcc::cfgr::Ppre1R::Div8 => hclk / 8,
            rcc::cfgr::Ppre1R::Div16 => hclk / 16,
            _ => hclk,
        };

        let apb2 = match rcc.cfgr.read().ppre2() {
            rcc::cfgr::Ppre2R::Div2 => hclk / 2,
            rcc::cfgr::Ppre2R::Div4 => hclk / 4,
            rcc::cfgr::Ppre2R::Div8 => hclk / 8,
            rcc::cfgr::Ppre2R::Div16 => hclk / 16,
            _ => hclk,
        };

        ClockSpeeds{
            sysclk: sysclk,
            hclk: hclk,
            apb1: apb1,
            apb2: apb2,
        }
    }

    fn get_pll_speed(rcc: &Rcc) -> u32 {
        let hse_div = match rcc.cfgr.read().pllxtpre(){
            rcc::cfgr::PllxtpreR::Div1 => 1,
            rcc::cfgr::PllxtpreR::Div2 => 2,
        };

        let src_spd = match rcc.cfgr.read().pllsrc(){
            rcc::cfgr::PllsrcR::Internal => HSI / 2,
            rcc::cfgr::PllsrcR::External => HSE / hse_div,
        };

        match rcc.cfgr.read().pllmul() {
            rcc::cfgr::PllmulR::Mul2 => src_spd * 2,
            rcc::cfgr::PllmulR::Mul3 => src_spd * 3,
            rcc::cfgr::PllmulR::Mul4 => src_spd * 4,
            rcc::cfgr::PllmulR::Mul5 => src_spd * 5,
            rcc::cfgr::PllmulR::Mul6 => src_spd * 6,
            rcc::cfgr::PllmulR::Mul7 => src_spd * 7,
            rcc::cfgr::PllmulR::Mul8 => src_spd * 8,
            rcc::cfgr::PllmulR::Mul9 => src_spd * 9,
            rcc::cfgr::PllmulR::Mul10 => src_spd * 10,
            rcc::cfgr::PllmulR::Mul11 => src_spd * 11,
            rcc::cfgr::PllmulR::Mul12 => src_spd * 12,
            rcc::cfgr::PllmulR::Mul13 => src_spd * 13,
            rcc::cfgr::PllmulR::Mul14 => src_spd * 14,
            rcc::cfgr::PllmulR::Mul15 => src_spd * 15,
            rcc::cfgr::PllmulR::Mul16 => src_spd * 16,
            _ => src_spd,
        }
    }
}

/// Preconfigured clock speed options
/// enum fields can't start with a number makes the names not look as good
pub enum Speed {
    /// 8Mhz
    S8Mhz,
    /// 16 Mhz
    S16Mhz,
    /// 32Mhz
    S32Mhz,
    /// 72 Mhz
    S72Mhz,}

/// Initializes SYSCLK to requested speed
/// uses external clock for all speeds
pub fn init(rcc: &Rcc, flash: &Flash, speed: Speed) {
    // enable external clock
    rcc.cr.modify(|_,w| w.hseon().enabled());
    while rcc.cr.read().hserdy().is_notready() {}

    // configure pll to external clock
    rcc.cfgr.modify(|_,w| w.pllsrc().external());

    // enable flash prefetch buffer
    flash.acr.modify(|_,w| w.prftbe().enabled());

    match speed {
        Speed::S8Mhz => rcc.cfgr.modify(|_,w| w.sw().hse()),
        Speed::S16Mhz => {
            rcc.cfgr.modify(|_,w| w.pllmul().mul2());
            use_pll(rcc);
        }
        Speed::S32Mhz => {
            rcc.cfgr.modify(|_,w| w.pllmul().mul4());

            // set flash latency to one
            flash.acr.modify(|_,w| w.latency().one());
            use_pll(rcc);
        }
        Speed::S72Mhz => {
            rcc.cfgr.modify(|_,w| w.pllmul().mul9());

            // set apb1 to hclk / 2
            rcc.cfgr.modify(|_,w| w.ppre1().div2());

            // set flash latency to two
            flash.acr.modify(|_,w| w.latency().two());
            use_pll(rcc);
        }
    }
}

fn use_pll(rcc: &Rcc) {
    // enable pll
    rcc.cr.modify(|_,w| w.pllon().enabled());

    while rcc.cr.read().pllrdy().is_unlocked() {}

    // set system clock to pll
    rcc.cfgr.modify(|_,w| w.sw().pll());
}<commit_msg>make sure system clock is set to pll before returning from use_pll<commit_after>//! System and Hardware Clocks

/// Internal Oscillator
pub const HSI: u32 = 8_000_000;
/// External clock
pub const HSE: u32 = 8_000_000;

use stm32f103xx::{Rcc, Flash, rcc};

/// Board clock speeds
pub struct ClockSpeeds{
    /// System Clock
    pub sysclk: u32,
    /// AHB peripheral clock
    pub hclk: u32,
    /// Low speed bus
    pub apb1: u32,
    /// high speed bus
    pub apb2: u32,
}

impl ClockSpeeds {
    /// Get clock speeds
    pub fn get(rcc: &Rcc) -> ClockSpeeds {
        let sysclk = match rcc.cfgr.read().sws(){
            rcc::cfgr::SwsR::Hse => HSE,
            rcc::cfgr::SwsR::Pll => Self::get_pll_speed(rcc),
            _ => HSI,
        };

        let hclk = match rcc.cfgr.read().hpre() {
            rcc::cfgr::HpreR::Div2 => sysclk / 2,
            rcc::cfgr::HpreR::Div4 => sysclk / 4,
            rcc::cfgr::HpreR::Div8 => sysclk / 8,
            rcc::cfgr::HpreR::Div16 => sysclk / 16,
            rcc::cfgr::HpreR::Div64 => sysclk / 64,
            rcc::cfgr::HpreR::Div128 => sysclk / 128,
            rcc::cfgr::HpreR::Div256 => sysclk / 256,
            rcc::cfgr::HpreR::Div512 => sysclk / 512,
            _ => sysclk,
        };

        let apb1 = match rcc.cfgr.read().ppre1() {
            rcc::cfgr::Ppre1R::Div2 => hclk / 2,
            rcc::cfgr::Ppre1R::Div4 => hclk / 4,
            rcc::cfgr::Ppre1R::Div8 => hclk / 8,
            rcc::cfgr::Ppre1R::Div16 => hclk / 16,
            _ => hclk,
        };

        let apb2 = match rcc.cfgr.read().ppre2() {
            rcc::cfgr::Ppre2R::Div2 => hclk / 2,
            rcc::cfgr::Ppre2R::Div4 => hclk / 4,
            rcc::cfgr::Ppre2R::Div8 => hclk / 8,
            rcc::cfgr::Ppre2R::Div16 => hclk / 16,
            _ => hclk,
        };

        ClockSpeeds{
            sysclk: sysclk,
            hclk: hclk,
            apb1: apb1,
            apb2: apb2,
        }
    }

    fn get_pll_speed(rcc: &Rcc) -> u32 {
        let hse_div = match rcc.cfgr.read().pllxtpre(){
            rcc::cfgr::PllxtpreR::Div1 => 1,
            rcc::cfgr::PllxtpreR::Div2 => 2,
        };

        let src_spd = match rcc.cfgr.read().pllsrc(){
            rcc::cfgr::PllsrcR::Internal => HSI / 2,
            rcc::cfgr::PllsrcR::External => HSE / hse_div,
        };

        match rcc.cfgr.read().pllmul() {
            rcc::cfgr::PllmulR::Mul2 => src_spd * 2,
            rcc::cfgr::PllmulR::Mul3 => src_spd * 3,
            rcc::cfgr::PllmulR::Mul4 => src_spd * 4,
            rcc::cfgr::PllmulR::Mul5 => src_spd * 5,
            rcc::cfgr::PllmulR::Mul6 => src_spd * 6,
            rcc::cfgr::PllmulR::Mul7 => src_spd * 7,
            rcc::cfgr::PllmulR::Mul8 => src_spd * 8,
            rcc::cfgr::PllmulR::Mul9 => src_spd * 9,
            rcc::cfgr::PllmulR::Mul10 => src_spd * 10,
            rcc::cfgr::PllmulR::Mul11 => src_spd * 11,
            rcc::cfgr::PllmulR::Mul12 => src_spd * 12,
            rcc::cfgr::PllmulR::Mul13 => src_spd * 13,
            rcc::cfgr::PllmulR::Mul14 => src_spd * 14,
            rcc::cfgr::PllmulR::Mul15 => src_spd * 15,
            rcc::cfgr::PllmulR::Mul16 => src_spd * 16,
            _ => src_spd,
        }
    }
}

/// Preconfigured clock speed options
/// enum fields can't start with a number makes the names not look as good
pub enum Speed {
    /// 8Mhz
    S8Mhz,
    /// 16 Mhz
    S16Mhz,
    /// 32Mhz
    S32Mhz,
    /// 72 Mhz
    S72Mhz,}

/// Initializes SYSCLK to requested speed
/// uses external clock for all speeds
pub fn init(rcc: &Rcc, flash: &Flash, speed: Speed) {
    // enable external clock
    rcc.cr.modify(|_,w| w.hseon().enabled());
    while rcc.cr.read().hserdy().is_notready() {}

    // configure pll to external clock
    rcc.cfgr.modify(|_,w| w.pllsrc().external());

    // enable flash prefetch buffer
    flash.acr.modify(|_,w| w.prftbe().enabled());

    match speed {
        Speed::S8Mhz => rcc.cfgr.modify(|_,w| w.sw().hse()),
        Speed::S16Mhz => {
            rcc.cfgr.modify(|_,w| w.pllmul().mul2());
            use_pll(rcc);
        }
        Speed::S32Mhz => {
            rcc.cfgr.modify(|_,w| w.pllmul().mul4());

            // set flash latency to one
            flash.acr.modify(|_,w| w.latency().one());
            use_pll(rcc);
        }
        Speed::S72Mhz => {
            rcc.cfgr.modify(|_,w| w.pllmul().mul9());

            // set apb1 to hclk / 2
            rcc.cfgr.modify(|_,w| w.ppre1().div2());

            // set flash latency to two
            flash.acr.modify(|_,w| w.latency().two());
            use_pll(rcc);
        }
    }
}

fn use_pll(rcc: &Rcc) {
    // enable pll
    rcc.cr.modify(|_,w| w.pllon().enabled());

    while rcc.cr.read().pllrdy().is_unlocked() {}

    // set system clock to pll
    rcc.cfgr.modify(|_,w| w.sw().pll());

    while !rcc.cfgr.read().sws().is_pll() {}
}<|endoftext|>


<commit_before>//! The Rust compiler.
//!
//! # Note
//!
//! This API is completely unstable and subject to change.

#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
      html_favicon_url = "https://doc.rust-lang.org/favicon.ico",
      html_root_url = "https://doc.rust-lang.org/nightly/")]
#![deny(warnings)]

#![feature(box_syntax)]

use std::any::Any;
use std::io::Write;
use std::fs;
use std::path::Path;
use std::sync::{mpsc, Arc};

use rustc_data_structures::owning_ref::OwningRef;
use flate2::Compression;
use flate2::write::DeflateEncoder;

use syntax::symbol::Symbol;
use rustc::hir::def_id::LOCAL_CRATE;
use rustc::session::{Session, CompileIncomplete};
use rustc::session::config::{CrateType, OutputFilenames, PrintRequest};
use rustc::ty::TyCtxt;
use rustc::ty::query::Providers;
use rustc::middle::cstore::EncodedMetadata;
use rustc::middle::cstore::MetadataLoader;
use rustc::dep_graph::DepGraph;
use rustc_target::spec::Target;
use rustc_mir::monomorphize::collector;
use link::out_filename;

pub use rustc_data_structures::sync::MetadataRef;

pub trait CodegenBackend {
    fn init(&self, _sess: &Session) {}
    fn print(&self, _req: PrintRequest, _sess: &Session) {}
    fn target_features(&self, _sess: &Session) -> Vec<Symbol> { vec![] }
    fn print_passes(&self) {}
    fn print_version(&self) {}
    fn diagnostics(&self) -> &[(&'static str, &'static str)] { &[] }

    fn metadata_loader(&self) -> Box<dyn MetadataLoader + Sync>;
    fn provide(&self, _providers: &mut Providers);
    fn provide_extern(&self, _providers: &mut Providers);
    fn codegen_crate<'a, 'tcx>(
        &self,
        tcx: TyCtxt<'a, 'tcx, 'tcx>,
        rx: mpsc::Receiver<Box<dyn Any + Send>>
    ) -> Box<dyn Any>;

    /// This is called on the returned `Box<dyn Any>` from `codegen_backend`
    ///
    /// # Panics
    ///
    /// Panics when the passed `Box<dyn Any>` was not returned by `codegen_backend`.
    fn join_codegen_and_link(
        &self,
        ongoing_codegen: Box<dyn Any>,
        sess: &Session,
        dep_graph: &DepGraph,
        outputs: &OutputFilenames,
    ) -> Result<(), CompileIncomplete>;
}

pub struct NoLlvmMetadataLoader;

impl MetadataLoader for NoLlvmMetadataLoader {
    fn get_rlib_metadata(&self, _: &Target, filename: &Path) -> Result<MetadataRef, String> {
        let buf = fs::read(filename).map_err(|e| format!("metadata file open err: {:?}", e))?;
        let buf: OwningRef<Vec<u8>, [u8]> = OwningRef::new(buf);
        Ok(rustc_erase_owner!(buf.map_owner_box()))
    }

    fn get_dylib_metadata(&self, target: &Target, filename: &Path) -> Result<MetadataRef, String> {
        self.get_rlib_metadata(target, filename)
    }
}

pub struct MetadataOnlyCodegenBackend(());
pub struct OngoingCodegen {
    metadata: EncodedMetadata,
    metadata_version: Vec<u8>,
    crate_name: Symbol,
}

impl MetadataOnlyCodegenBackend {
    pub fn boxed() -> Box<dyn CodegenBackend> {
        box MetadataOnlyCodegenBackend(())
    }
}

impl CodegenBackend for MetadataOnlyCodegenBackend {
    fn init(&self, sess: &Session) {
        for cty in sess.opts.crate_types.iter() {
            match *cty {
                CrateType::Rlib | CrateType::Dylib | CrateType::Executable => {},
                _ => {
                    sess.diagnostic().warn(
                        &format!("LLVM unsupported, so output type {} is not supported", cty)
                    );
                },
            }
        }
    }

    fn metadata_loader(&self) -> Box<dyn MetadataLoader + Sync> {
        box NoLlvmMetadataLoader
    }

    fn provide(&self, providers: &mut Providers) {
        ::symbol_names::provide(providers);

        providers.target_features_whitelist = |_tcx, _cnum| {
            Default::default() // Just a dummy
        };
        providers.is_reachable_non_generic = |_tcx, _defid| true;
        providers.exported_symbols = |_tcx, _crate| Arc::new(Vec::new());
    }
    fn provide_extern(&self, providers: &mut Providers) {
        providers.is_reachable_non_generic = |_tcx, _defid| true;
    }

    fn codegen_crate<'a, 'tcx>(
        &self,
        tcx: TyCtxt<'a, 'tcx, 'tcx>,
        _rx: mpsc::Receiver<Box<dyn Any + Send>>
    ) -> Box<dyn Any> {
        use rustc_mir::monomorphize::item::MonoItem;

        ::check_for_rustc_errors_attr(tcx);
        ::symbol_names_test::report_symbol_names(tcx);
        ::rustc_incremental::assert_dep_graph(tcx);
        ::rustc_incremental::assert_module_sources::assert_module_sources(tcx);
        ::rustc_mir::monomorphize::assert_symbols_are_distinct(tcx,
            collector::collect_crate_mono_items(
                tcx,
                collector::MonoItemCollectionMode::Eager
            ).0.iter()
        );
        // FIXME: Fix this
        // ::rustc::middle::dependency_format::calculate(tcx);
        let _ = tcx.link_args(LOCAL_CRATE);
        let _ = tcx.native_libraries(LOCAL_CRATE);
        for mono_item in
            collector::collect_crate_mono_items(
                tcx,
                collector::MonoItemCollectionMode::Eager
            ).0 {
            if let MonoItem::Fn(inst) = mono_item {
                let def_id = inst.def_id();
                if def_id.is_local()  {
                    let _ = inst.def.is_inline(tcx);
                    let _ = tcx.codegen_fn_attrs(def_id);
                }
            }
        }
        tcx.sess.abort_if_errors();

        let metadata = tcx.encode_metadata();

        box OngoingCodegen {
            metadata,
            metadata_version: tcx.metadata_encoding_version().to_vec(),
            crate_name: tcx.crate_name(LOCAL_CRATE),
        }
    }

    fn join_codegen_and_link(
        &self,
        ongoing_codegen: Box<dyn Any>,
        sess: &Session,
        _dep_graph: &DepGraph,
        outputs: &OutputFilenames,
    ) -> Result<(), CompileIncomplete> {
        let ongoing_codegen = ongoing_codegen.downcast::<OngoingCodegen>()
            .expect("Expected MetadataOnlyCodegenBackend's OngoingCodegen, found Box<dyn Any>");
        for &crate_type in sess.opts.crate_types.iter() {
            if crate_type != CrateType::Rlib &&
               crate_type != CrateType::Dylib {
                continue;
            }
            let output_name =
                out_filename(sess, crate_type, &outputs, &ongoing_codegen.crate_name.as_str());
            let mut compressed = ongoing_codegen.metadata_version.clone();
            let metadata = if crate_type == CrateType::Dylib {
                DeflateEncoder::new(&mut compressed, Compression::fast())
                    .write_all(&ongoing_codegen.metadata.raw_data)
                    .unwrap();
                &compressed
            } else {
                &ongoing_codegen.metadata.raw_data
            };
            fs::write(&output_name, metadata).unwrap();
        }

        sess.abort_if_errors();
        if !sess.opts.crate_types.contains(&CrateType::Rlib)
            && !sess.opts.crate_types.contains(&CrateType::Dylib)
        {
            sess.fatal("Executables are not supported by the metadata-only backend.");
        }
        Ok(())
    }
}
<commit_msg>Rollup merge of #57418 - lqd:collector_query, r=michaelwoerister<commit_after>//! The Rust compiler.
//!
//! # Note
//!
//! This API is completely unstable and subject to change.

#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
      html_favicon_url = "https://doc.rust-lang.org/favicon.ico",
      html_root_url = "https://doc.rust-lang.org/nightly/")]
#![deny(warnings)]

#![feature(box_syntax)]

use std::any::Any;
use std::io::Write;
use std::fs;
use std::path::Path;
use std::sync::{mpsc, Arc};

use rustc_data_structures::owning_ref::OwningRef;
use flate2::Compression;
use flate2::write::DeflateEncoder;

use syntax::symbol::Symbol;
use rustc::hir::def_id::LOCAL_CRATE;
use rustc::session::{Session, CompileIncomplete};
use rustc::session::config::{CrateType, OutputFilenames, PrintRequest};
use rustc::ty::TyCtxt;
use rustc::ty::query::Providers;
use rustc::middle::cstore::EncodedMetadata;
use rustc::middle::cstore::MetadataLoader;
use rustc::dep_graph::DepGraph;
use rustc_target::spec::Target;
use link::out_filename;

pub use rustc_data_structures::sync::MetadataRef;

pub trait CodegenBackend {
    fn init(&self, _sess: &Session) {}
    fn print(&self, _req: PrintRequest, _sess: &Session) {}
    fn target_features(&self, _sess: &Session) -> Vec<Symbol> { vec![] }
    fn print_passes(&self) {}
    fn print_version(&self) {}
    fn diagnostics(&self) -> &[(&'static str, &'static str)] { &[] }

    fn metadata_loader(&self) -> Box<dyn MetadataLoader + Sync>;
    fn provide(&self, _providers: &mut Providers);
    fn provide_extern(&self, _providers: &mut Providers);
    fn codegen_crate<'a, 'tcx>(
        &self,
        tcx: TyCtxt<'a, 'tcx, 'tcx>,
        rx: mpsc::Receiver<Box<dyn Any + Send>>
    ) -> Box<dyn Any>;

    /// This is called on the returned `Box<dyn Any>` from `codegen_backend`
    ///
    /// # Panics
    ///
    /// Panics when the passed `Box<dyn Any>` was not returned by `codegen_backend`.
    fn join_codegen_and_link(
        &self,
        ongoing_codegen: Box<dyn Any>,
        sess: &Session,
        dep_graph: &DepGraph,
        outputs: &OutputFilenames,
    ) -> Result<(), CompileIncomplete>;
}

pub struct NoLlvmMetadataLoader;

impl MetadataLoader for NoLlvmMetadataLoader {
    fn get_rlib_metadata(&self, _: &Target, filename: &Path) -> Result<MetadataRef, String> {
        let buf = fs::read(filename).map_err(|e| format!("metadata file open err: {:?}", e))?;
        let buf: OwningRef<Vec<u8>, [u8]> = OwningRef::new(buf);
        Ok(rustc_erase_owner!(buf.map_owner_box()))
    }

    fn get_dylib_metadata(&self, target: &Target, filename: &Path) -> Result<MetadataRef, String> {
        self.get_rlib_metadata(target, filename)
    }
}

pub struct MetadataOnlyCodegenBackend(());
pub struct OngoingCodegen {
    metadata: EncodedMetadata,
    metadata_version: Vec<u8>,
    crate_name: Symbol,
}

impl MetadataOnlyCodegenBackend {
    pub fn boxed() -> Box<dyn CodegenBackend> {
        box MetadataOnlyCodegenBackend(())
    }
}

impl CodegenBackend for MetadataOnlyCodegenBackend {
    fn init(&self, sess: &Session) {
        for cty in sess.opts.crate_types.iter() {
            match *cty {
                CrateType::Rlib | CrateType::Dylib | CrateType::Executable => {},
                _ => {
                    sess.diagnostic().warn(
                        &format!("LLVM unsupported, so output type {} is not supported", cty)
                    );
                },
            }
        }
    }

    fn metadata_loader(&self) -> Box<dyn MetadataLoader + Sync> {
        box NoLlvmMetadataLoader
    }

    fn provide(&self, providers: &mut Providers) {
        ::symbol_names::provide(providers);

        providers.target_features_whitelist = |_tcx, _cnum| {
            Default::default() // Just a dummy
        };
        providers.is_reachable_non_generic = |_tcx, _defid| true;
        providers.exported_symbols = |_tcx, _crate| Arc::new(Vec::new());
    }
    fn provide_extern(&self, providers: &mut Providers) {
        providers.is_reachable_non_generic = |_tcx, _defid| true;
    }

    fn codegen_crate<'a, 'tcx>(
        &self,
        tcx: TyCtxt<'a, 'tcx, 'tcx>,
        _rx: mpsc::Receiver<Box<dyn Any + Send>>
    ) -> Box<dyn Any> {
        use rustc_mir::monomorphize::item::MonoItem;

        ::check_for_rustc_errors_attr(tcx);
        ::symbol_names_test::report_symbol_names(tcx);
        ::rustc_incremental::assert_dep_graph(tcx);
        ::rustc_incremental::assert_module_sources::assert_module_sources(tcx);
        // FIXME: Fix this
        // ::rustc::middle::dependency_format::calculate(tcx);
        let _ = tcx.link_args(LOCAL_CRATE);
        let _ = tcx.native_libraries(LOCAL_CRATE);
        let (_, cgus) = tcx.collect_and_partition_mono_items(LOCAL_CRATE);
        for (mono_item, _) in cgus.iter().flat_map(|cgu| cgu.items().iter()) {
            if let MonoItem::Fn(inst) = mono_item {
                let def_id = inst.def_id();
                if def_id.is_local() {
                    let _ = tcx.codegen_fn_attrs(def_id);
                }
            }
        }
        tcx.sess.abort_if_errors();

        let metadata = tcx.encode_metadata();

        box OngoingCodegen {
            metadata,
            metadata_version: tcx.metadata_encoding_version().to_vec(),
            crate_name: tcx.crate_name(LOCAL_CRATE),
        }
    }

    fn join_codegen_and_link(
        &self,
        ongoing_codegen: Box<dyn Any>,
        sess: &Session,
        _dep_graph: &DepGraph,
        outputs: &OutputFilenames,
    ) -> Result<(), CompileIncomplete> {
        let ongoing_codegen = ongoing_codegen.downcast::<OngoingCodegen>()
            .expect("Expected MetadataOnlyCodegenBackend's OngoingCodegen, found Box<dyn Any>");
        for &crate_type in sess.opts.crate_types.iter() {
            if crate_type != CrateType::Rlib &&
               crate_type != CrateType::Dylib {
                continue;
            }
            let output_name =
                out_filename(sess, crate_type, &outputs, &ongoing_codegen.crate_name.as_str());
            let mut compressed = ongoing_codegen.metadata_version.clone();
            let metadata = if crate_type == CrateType::Dylib {
                DeflateEncoder::new(&mut compressed, Compression::fast())
                    .write_all(&ongoing_codegen.metadata.raw_data)
                    .unwrap();
                &compressed
            } else {
                &ongoing_codegen.metadata.raw_data
            };
            fs::write(&output_name, metadata).unwrap();
        }

        sess.abort_if_errors();
        if !sess.opts.crate_types.contains(&CrateType::Rlib)
            && !sess.opts.crate_types.contains(&CrateType::Dylib)
        {
            sess.fatal("Executables are not supported by the metadata-only backend.");
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before>use std::clone::Clone;
use std::collections::HashMap;
use std::default::Default;
use std::path::{Path, PathBuf};

use chrono::{Datelike, Timelike};
use failure::ResultExt;
use itertools;
use jsonfeed;
use liquid;
use liquid::value::Object;
use liquid::value::Value;
use regex::Regex;
use rss;

use crate::cobalt_model;
use crate::cobalt_model::files;
use crate::cobalt_model::permalink;
use crate::cobalt_model::slug;
use crate::error::*;

/// Convert the source file's relative path into a format useful for generating permalinks that
/// mirror the source directory hierarchy.
fn format_path_variable(source_file: &Path) -> String {
    let parent = source_file
        .parent()
        .and_then(|p| p.to_str())
        .unwrap_or("")
        .to_owned();
    let mut path = parent.replace("\\", "/");
    if path.starts_with("./") {
        path.remove(0);
    }
    if path.starts_with('/') {
        path.remove(0);
    }
    path
}

pub fn permalink_attributes(front: &cobalt_model::Frontmatter, dest_file: &Path) -> Object {
    let mut attributes = Object::new();

    attributes.insert(
        "parent".into(),
        Value::scalar(format_path_variable(dest_file)),
    );

    let filename = dest_file
        .file_stem()
        .and_then(|s| s.to_str())
        .unwrap_or("")
        .to_owned();
    attributes.insert("name".into(), Value::scalar(filename));

    attributes.insert("ext".into(), Value::scalar(".html"));

    // TODO(epage): Add `collection` (the collection's slug), see #257
    // or `parent.slug`, see #323

    attributes.insert("slug".into(), Value::scalar(front.slug.clone()));

    attributes.insert(
        "categories".into(),
        Value::scalar(itertools::join(
            front.categories.iter().map(slug::slugify),
            "/",
        )),
    );

    if let Some(ref date) = front.published_date {
        attributes.insert("year".into(), Value::scalar(date.year().to_string()));
        attributes.insert(
            "month".into(),
            Value::scalar(format!("{:02}", &date.month())),
        );
        attributes.insert("i_month".into(), Value::scalar(date.month().to_string()));
        attributes.insert("day".into(), Value::scalar(format!("{:02}", &date.day())));
        attributes.insert("i_day".into(), Value::scalar(date.day().to_string()));
        attributes.insert("hour".into(), Value::scalar(format!("{:02}", &date.hour())));
        attributes.insert(
            "minute".into(),
            Value::scalar(format!("{:02}", &date.minute())),
        );
        attributes.insert(
            "second".into(),
            Value::scalar(format!("{:02}", &date.second())),
        );
    }

    attributes.insert("data".into(), Value::Object(front.data.clone()));

    attributes
}

fn document_attributes(
    front: &cobalt_model::Frontmatter,
    source_file: &Path,
    url_path: &str,
) -> Object {
    let categories = Value::Array(
        front
            .categories
            .iter()
            .cloned()
            .map(Value::scalar)
            .collect(),
    );
    // Reason for `file`:
    // - Allow access to assets in the original location
    // - Ease linking back to page's source
    let file: Object = vec![
        (
            "permalink".into(),
            Value::scalar(source_file.to_str().unwrap_or("").to_owned()),
        ),
        (
            "parent".into(),
            Value::scalar(
                source_file
                    .parent()
                    .and_then(Path::to_str)
                    .unwrap_or("")
                    .to_owned(),
            ),
        ),
    ]
    .into_iter()
    .collect();
    let attributes = vec![
        ("permalink".into(), Value::scalar(url_path.to_owned())),
        ("title".into(), Value::scalar(front.title.clone())),
        ("slug".into(), Value::scalar(front.slug.clone())),
        (
            "description".into(),
            Value::scalar(
                front
                    .description
                    .as_ref()
                    .map(|s| s.as_str())
                    .unwrap_or("")
                    .to_owned(),
            ),
        ),
        ("categories".into(), categories),
        ("is_draft".into(), Value::scalar(front.is_draft)),
        ("weight".into(), Value::scalar(front.weight)),
        ("file".into(), Value::Object(file)),
        ("collection".into(), Value::scalar(front.collection.clone())),
        ("data".into(), Value::Object(front.data.clone())),
    ];
    let mut attributes: Object = attributes.into_iter().collect();

    if let Some(ref tags) = front.tags {
        let tags = Value::Array(tags.iter().cloned().map(Value::scalar).collect());
        attributes.insert("tags".into(), tags);
    }

    if let Some(ref published_date) = front.published_date {
        attributes.insert(
            "published_date".into(),
            Value::scalar(liquid::value::Date::from(*published_date)),
        );
    }

    attributes
}

#[derive(Debug, Clone)]
pub struct Document {
    pub url_path: String,
    pub file_path: PathBuf,
    pub content: String,
    pub attributes: Object,
    pub front: cobalt_model::Frontmatter,
}

impl Document {
    pub fn new(
        url_path: String,
        file_path: PathBuf,
        content: String,
        attributes: Object,
        front: cobalt_model::Frontmatter,
    ) -> Document {
        Document {
            url_path,
            file_path,
            content,
            attributes,
            front,
        }
    }

    pub fn parse(
        src_path: &Path,
        rel_path: &Path,
        default_front: cobalt_model::FrontmatterBuilder,
    ) -> Result<Document> {
        trace!("Parsing {:?}", rel_path);
        let content = files::read_file(src_path)?;
        let builder =
            cobalt_model::DocumentBuilder::<cobalt_model::FrontmatterBuilder>::parse(&content)?;
        let (front, content) = builder.parts();
        let front = front.merge_path(rel_path).merge(default_front);

        let front = front.build()?;

        let (file_path, url_path) = {
            let perma_attributes = permalink_attributes(&front, rel_path);
            let url_path = permalink::explode_permalink(&front.permalink, &perma_attributes)
                .with_context(|_| {
                    failure::format_err!("Failed to create permalink `{}`", front.permalink)
                })?;
            let file_path = permalink::format_url_as_file(&url_path);
            (file_path, url_path)
        };

        let doc_attributes = document_attributes(&front, rel_path, url_path.as_ref());

        Ok(Document::new(
            url_path,
            file_path,
            content.to_string(),
            doc_attributes,
            front,
        ))
    }

    /// Metadata for generating RSS feeds
    pub fn to_rss(&self, root_url: &str) -> Result<rss::Item> {
        let link = format!("{}/{}", root_url, &self.url_path);
        let guid = rss::GuidBuilder::default()
            .value(link.clone())
            .permalink(true)
            .build()
            .map_err(failure::err_msg)?;

        let item = rss::ItemBuilder::default()
            .title(Some(self.front.title.clone()))
            .link(Some(link))
            .guid(Some(guid))
            .pub_date(self.front.published_date.map(|date| date.to_rfc2822()))
            .description(self.description_to_str())
            .build()
            .map_err(failure::err_msg)?;
        Ok(item)
    }

    /// Metadata for generating JSON feeds
    pub fn to_jsonfeed(&self, root_url: &str) -> jsonfeed::Item {
        let link = format!("{}/{}", root_url, &self.url_path);

        jsonfeed::Item {
            id: link.clone(),
            url: Some(link),
            title: Some(self.front.title.clone()),
            content: jsonfeed::Content::Html(
                self.description_to_str().unwrap_or_else(|| "".into()),
            ),
            date_published: self.front.published_date.map(|date| date.to_rfc2822()),
            // TODO completely implement categories, see Issue 131
            tags: Some(self.front.categories.clone()),
            ..Default::default()
        }
    }

    pub fn to_sitemap<T: std::io::Write>(
        &self,
        root_url: &str,
        writer: &mut sitemap::writer::UrlSetWriter<T>,
    ) -> Result<()> {
        let link = format!("{}/{}", root_url, &self.url_path);
        let mut url = sitemap::structs::UrlEntry::builder();
        url = url.loc(link);
        if let Some(date) = self.front.published_date {
            url = url.lastmod(*date);
        }
        writer.url(url)?;
        Ok(())
    }

    fn description_to_str(&self) -> Option<String> {
        self.front
            .description
            .clone()
            .or_else(|| {
                self.attributes
                    .get("excerpt")
                    .map(|s| s.render().to_string())
            })
            .or_else(|| {
                self.attributes
                    .get("content")
                    .map(|s| s.render().to_string())
            })
    }

    /// Renders liquid templates into HTML in the context of current document.
    ///
    /// Takes `content` string and returns rendered HTML. This function doesn't
    /// take `"extends"` attribute into account. This function can be used for
    /// rendering content or excerpt.
    fn render_html(
        &self,
        content: &str,
        globals: &Object,
        parser: &cobalt_model::Liquid,
        markdown: &cobalt_model::Markdown,
    ) -> Result<String> {
        let template = parser.parse(content)?;
        let html = template.render(globals)?;

        let html = match self.front.format {
            cobalt_model::SourceFormat::Raw => html,
            cobalt_model::SourceFormat::Markdown => markdown.parse(&html)?,
        };
        Ok(html.to_owned())
    }

    /// Renders excerpt and adds it to attributes of the document.
    pub fn render_excerpt(
        &mut self,
        globals: &Object,
        parser: &cobalt_model::Liquid,
        markdown: &cobalt_model::Markdown,
    ) -> Result<()> {
        let value = if let Some(excerpt_str) = self.front.excerpt.as_ref() {
            let excerpt = self.render_html(excerpt_str, globals, parser, markdown)?;
            Value::scalar(excerpt)
        } else if self.front.excerpt_separator.is_empty() {
            Value::Nil
        } else {
            let excerpt = extract_excerpt(
                &self.content,
                self.front.format,
                &self.front.excerpt_separator,
            );
            let excerpt = self.render_html(&excerpt, globals, parser, markdown)?;
            Value::scalar(excerpt)
        };

        self.attributes.insert("excerpt".into(), value);
        Ok(())
    }

    /// Renders the content and adds it to attributes of the document.
    ///
    /// When we say "content" we mean only this document without extended layout.
    pub fn render_content(
        &mut self,
        globals: &Object,
        parser: &cobalt_model::Liquid,
        markdown: &cobalt_model::Markdown,
    ) -> Result<()> {
        let content_html = self.render_html(&self.content, globals, parser, markdown)?;
        self.attributes
            .insert("content".into(), Value::scalar(content_html.clone()));
        Ok(())
    }

    /// Renders the document to an HTML string.
    ///
    /// Side effects:
    ///
    /// * layout may be inserted to layouts cache
    pub fn render(
        &mut self,
        globals: &Object,
        parser: &cobalt_model::Liquid,
        layouts: &HashMap<String, String>,
    ) -> Result<String> {
        if let Some(ref layout) = self.front.layout {
            let layout_data_ref = layouts.get(layout).ok_or_else(|| {
                failure::format_err!(
                    "Layout {} does not exist (referenced in {}).",
                    layout,
                    self.file_path.display()
                )
            })?;

            let template = parser
                .parse(layout_data_ref)
                .with_context(|_| failure::format_err!("Failed to parse layout {:?}", layout))?;
            let content_html = template
                .render(globals)
                .with_context(|_| failure::format_err!("Failed to render layout {:?}", layout))?;
            Ok(content_html)
        } else {
            let content_html = globals
                .get("page")
                .ok_or_else(|| failure::err_msg("Internal error: page isn't in globals"))?
                .get(&liquid::value::Scalar::new("content"))
                .ok_or_else(|| failure::err_msg("Internal error: page.content isn't in globals"))?
                .render()
                .to_string();

            Ok(content_html)
        }
    }
}

fn extract_excerpt_raw(content: &str, excerpt_separator: &str) -> String {
    content
        .split(excerpt_separator)
        .next()
        .unwrap_or(content)
        .to_owned()
}

fn extract_excerpt_markdown(content: &str, excerpt_separator: &str) -> String {
    lazy_static! {
        static ref MARKDOWN_REF: Regex = Regex::new(r"(?m:^ {0,3}\[[^\]]+\]:.+$)").unwrap();
    }

    let mut trail = String::new();

    if MARKDOWN_REF.is_match(content) {
        for mat in MARKDOWN_REF.find_iter(content) {
            trail.push_str(mat.as_str());
            trail.push('\n');
        }
    }
    trail + content.split(excerpt_separator).next().unwrap_or(content)
}

fn extract_excerpt(
    content: &str,
    format: cobalt_model::SourceFormat,
    excerpt_separator: &str,
) -> String {
    match format {
        cobalt_model::SourceFormat::Markdown => {
            extract_excerpt_markdown(content, excerpt_separator)
        }
        cobalt_model::SourceFormat::Raw => extract_excerpt_raw(content, excerpt_separator),
    }
}

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

    #[test]
    fn format_path_variable_file() {
        let input = Path::new("/hello/world/file.liquid");
        let actual = format_path_variable(input);
        assert_eq!(actual, "hello/world");
    }

    #[test]
    fn format_path_variable_relative() {
        let input = Path::new("hello/world/file.liquid");
        let actual = format_path_variable(input);
        assert_eq!(actual, "hello/world");

        let input = Path::new("./hello/world/file.liquid");
        let actual = format_path_variable(input);
        assert_eq!(actual, "hello/world");
    }
}
<commit_msg>fix: Do not render `Nil` excerpt<commit_after>use std::clone::Clone;
use std::collections::HashMap;
use std::default::Default;
use std::path::{Path, PathBuf};

use chrono::{Datelike, Timelike};
use failure::ResultExt;
use itertools;
use jsonfeed;
use liquid;
use liquid::value::Object;
use liquid::value::Value;
use regex::Regex;
use rss;

use crate::cobalt_model;
use crate::cobalt_model::files;
use crate::cobalt_model::permalink;
use crate::cobalt_model::slug;
use crate::error::*;

/// Convert the source file's relative path into a format useful for generating permalinks that
/// mirror the source directory hierarchy.
fn format_path_variable(source_file: &Path) -> String {
    let parent = source_file
        .parent()
        .and_then(|p| p.to_str())
        .unwrap_or("")
        .to_owned();
    let mut path = parent.replace("\\", "/");
    if path.starts_with("./") {
        path.remove(0);
    }
    if path.starts_with('/') {
        path.remove(0);
    }
    path
}

pub fn permalink_attributes(front: &cobalt_model::Frontmatter, dest_file: &Path) -> Object {
    let mut attributes = Object::new();

    attributes.insert(
        "parent".into(),
        Value::scalar(format_path_variable(dest_file)),
    );

    let filename = dest_file
        .file_stem()
        .and_then(|s| s.to_str())
        .unwrap_or("")
        .to_owned();
    attributes.insert("name".into(), Value::scalar(filename));

    attributes.insert("ext".into(), Value::scalar(".html"));

    // TODO(epage): Add `collection` (the collection's slug), see #257
    // or `parent.slug`, see #323

    attributes.insert("slug".into(), Value::scalar(front.slug.clone()));

    attributes.insert(
        "categories".into(),
        Value::scalar(itertools::join(
            front.categories.iter().map(slug::slugify),
            "/",
        )),
    );

    if let Some(ref date) = front.published_date {
        attributes.insert("year".into(), Value::scalar(date.year().to_string()));
        attributes.insert(
            "month".into(),
            Value::scalar(format!("{:02}", &date.month())),
        );
        attributes.insert("i_month".into(), Value::scalar(date.month().to_string()));
        attributes.insert("day".into(), Value::scalar(format!("{:02}", &date.day())));
        attributes.insert("i_day".into(), Value::scalar(date.day().to_string()));
        attributes.insert("hour".into(), Value::scalar(format!("{:02}", &date.hour())));
        attributes.insert(
            "minute".into(),
            Value::scalar(format!("{:02}", &date.minute())),
        );
        attributes.insert(
            "second".into(),
            Value::scalar(format!("{:02}", &date.second())),
        );
    }

    attributes.insert("data".into(), Value::Object(front.data.clone()));

    attributes
}

fn document_attributes(
    front: &cobalt_model::Frontmatter,
    source_file: &Path,
    url_path: &str,
) -> Object {
    let categories = Value::Array(
        front
            .categories
            .iter()
            .cloned()
            .map(Value::scalar)
            .collect(),
    );
    // Reason for `file`:
    // - Allow access to assets in the original location
    // - Ease linking back to page's source
    let file: Object = vec![
        (
            "permalink".into(),
            Value::scalar(source_file.to_str().unwrap_or("").to_owned()),
        ),
        (
            "parent".into(),
            Value::scalar(
                source_file
                    .parent()
                    .and_then(Path::to_str)
                    .unwrap_or("")
                    .to_owned(),
            ),
        ),
    ]
    .into_iter()
    .collect();
    let attributes = vec![
        ("permalink".into(), Value::scalar(url_path.to_owned())),
        ("title".into(), Value::scalar(front.title.clone())),
        ("slug".into(), Value::scalar(front.slug.clone())),
        (
            "description".into(),
            Value::scalar(
                front
                    .description
                    .as_ref()
                    .map(|s| s.as_str())
                    .unwrap_or("")
                    .to_owned(),
            ),
        ),
        ("categories".into(), categories),
        ("is_draft".into(), Value::scalar(front.is_draft)),
        ("weight".into(), Value::scalar(front.weight)),
        ("file".into(), Value::Object(file)),
        ("collection".into(), Value::scalar(front.collection.clone())),
        ("data".into(), Value::Object(front.data.clone())),
    ];
    let mut attributes: Object = attributes.into_iter().collect();

    if let Some(ref tags) = front.tags {
        let tags = Value::Array(tags.iter().cloned().map(Value::scalar).collect());
        attributes.insert("tags".into(), tags);
    }

    if let Some(ref published_date) = front.published_date {
        attributes.insert(
            "published_date".into(),
            Value::scalar(liquid::value::Date::from(*published_date)),
        );
    }

    attributes
}

#[derive(Debug, Clone)]
pub struct Document {
    pub url_path: String,
    pub file_path: PathBuf,
    pub content: String,
    pub attributes: Object,
    pub front: cobalt_model::Frontmatter,
}

impl Document {
    pub fn new(
        url_path: String,
        file_path: PathBuf,
        content: String,
        attributes: Object,
        front: cobalt_model::Frontmatter,
    ) -> Document {
        Document {
            url_path,
            file_path,
            content,
            attributes,
            front,
        }
    }

    pub fn parse(
        src_path: &Path,
        rel_path: &Path,
        default_front: cobalt_model::FrontmatterBuilder,
    ) -> Result<Document> {
        trace!("Parsing {:?}", rel_path);
        let content = files::read_file(src_path)?;
        let builder =
            cobalt_model::DocumentBuilder::<cobalt_model::FrontmatterBuilder>::parse(&content)?;
        let (front, content) = builder.parts();
        let front = front.merge_path(rel_path).merge(default_front);

        let front = front.build()?;

        let (file_path, url_path) = {
            let perma_attributes = permalink_attributes(&front, rel_path);
            let url_path = permalink::explode_permalink(&front.permalink, &perma_attributes)
                .with_context(|_| {
                    failure::format_err!("Failed to create permalink `{}`", front.permalink)
                })?;
            let file_path = permalink::format_url_as_file(&url_path);
            (file_path, url_path)
        };

        let doc_attributes = document_attributes(&front, rel_path, url_path.as_ref());

        Ok(Document::new(
            url_path,
            file_path,
            content.to_string(),
            doc_attributes,
            front,
        ))
    }

    /// Metadata for generating RSS feeds
    pub fn to_rss(&self, root_url: &str) -> Result<rss::Item> {
        let link = format!("{}/{}", root_url, &self.url_path);
        let guid = rss::GuidBuilder::default()
            .value(link.clone())
            .permalink(true)
            .build()
            .map_err(failure::err_msg)?;

        let item = rss::ItemBuilder::default()
            .title(Some(self.front.title.clone()))
            .link(Some(link))
            .guid(Some(guid))
            .pub_date(self.front.published_date.map(|date| date.to_rfc2822()))
            .description(self.description_to_str())
            .build()
            .map_err(failure::err_msg)?;
        Ok(item)
    }

    /// Metadata for generating JSON feeds
    pub fn to_jsonfeed(&self, root_url: &str) -> jsonfeed::Item {
        let link = format!("{}/{}", root_url, &self.url_path);

        jsonfeed::Item {
            id: link.clone(),
            url: Some(link),
            title: Some(self.front.title.clone()),
            content: jsonfeed::Content::Html(
                self.description_to_str().unwrap_or_else(|| "".into()),
            ),
            date_published: self.front.published_date.map(|date| date.to_rfc2822()),
            // TODO completely implement categories, see Issue 131
            tags: Some(self.front.categories.clone()),
            ..Default::default()
        }
    }

    pub fn to_sitemap<T: std::io::Write>(
        &self,
        root_url: &str,
        writer: &mut sitemap::writer::UrlSetWriter<T>,
    ) -> Result<()> {
        let link = format!("{}/{}", root_url, &self.url_path);
        let mut url = sitemap::structs::UrlEntry::builder();
        url = url.loc(link);
        if let Some(date) = self.front.published_date {
            url = url.lastmod(*date);
        }
        writer.url(url)?;
        Ok(())
    }

    fn description_to_str(&self) -> Option<String> {
        self.front
            .description
            .clone()
            .or_else(|| {
                self.attributes.get("excerpt").and_then(|excerpt| {
                    if excerpt.is_nil() {
                        None
                    } else {
                        Some(excerpt.render().to_string())
                    }
                })
            })
            .or_else(|| {
                self.attributes
                    .get("content")
                    .map(|s| s.render().to_string())
            })
    }

    /// Renders liquid templates into HTML in the context of current document.
    ///
    /// Takes `content` string and returns rendered HTML. This function doesn't
    /// take `"extends"` attribute into account. This function can be used for
    /// rendering content or excerpt.
    fn render_html(
        &self,
        content: &str,
        globals: &Object,
        parser: &cobalt_model::Liquid,
        markdown: &cobalt_model::Markdown,
    ) -> Result<String> {
        let template = parser.parse(content)?;
        let html = template.render(globals)?;

        let html = match self.front.format {
            cobalt_model::SourceFormat::Raw => html,
            cobalt_model::SourceFormat::Markdown => markdown.parse(&html)?,
        };
        Ok(html.to_owned())
    }

    /// Renders the excerpt and adds it to attributes of the document.
    ///
    /// The excerpt is either taken from the `excerpt` frontmatter setting, if
    /// given, or extracted from the content, if `excerpt_separator` is not
    /// empty. When neither condition applies, the excerpt is set to the `Nil`
    /// value.
    pub fn render_excerpt(
        &mut self,
        globals: &Object,
        parser: &cobalt_model::Liquid,
        markdown: &cobalt_model::Markdown,
    ) -> Result<()> {
        let value = if let Some(excerpt_str) = self.front.excerpt.as_ref() {
            let excerpt = self.render_html(excerpt_str, globals, parser, markdown)?;
            Value::scalar(excerpt)
        } else if self.front.excerpt_separator.is_empty() {
            Value::Nil
        } else {
            let excerpt = extract_excerpt(
                &self.content,
                self.front.format,
                &self.front.excerpt_separator,
            );
            let excerpt = self.render_html(&excerpt, globals, parser, markdown)?;
            Value::scalar(excerpt)
        };

        self.attributes.insert("excerpt".into(), value);
        Ok(())
    }

    /// Renders the content and adds it to attributes of the document.
    ///
    /// When we say "content" we mean only this document without extended layout.
    pub fn render_content(
        &mut self,
        globals: &Object,
        parser: &cobalt_model::Liquid,
        markdown: &cobalt_model::Markdown,
    ) -> Result<()> {
        let content_html = self.render_html(&self.content, globals, parser, markdown)?;
        self.attributes
            .insert("content".into(), Value::scalar(content_html.clone()));
        Ok(())
    }

    /// Renders the document to an HTML string.
    ///
    /// Side effects:
    ///
    /// * layout may be inserted to layouts cache
    pub fn render(
        &mut self,
        globals: &Object,
        parser: &cobalt_model::Liquid,
        layouts: &HashMap<String, String>,
    ) -> Result<String> {
        if let Some(ref layout) = self.front.layout {
            let layout_data_ref = layouts.get(layout).ok_or_else(|| {
                failure::format_err!(
                    "Layout {} does not exist (referenced in {}).",
                    layout,
                    self.file_path.display()
                )
            })?;

            let template = parser
                .parse(layout_data_ref)
                .with_context(|_| failure::format_err!("Failed to parse layout {:?}", layout))?;
            let content_html = template
                .render(globals)
                .with_context(|_| failure::format_err!("Failed to render layout {:?}", layout))?;
            Ok(content_html)
        } else {
            let content_html = globals
                .get("page")
                .ok_or_else(|| failure::err_msg("Internal error: page isn't in globals"))?
                .get(&liquid::value::Scalar::new("content"))
                .ok_or_else(|| failure::err_msg("Internal error: page.content isn't in globals"))?
                .render()
                .to_string();

            Ok(content_html)
        }
    }
}

fn extract_excerpt_raw(content: &str, excerpt_separator: &str) -> String {
    content
        .split(excerpt_separator)
        .next()
        .unwrap_or(content)
        .to_owned()
}

fn extract_excerpt_markdown(content: &str, excerpt_separator: &str) -> String {
    lazy_static! {
        static ref MARKDOWN_REF: Regex = Regex::new(r"(?m:^ {0,3}\[[^\]]+\]:.+$)").unwrap();
    }

    let mut trail = String::new();

    if MARKDOWN_REF.is_match(content) {
        for mat in MARKDOWN_REF.find_iter(content) {
            trail.push_str(mat.as_str());
            trail.push('\n');
        }
    }
    trail + content.split(excerpt_separator).next().unwrap_or(content)
}

fn extract_excerpt(
    content: &str,
    format: cobalt_model::SourceFormat,
    excerpt_separator: &str,
) -> String {
    match format {
        cobalt_model::SourceFormat::Markdown => {
            extract_excerpt_markdown(content, excerpt_separator)
        }
        cobalt_model::SourceFormat::Raw => extract_excerpt_raw(content, excerpt_separator),
    }
}

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

    #[test]
    fn format_path_variable_file() {
        let input = Path::new("/hello/world/file.liquid");
        let actual = format_path_variable(input);
        assert_eq!(actual, "hello/world");
    }

    #[test]
    fn format_path_variable_relative() {
        let input = Path::new("hello/world/file.liquid");
        let actual = format_path_variable(input);
        assert_eq!(actual, "hello/world");

        let input = Path::new("./hello/world/file.liquid");
        let actual = format_path_variable(input);
        assert_eq!(actual, "hello/world");
    }
}
<|endoftext|>


<commit_before>// Copyright 2016 GFX developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://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.

pub enum MTLDrawable {}

foreign_obj_type! {
    type CType = MTLDrawable;
    pub struct Drawable;
    pub struct DrawableRef;
}

impl DrawableRef {
    pub fn present(&self) {
        unsafe { msg_send![self, present] }
    }
}
<commit_msg>Expose Drawable ID (#206)<commit_after>// Copyright 2016 GFX developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://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.

use cocoa_foundation::foundation::NSUInteger;

pub enum MTLDrawable {}

foreign_obj_type! {
    type CType = MTLDrawable;
    pub struct Drawable;
    pub struct DrawableRef;
}

impl DrawableRef {
    pub fn present(&self) {
        unsafe { msg_send![self, present] }
    }

    pub fn drawable_id(&self) -> NSUInteger {
        unsafe { msg_send![self, drawableID] }
    }
}
<|endoftext|>


<commit_before>
extern crate dsp;

use {Compressor, Detector, EvenGainFunction, PeakCompressor, RmsCompressor};
use self::dsp::{FromSample, Sample};

impl<D, F> Compressor<D, F> {

    /// Compresses the given `output` using a unique gain per channel.
    #[inline]
    pub fn compress_per_channel<S>(&mut self, output: &mut [S], n_frames: usize, n_channels: usize)
        where S: Sample + FromSample<f32>,
              D: Detector,
              F: EvenGainFunction,
              f32: FromSample<S>,
    {
        let mut idx = 0;
        for _ in 0..n_frames {
            for j in 0..n_channels {
                let sample = output[idx].to_sample::<f32>();
                let gain = self.next_gain_for_channel(j, sample);
                let compressed_sample = sample * gain;
                output[idx] = compressed_sample.to_sample::<S>();
                idx += 1;
            }
        }
    }

    /// Compresses the given `output` using an even gain across all channels.
    #[inline]
    pub fn compress<S>(&mut self, output: &mut [S], n_frames: usize, n_channels: usize)
        where S: Sample + FromSample<f32>,
              D: Detector,
              F: EvenGainFunction,
              f32: FromSample<S>,
    {
        let mut idx = 0;
        for _ in 0..n_frames {
            let gain = {
                let end_idx = idx + n_channels;
                let slice = &output[idx..end_idx];
                let samples = slice.iter().map(|s| s.to_sample::<f32>());
                self.next_gain(samples)
            };
            for _ in 0..n_channels {
                let sample = output[idx].to_sample::<f32>();
                let compressed_sample = gain * sample;
                output[idx] = compressed_sample.to_sample::<S>();
                idx += 1;
            }
        }
    }

}

impl<S, F> dsp::Node<S> for PeakCompressor<F>
    where S: Sample + FromSample<f32>,
          F: EvenGainFunction,
          f32: FromSample<S>,
{
    #[inline]
    fn audio_requested(&mut self, output: &mut [S], settings: dsp::Settings) {
        let sample_hz = settings.sample_hz as f64;
        let n_channels = settings.channels as usize;
        self.update_attack_to_sample_hz(sample_hz);
        self.update_release_to_sample_hz(sample_hz);
        self.set_channels(n_channels);
        self.compress(output, settings.frames as usize, n_channels);
    }
}

impl<S, F> dsp::Node<S> for RmsCompressor<F>
    where S: Sample + FromSample<f32>,
          F: EvenGainFunction,
          f32: FromSample<S>,
{
    #[inline]
    fn audio_requested(&mut self, output: &mut [S], settings: dsp::Settings) {
        let sample_hz = settings.sample_hz as f64;
        let n_channels = settings.channels as usize;
        self.update_attack_to_sample_hz(sample_hz);
        self.update_release_to_sample_hz(sample_hz);
        self.update_window_to_sample_hz(sample_hz);
        self.set_channels(n_channels);
        self.compress(output, settings.frames as usize, n_channels);
    }
}
<commit_msg>Use sample::Duplex instead of FromSample<commit_after>
extern crate dsp;

use {Compressor, Detector, EvenGainFunction, PeakCompressor, RmsCompressor};
use self::dsp::Sample;

impl<D, F> Compressor<D, F> {

    /// Compresses the given `output` using a unique gain per channel.
    #[inline]
    pub fn compress_per_channel<S>(&mut self, output: &mut [S], n_frames: usize, n_channels: usize)
        where S: Sample + dsp::sample::Duplex<f32>,
              D: Detector,
              F: EvenGainFunction,
    {
        let mut idx = 0;
        for _ in 0..n_frames {
            for j in 0..n_channels {
                let sample = output[idx].to_sample::<f32>();
                let gain = self.next_gain_for_channel(j, sample);
                let compressed_sample = sample * gain;
                output[idx] = compressed_sample.to_sample::<S>();
                idx += 1;
            }
        }
    }

    /// Compresses the given `output` using an even gain across all channels.
    #[inline]
    pub fn compress<S>(&mut self, output: &mut [S], n_frames: usize, n_channels: usize)
        where S: Sample + dsp::sample::Duplex<f32>,
              D: Detector,
              F: EvenGainFunction,
    {
        let mut idx = 0;
        for _ in 0..n_frames {
            let gain = {
                let end_idx = idx + n_channels;
                let slice = &output[idx..end_idx];
                let samples = slice.iter().map(|s| s.to_sample::<f32>());
                self.next_gain(samples)
            };
            for _ in 0..n_channels {
                let sample = output[idx].to_sample::<f32>();
                let compressed_sample = gain * sample;
                output[idx] = compressed_sample.to_sample::<S>();
                idx += 1;
            }
        }
    }

}

impl<S, F> dsp::Node<S> for PeakCompressor<F>
    where S: Sample + dsp::sample::Duplex<f32>,
          F: EvenGainFunction,
{
    #[inline]
    fn audio_requested(&mut self, output: &mut [S], settings: dsp::Settings) {
        let sample_hz = settings.sample_hz as f64;
        let n_channels = settings.channels as usize;
        self.update_attack_to_sample_hz(sample_hz);
        self.update_release_to_sample_hz(sample_hz);
        self.set_channels(n_channels);
        self.compress(output, settings.frames as usize, n_channels);
    }
}

impl<S, F> dsp::Node<S> for RmsCompressor<F>
    where S: Sample + dsp::sample::Duplex<f32>,
          F: EvenGainFunction,
{
    #[inline]
    fn audio_requested(&mut self, output: &mut [S], settings: dsp::Settings) {
        let sample_hz = settings.sample_hz as f64;
        let n_channels = settings.channels as usize;
        self.update_attack_to_sample_hz(sample_hz);
        self.update_release_to_sample_hz(sample_hz);
        self.update_window_to_sample_hz(sample_hz);
        self.set_channels(n_channels);
        self.compress(output, settings.frames as usize, n_channels);
    }
}
<|endoftext|>


<commit_before>extern crate hyper;

use hyper::{ client, Url };
use hyper::method::Method;
use hyper::header::{
  Accept, Authorization, ContentType,
  UserAgent, qitem
};
use mime::{ Attr, Mime, Value };
use mime::TopLevel::Application;
use mime::SubLevel::Json;
use std::io::prelude::*;
use std::io::Result;
use std::ops::DerefMut;

#[doc(hidden)]
pub struct Body<'a> {
 read: &'a mut Box<&'a mut Read>,
 size: u64
}

impl<'a> Body<'a> {
  /// Create a new body instance
  pub fn new(read: &'a mut Box<&'a mut Read>, size: u64) -> Body<'a> {
    Body { read: read, size: size }
  }
}

#[doc(hidden)]
pub trait Transport {
  fn request(&mut self, method: Method, path: String, body: Option<Body>, token: Option<String>) -> Result<String>;
}

impl Transport for (hyper::Client, String) {
  fn request(&mut self, method: Method, path: String, body: Option<Body>, token: Option<String>) -> Result<String> {
    let uri = Url::parse(&format!("{}/api/v1{}", self.1, path)).ok().expect("invalid url");
    let content_type: Mime = Mime(Application, Json, vec![(Attr::Charset, Value::Utf8)]);
    let bound = self.0.request(method, uri)
      .header(UserAgent("cargopants/0.1.0".to_string()))
      .header(Accept(vec![qitem(content_type.clone())]))
      .header(ContentType(content_type));
    let authenticated = match token.clone() {
      Some(auth) => bound.header(Authorization(auth)),
               _ => bound
    };
    let embodied = match body {
      Some(Body { read: r, size: l }) => {
        let reader: &mut Read  = *r.deref_mut();
        authenticated.body(client::Body::SizedBody(reader, l))
      },
      _  => authenticated
    };
    let mut res = match embodied.send() {
      Ok(r)    => r,
      Err(err) => panic!("failed request: {:?}", err)
    };
    let mut body = String::new();
    res.read_to_string(&mut body).map(|_| body)
  }
}
<commit_msg>import fewer names<commit_after>extern crate hyper;

use hyper::{ client, Url };
use hyper::method::Method;
use hyper::header::{
  Accept, Authorization, ContentType,
  UserAgent, qitem
};
use mime::{ Attr, Mime, Value };
use mime::TopLevel::Application;
use mime::SubLevel::Json;
use std::io::{ Read, Result };
use std::ops::DerefMut;

#[doc(hidden)]
pub struct Body<'a> {
 read: &'a mut Box<&'a mut Read>,
 size: u64
}

impl<'a> Body<'a> {
  /// Create a new body instance
  pub fn new(read: &'a mut Box<&'a mut Read>, size: u64) -> Body<'a> {
    Body { read: read, size: size }
  }
}

#[doc(hidden)]
pub trait Transport {
  fn request(&mut self, method: Method, path: String, body: Option<Body>, token: Option<String>) -> Result<String>;
}

impl Transport for (hyper::Client, String) {
  fn request(&mut self, method: Method, path: String, body: Option<Body>, token: Option<String>) -> Result<String> {
    let uri = Url::parse(&format!("{}/api/v1{}", self.1, path)).ok().expect("invalid url");
    let content_type: Mime = Mime(Application, Json, vec![(Attr::Charset, Value::Utf8)]);
    let bound = self.0.request(method, uri)
      .header(UserAgent("cargopants/0.1.0".to_string()))
      .header(Accept(vec![qitem(content_type.clone())]))
      .header(ContentType(content_type));
    let authenticated = match token.clone() {
      Some(auth) => bound.header(Authorization(auth)),
               _ => bound
    };
    let embodied = match body {
      Some(Body { read: r, size: l }) => {
        let reader: &mut Read  = *r.deref_mut();
        authenticated.body(client::Body::SizedBody(reader, l))
      },
      _  => authenticated
    };
    let mut res = match embodied.send() {
      Ok(r)    => r,
      Err(err) => panic!("failed request: {:?}", err)
    };
    let mut body = String::new();
    res.read_to_string(&mut body).map(|_| body)
  }
}
<|endoftext|>


<commit_before>use std::rc::Rc;

use std::cmp::{max, min, Ordering};
use interpreter::InterpretResult;
use variable::Variable;

/// Handles the dispatching of named functions using an array of arguments.
pub trait FnDispatcher {
    /// Dispatches and interprets a function by name.
    fn call(&self, fn_name: &str, args: &Vec<Rc<Variable>>) -> InterpretResult;
}

/// Built-in function implementations.
#[derive(Clone)]
pub struct BuiltinFunctions;

impl BuiltinFunctions {
    /// Creates a new Builtins function dispatcher.
    pub fn new() -> BuiltinFunctions {
        BuiltinFunctions
    }
}

/// Validates the arity of a function.
#[inline]
fn arity(fn_name: &str, arity: usize, args: &Vec<Rc<Variable>>) -> Result<(), String> {
    if args.len() != arity {
        Err(format!("{} arity error: expected {} args, found {}", fn_name, arity, args.len()))
    } else {
        Ok(())
    }
}

fn homogeneous_array(valid_types: Vec<&str>, array: &Vec<Rc<Variable>>) -> bool {
    let matched_type = array[0].get_type();
    if !valid_types.contains(&matched_type) {
        return false;
    }
    for a in array {
        if a.get_type() != matched_type {
            return false;
        }
    }
    return true;
}

/// Macro used to format a type error.
macro_rules! type_err {
    ($f:expr, $position:expr, $expected:expr, $given:expr) => {
        format!("{} expects arg {} to be {}. Given {:?}", $f, $position, $expected, $given)
    }
}

/// Macro used to variadically validate validate Variable and argument arity.
macro_rules! validate {
    // Validate positional arguments only.
    ($name:expr, $args:expr, $($x:expr),*) => (
        {
            let validators: Vec<Box<Fn(&Rc<Variable>) -> Result<(), String>>> = vec![$($x), *];
            try!(arity($name, validators.len(), $args));
            for (k, v) in $args.iter().enumerate() {
                if let Err(e) = validators[k](v) {
                    return Err(format!("{} error at argument {}: {}", $name, k, e));
                }
            }
        }
    );
    // Validate positional arguments with a variadic validator.
    ($name:expr, $args:expr, $($x:expr),* ...$variadic:expr ) => (
        {
            let validators: Vec<Box<Fn(&Rc<Variable>) -> Result<(), String>>> = vec![$($x), *];
            if $args.len() < validators.len() {
                return Err(format!("{} arity error: expected at least {} args, found {}",
                                   $name, validators.len(), $args.len()));
            }
            for (k, v) in $args.iter().enumerate() {
                try!((match validators.get(k) {
                    Some(validator) => validator(v),
                    None => $variadic(v)
                }).or_else(|e| Err(format!("{} error at argument {}: {}", $name, k, e))));
            }
        }
    );
}

macro_rules! homogeneous {
    ($($x:ident)|*) => [
        Box::new(move |arg: &Rc<Variable>| -> Result<(), String> {
            let valid_types = vec![$(stringify!($x)), *];
            match **arg {
                Variable::Array(ref array) if array.is_empty() => Ok(()),
                Variable::Array(ref array) if homogeneous_array(valid_types, array) => Ok(()),
                _ => Err(format!("type array[{}]", valid_types.join("|")))
            }
        })
    ];
}

/// Creates a closure used to validate the type of the given variable.
macro_rules! jptype {
    // Validate a type against a single acceptable type.
    ($type_name:ident) => [
        Box::new(move |arg: &Rc<Variable>| -> Result<(), String> {
            let arg_type = arg.get_type();
            if arg_type != "any" && arg_type != stringify!($type_name) {
                Err(format!("type {}", stringify!($type_name)))
            } else {
                Ok(())
            }
        })
    ];
    // Validate a type against multiple acceptable types
    ($($x:ident)|*) => [
        {
            Box::new(move |arg: &Rc<Variable>| -> Result<(), String> {
                let valid_types = vec![$(stringify!($x)), *];
                let arg_type = arg.get_type();
                if arg_type != "any" && !valid_types.contains(&arg_type) {
                    Err(format!("type {}", valid_types.join("|")))
                } else {
                    Ok(())
                }
            })
        }
    ];
}

/// Macro used to implement min and max functions.
macro_rules! min_max {
    ($operator:ident, $args:expr) => (
        match *$args[0] {
            Variable::Array(ref array) if array.is_empty() => Ok(Rc::new(Variable::Null)),
            Variable::Array(ref array) => {
                if array[0].is_string() {
                    Ok(Rc::new(Variable::String(array.iter().fold(
                        array[0].as_string().unwrap().clone(),
                        |acc, item| $operator(acc, item.as_string().unwrap().clone())))))
                } else {
                    Ok(Rc::new(Variable::F64(array.iter().fold(
                        array[0].as_f64().unwrap(),
                        |acc, item| acc.$operator(item.as_f64().unwrap())))))
                }
            },
            _ => panic!() // never encountered due to previous validation
        }
    )
}

impl FnDispatcher for BuiltinFunctions {
    fn call(&self, fn_name: &str, args: &Vec<Rc<Variable>>) -> InterpretResult {
        match fn_name {
            "abs" => {
                validate!("abs", args, jptype![number]);
                Ok(Rc::new(Variable::F64(args[0].as_f64().unwrap().abs())))
            },
            "avg" => {
                try!(arity("avg", 1, args));
                args[0].as_array()
                    .and_then(|array| {
                        let mut total = 0f64;
                        for value in array {
                            match **value {
                                Variable::I64(n) => total += n as f64,
                                Variable::U64(n) => total += n as f64,
                                Variable::F64(n) => total += n,
                                _ => return None
                            }
                        }
                        Some(Rc::new(Variable::F64(total / array.len() as f64)))
                    })
                    .ok_or(type_err!("avg", 0, "array[number]", args[0]))
            },
            "ceil" => {
                validate!("ceil", args, jptype![number]);
                Ok(Rc::new(Variable::F64(args[0].as_f64().unwrap().ceil())))
            },
            "contains" => {
                validate!("contains", args, jptype![array|string], jptype![any]);
                match *args[0] {
                    Variable::Array(ref a) => Ok(Rc::new(Variable::Boolean(a.contains(&args[1])))),
                    Variable::String(ref subj) => {
                        match args[1].as_string() {
                            Some(search) => Ok(Rc::new(Variable::Boolean(subj.contains(search)))),
                            None => Err(format!("contains found {:?} for string search arg",
                                                args[1]))
                        }
                    },
                    _ => panic!() // never encountered due to previous validation
                }
            },
            "ends_with" => {
                validate!("ends_with", args, jptype![string], jptype![string]);
                let subject = args[0].as_string().unwrap();
                let search = args[1].as_string().unwrap();
                Ok(Rc::new(Variable::Boolean(subject.ends_with(search))))
            },
            "floor" => {
                validate!("floor", args, jptype![number]);
                Ok(Rc::new(Variable::F64(args[0].as_f64().unwrap().floor())))
            },
            "keys" => {
                validate!("keys", args, jptype![object]);
                Ok(Rc::new(args[0].object_keys_to_array().unwrap()))
            },
            "length" => {
                validate!("length", args, jptype![array|object|string]);
                match *args[0] {
                    Variable::Array(ref a) => Ok(Rc::new(Variable::U64(a.len() as u64))),
                    Variable::Object(ref m) => Ok(Rc::new(Variable::U64(m.len() as u64))),
                    Variable::String(ref s) => Ok(Rc::new(Variable::U64(s.len() as u64))),
                    _ => panic!() // never encountered due to previous validation
                }
            },
            "max" => {
                validate!("max", args, homogeneous![string|number]);
                min_max!(max, args)
            },
            "min" => {
                validate!("min", args, homogeneous![string|number]);
                min_max!(min, args)
            },
            "not_null" => {
                validate!("not_null", args, jptype![any] ...jptype![any]);
                for arg in args {
                    if **arg != Variable::Null {
                        return Ok(arg.clone());
                    }
                }
                Ok(Rc::new(Variable::Null))
            },
            "sort" => {
                validate!("sort", args, homogeneous![string|number]);
                let mut values: Vec<Rc<Variable>> = args[0].as_array().unwrap().clone();
                if values[0].is_string() {
                    values.sort_by(|a, b| a.as_string().unwrap().cmp(b.as_string().unwrap()));
                } else {
                    values.sort_by(|a, b| a.as_f64().unwrap()
                        .partial_cmp(&b.as_f64().unwrap())
                        .unwrap_or(Ordering::Equal));
                }
                Ok(Rc::new(Variable::Array(values)))
            },
            "starts_with" => {
                validate!("starts_with", args, jptype![string], jptype![string]);
                let subject = args[0].as_string().unwrap();
                let search = args[1].as_string().unwrap();
                Ok(Rc::new(Variable::Boolean(subject.starts_with(search))))
            },
            "sum" => {
                validate!("sum", args, homogeneous![number]);
                let array = args[0].as_array().unwrap();
                Ok(Rc::new(Variable::F64(array.iter().fold(
                    array[0].as_f64().unwrap().clone(),
                    |acc, item| acc.max(item.as_f64().unwrap())))))
            },
            "to_array" => {
                try!(arity("to_array", 1, args));
                match *args[0] {
                    Variable::Array(_) => Ok(args[0].clone()),
                    _ => Ok(Rc::new(Variable::Array(vec![args[0].clone()])))
                }
            },
            "to_number" => {
                try!(arity("to_number", 1, args));
                match *args[0] {
                    Variable::I64(_) | Variable::F64(_) | Variable::U64(_) => Ok(args[0].clone()),
                    Variable::String(ref s) => {
                        match s.parse::<f64>() {
                            Ok(f)  => Ok(Rc::new(Variable::F64(f))),
                            Err(_) => Ok(Rc::new(Variable::Null))
                        }
                    },
                    _ => Ok(Rc::new(Variable::Null))
                }
            },
            "type" => {
                try!(arity("type", 1, args));
                Ok(Rc::new(Variable::String(args[0].get_type().to_string())))
            },
            "values" => {
                validate!("values", args, jptype![object]);
                Ok(Rc::new(args[0].object_values_to_array().unwrap()))
            },
            _ => Err(format!("Unknown function: {}", fn_name))
        }
    }
}
<commit_msg>Adding the join function<commit_after>use std::rc::Rc;

use std::cmp::{max, min, Ordering};
use interpreter::InterpretResult;
use variable::Variable;

/// Handles the dispatching of named functions using an array of arguments.
pub trait FnDispatcher {
    /// Dispatches and interprets a function by name.
    fn call(&self, fn_name: &str, args: &Vec<Rc<Variable>>) -> InterpretResult;
}

/// Built-in function implementations.
#[derive(Clone)]
pub struct BuiltinFunctions;

impl BuiltinFunctions {
    /// Creates a new Builtins function dispatcher.
    pub fn new() -> BuiltinFunctions {
        BuiltinFunctions
    }
}

/// Validates the arity of a function.
#[inline]
fn arity(fn_name: &str, arity: usize, args: &Vec<Rc<Variable>>) -> Result<(), String> {
    if args.len() != arity {
        Err(format!("{} arity error: expected {} args, found {}", fn_name, arity, args.len()))
    } else {
        Ok(())
    }
}

fn homogeneous_array(valid_types: Vec<&str>, array: &Vec<Rc<Variable>>) -> bool {
    let matched_type = array[0].get_type();
    if !valid_types.contains(&matched_type) {
        return false;
    }
    for a in array {
        if a.get_type() != matched_type {
            return false;
        }
    }
    return true;
}

/// Macro used to format a type error.
macro_rules! type_err {
    ($f:expr, $position:expr, $expected:expr, $given:expr) => {
        format!("{} expects arg {} to be {}. Given {:?}", $f, $position, $expected, $given)
    }
}

/// Macro used to variadically validate validate Variable and argument arity.
macro_rules! validate {
    // Validate positional arguments only.
    ($name:expr, $args:expr, $($x:expr),*) => (
        {
            let validators: Vec<Box<Fn(&Rc<Variable>) -> Result<(), String>>> = vec![$($x), *];
            try!(arity($name, validators.len(), $args));
            for (k, v) in $args.iter().enumerate() {
                if let Err(e) = validators[k](v) {
                    return Err(format!("{} error at argument {}: {}", $name, k, e));
                }
            }
        }
    );
    // Validate positional arguments with a variadic validator.
    ($name:expr, $args:expr, $($x:expr),* ...$variadic:expr ) => (
        {
            let validators: Vec<Box<Fn(&Rc<Variable>) -> Result<(), String>>> = vec![$($x), *];
            if $args.len() < validators.len() {
                return Err(format!("{} arity error: expected at least {} args, found {}",
                                   $name, validators.len(), $args.len()));
            }
            for (k, v) in $args.iter().enumerate() {
                try!((match validators.get(k) {
                    Some(validator) => validator(v),
                    None => $variadic(v)
                }).or_else(|e| Err(format!("{} error at argument {}: {}", $name, k, e))));
            }
        }
    );
}

macro_rules! homogeneous {
    ($($x:ident)|*) => [
        Box::new(move |arg: &Rc<Variable>| -> Result<(), String> {
            let valid_types = vec![$(stringify!($x)), *];
            match **arg {
                Variable::Array(ref array) if array.is_empty() => Ok(()),
                Variable::Array(ref array) if homogeneous_array(valid_types, array) => Ok(()),
                _ => Err(format!("type array[{}]", valid_types.join("|")))
            }
        })
    ];
}

/// Creates a closure used to validate the type of the given variable.
macro_rules! jptype {
    // Validate a type against a single acceptable type.
    ($type_name:ident) => [
        Box::new(move |arg: &Rc<Variable>| -> Result<(), String> {
            let arg_type = arg.get_type();
            if arg_type != "any" && arg_type != stringify!($type_name) {
                Err(format!("type {}", stringify!($type_name)))
            } else {
                Ok(())
            }
        })
    ];
    // Validate a type against multiple acceptable types
    ($($x:ident)|*) => [
        {
            Box::new(move |arg: &Rc<Variable>| -> Result<(), String> {
                let valid_types = vec![$(stringify!($x)), *];
                let arg_type = arg.get_type();
                if arg_type != "any" && !valid_types.contains(&arg_type) {
                    Err(format!("type {}", valid_types.join("|")))
                } else {
                    Ok(())
                }
            })
        }
    ];
}

/// Macro used to implement min and max functions.
macro_rules! min_max {
    ($operator:ident, $args:expr) => (
        match *$args[0] {
            Variable::Array(ref array) if array.is_empty() => Ok(Rc::new(Variable::Null)),
            Variable::Array(ref array) => {
                if array[0].is_string() {
                    Ok(Rc::new(Variable::String(array.iter().fold(
                        array[0].as_string().unwrap().clone(),
                        |acc, item| $operator(acc, item.as_string().unwrap().clone())))))
                } else {
                    Ok(Rc::new(Variable::F64(array.iter().fold(
                        array[0].as_f64().unwrap(),
                        |acc, item| acc.$operator(item.as_f64().unwrap())))))
                }
            },
            _ => panic!() // never encountered due to previous validation
        }
    )
}

impl FnDispatcher for BuiltinFunctions {
    fn call(&self, fn_name: &str, args: &Vec<Rc<Variable>>) -> InterpretResult {
        match fn_name {
            "abs" => {
                validate!("abs", args, jptype![number]);
                Ok(Rc::new(Variable::F64(args[0].as_f64().unwrap().abs())))
            },
            "avg" => {
                try!(arity("avg", 1, args));
                args[0].as_array()
                    .and_then(|array| {
                        let mut total = 0f64;
                        for value in array {
                            match **value {
                                Variable::I64(n) => total += n as f64,
                                Variable::U64(n) => total += n as f64,
                                Variable::F64(n) => total += n,
                                _ => return None
                            }
                        }
                        Some(Rc::new(Variable::F64(total / array.len() as f64)))
                    })
                    .ok_or(type_err!("avg", 0, "array[number]", args[0]))
            },
            "ceil" => {
                validate!("ceil", args, jptype![number]);
                Ok(Rc::new(Variable::F64(args[0].as_f64().unwrap().ceil())))
            },
            "contains" => {
                validate!("contains", args, jptype![array|string], jptype![any]);
                match *args[0] {
                    Variable::Array(ref a) => Ok(Rc::new(Variable::Boolean(a.contains(&args[1])))),
                    Variable::String(ref subj) => {
                        match args[1].as_string() {
                            Some(search) => Ok(Rc::new(Variable::Boolean(subj.contains(search)))),
                            None => Err(format!("contains found {:?} for string search arg",
                                                args[1]))
                        }
                    },
                    _ => panic!() // never encountered due to previous validation
                }
            },
            "ends_with" => {
                validate!("ends_with", args, jptype![string], jptype![string]);
                let subject = args[0].as_string().unwrap();
                let search = args[1].as_string().unwrap();
                Ok(Rc::new(Variable::Boolean(subject.ends_with(search))))
            },
            "floor" => {
                validate!("floor", args, jptype![number]);
                Ok(Rc::new(Variable::F64(args[0].as_f64().unwrap().floor())))
            },
            "join" => {
                validate!("join", args, jptype![string], homogeneous![string]);
                Ok(Rc::new(Variable::String(args.iter().skip(1)
                    .map(|ref v| v.as_string().unwrap().clone())
                    .collect::<Vec<String>>()
                    .join(args[0].as_string().unwrap()))))
            },
            "keys" => {
                validate!("keys", args, jptype![object]);
                Ok(Rc::new(args[0].object_keys_to_array().unwrap()))
            },
            "length" => {
                validate!("length", args, jptype![array|object|string]);
                match *args[0] {
                    Variable::Array(ref a) => Ok(Rc::new(Variable::U64(a.len() as u64))),
                    Variable::Object(ref m) => Ok(Rc::new(Variable::U64(m.len() as u64))),
                    Variable::String(ref s) => Ok(Rc::new(Variable::U64(s.len() as u64))),
                    _ => panic!() // never encountered due to previous validation
                }
            },
            "max" => {
                validate!("max", args, homogeneous![string|number]);
                min_max!(max, args)
            },
            "min" => {
                validate!("min", args, homogeneous![string|number]);
                min_max!(min, args)
            },
            "not_null" => {
                validate!("not_null", args, jptype![any] ...jptype![any]);
                for arg in args {
                    if **arg != Variable::Null {
                        return Ok(arg.clone());
                    }
                }
                Ok(Rc::new(Variable::Null))
            },
            "sort" => {
                validate!("sort", args, homogeneous![string|number]);
                let mut values: Vec<Rc<Variable>> = args[0].as_array().unwrap().clone();
                if values[0].is_string() {
                    values.sort_by(|a, b| a.as_string().unwrap().cmp(b.as_string().unwrap()));
                } else {
                    values.sort_by(|a, b| a.as_f64().unwrap()
                        .partial_cmp(&b.as_f64().unwrap())
                        .unwrap_or(Ordering::Equal));
                }
                Ok(Rc::new(Variable::Array(values)))
            },
            "starts_with" => {
                validate!("starts_with", args, jptype![string], jptype![string]);
                let subject = args[0].as_string().unwrap();
                let search = args[1].as_string().unwrap();
                Ok(Rc::new(Variable::Boolean(subject.starts_with(search))))
            },
            "sum" => {
                validate!("sum", args, homogeneous![number]);
                let array = args[0].as_array().unwrap();
                Ok(Rc::new(Variable::F64(array.iter().fold(
                    array[0].as_f64().unwrap().clone(),
                    |acc, item| acc.max(item.as_f64().unwrap())))))
            },
            "to_array" => {
                try!(arity("to_array", 1, args));
                match *args[0] {
                    Variable::Array(_) => Ok(args[0].clone()),
                    _ => Ok(Rc::new(Variable::Array(vec![args[0].clone()])))
                }
            },
            "to_number" => {
                try!(arity("to_number", 1, args));
                match *args[0] {
                    Variable::I64(_) | Variable::F64(_) | Variable::U64(_) => Ok(args[0].clone()),
                    Variable::String(ref s) => {
                        match s.parse::<f64>() {
                            Ok(f)  => Ok(Rc::new(Variable::F64(f))),
                            Err(_) => Ok(Rc::new(Variable::Null))
                        }
                    },
                    _ => Ok(Rc::new(Variable::Null))
                }
            },
            "type" => {
                try!(arity("type", 1, args));
                Ok(Rc::new(Variable::String(args[0].get_type().to_string())))
            },
            "values" => {
                validate!("values", args, jptype![object]);
                Ok(Rc::new(args[0].object_values_to_array().unwrap()))
            },
            _ => Err(format!("Unknown function: {}", fn_name))
        }
    }
}
<|endoftext|>


<commit_before>#[deriving(PartialEq, Show)]
pub enum Token {
    Comma,
    Number(f64),
    ParenClose,
    ParenOpen,
    Word(String),
}

pub fn tokenize(input: &str) -> Tokenizer {
    Tokenizer {text: String::from_str(input)}
}

fn is_whitespace(c: char) -> bool {
    match c {
        '\n' | '\r' | '\t' | ' ' => true,
        _                        => false,
    }
}

fn is_numberlike(c: char) -> bool {
    match c {
        c if c.is_numeric() => true,
        '.' | '-' | '+'     => true,
        _                   => false,
    }
}

pub struct Tokenizer {
    text: String
}

impl Iterator<Token> for Tokenizer {
    fn next(&mut self) -> Option<Token> {
        let popped_char = self.pop_char();
        if popped_char.is_none() {
            return None
        }
        let next_char = popped_char.unwrap();

        match next_char {
            '\0' => None,
            '(' => Some(Token::ParenOpen),
            ')' => Some(Token::ParenClose),
            ',' => Some(Token::Comma),
            c if is_whitespace(c) => self.next(),
            c if is_numberlike(c) => {
                let mut number = c.to_string() + self.read_until_whitespace().as_slice();
                number = number.trim_left_chars('+').to_string();
                match number.parse::<f64>() {
                    Some(parsed_num) => Some(Token::Number(parsed_num)),
                    None => panic!("Could not parse number: {}", number),
                }
            }
            c => {
                let word = c.to_string() + self.read_until_whitespace().as_slice();
                Some(Token::Word(word))
            }
        }
    }
}

impl Tokenizer {
    fn pop_char(&mut self) -> Option<char> {
        if self.text.is_empty() {
            None
        } else {
            self.text.remove(0)
        }
    }

    fn read_until_whitespace(&mut self) -> String {
        let popped_char = self.pop_char();
        if popped_char.is_none() {
            return "".to_string()
        }
        let next_char = popped_char.unwrap();

        match next_char {
            '\0' | '(' | ')' | ',' => {
                self.text.insert(0, next_char);
                "".to_string()
            }
            c if is_whitespace(c) => "".to_string(),
            _ => next_char.to_string() + self.read_until_whitespace().as_slice(),
        }
    }
}

#[test]
fn test_tokenizer_empty() {
    let test_str = "";
    let tokens: Vec<Token> = tokenize(test_str).collect();
    assert_eq!(tokens, vec![]);
}

#[test]
fn test_tokenizer_1word() {
    let test_str = "hello";
    let tokens: Vec<Token> = tokenize(test_str).collect();
    assert_eq!(tokens, vec![
        Token::Word("hello".to_string()),
    ]);
}

#[test]
fn test_tokenizer_2words() {
    let test_str = "hello world";
    let tokens: Vec<Token> = tokenize(test_str).collect();
    assert_eq!(tokens, vec![
        Token::Word("hello".to_string()),
        Token::Word("world".to_string()),
    ]);
}

#[test]
fn test_tokenizer_1number() {
    let test_str = "4.2";
    let tokens: Vec<Token> = tokenize(test_str).collect();
    assert_eq!(tokens, vec![
        Token::Number(4.2),
    ]);
}

#[test]
fn test_tokenizer_1number_plus() {
    let test_str = "+4.2";
    let tokens: Vec<Token> = tokenize(test_str).collect();
    assert_eq!(tokens, vec![
        Token::Number(4.2),
    ]);
}

#[test]
fn test_tokenizer_2numbers() {
    let test_str = ".4 -2";
    let tokens: Vec<Token> = tokenize(test_str).collect();
    assert_eq!(tokens, vec![
        Token::Number(0.4),
        Token::Number(-2.0),
    ]);
}

#[test]
fn test_tokenizer_point() {
    let test_str = "POINT (10 -20)";
    let tokens: Vec<Token> = tokenize(test_str).collect();
    assert_eq!(tokens, vec![
        Token::Word("POINT".to_string()),
        Token::ParenOpen,
        Token::Number(10.0),
        Token::Number(-20.0),
        Token::ParenClose,
    ]);
}
<commit_msg>Reflect changes to String.remove<commit_after>#[deriving(PartialEq, Show)]
pub enum Token {
    Comma,
    Number(f64),
    ParenClose,
    ParenOpen,
    Word(String),
}

pub fn tokenize(input: &str) -> Tokenizer {
    Tokenizer {text: String::from_str(input)}
}

fn is_whitespace(c: char) -> bool {
    match c {
        '\n' | '\r' | '\t' | ' ' => true,
        _                        => false,
    }
}

fn is_numberlike(c: char) -> bool {
    match c {
        c if c.is_numeric() => true,
        '.' | '-' | '+'     => true,
        _                   => false,
    }
}

pub struct Tokenizer {
    text: String
}

impl Iterator<Token> for Tokenizer {
    fn next(&mut self) -> Option<Token> {
        let popped_char = self.pop_char();
        if popped_char.is_none() {
            return None
        }
        let next_char = popped_char.unwrap();

        match next_char {
            '\0' => None,
            '(' => Some(Token::ParenOpen),
            ')' => Some(Token::ParenClose),
            ',' => Some(Token::Comma),
            c if is_whitespace(c) => self.next(),
            c if is_numberlike(c) => {
                let mut number = c.to_string() + self.read_until_whitespace().as_slice();
                number = number.trim_left_chars('+').to_string();
                match number.parse::<f64>() {
                    Some(parsed_num) => Some(Token::Number(parsed_num)),
                    None => panic!("Could not parse number: {}", number),
                }
            }
            c => {
                let word = c.to_string() + self.read_until_whitespace().as_slice();
                Some(Token::Word(word))
            }
        }
    }
}

impl Tokenizer {
    fn pop_char(&mut self) -> Option<char> {
        if self.text.is_empty() {
            None
        } else {
            Some(self.text.remove(0))
        }
    }

    fn read_until_whitespace(&mut self) -> String {
        let popped_char = self.pop_char();
        if popped_char.is_none() {
            return "".to_string()
        }
        let next_char = popped_char.unwrap();

        match next_char {
            '\0' | '(' | ')' | ',' => {
                self.text.insert(0, next_char);
                "".to_string()
            }
            c if is_whitespace(c) => "".to_string(),
            _ => next_char.to_string() + self.read_until_whitespace().as_slice(),
        }
    }
}

#[test]
fn test_tokenizer_empty() {
    let test_str = "";
    let tokens: Vec<Token> = tokenize(test_str).collect();
    assert_eq!(tokens, vec![]);
}

#[test]
fn test_tokenizer_1word() {
    let test_str = "hello";
    let tokens: Vec<Token> = tokenize(test_str).collect();
    assert_eq!(tokens, vec![
        Token::Word("hello".to_string()),
    ]);
}

#[test]
fn test_tokenizer_2words() {
    let test_str = "hello world";
    let tokens: Vec<Token> = tokenize(test_str).collect();
    assert_eq!(tokens, vec![
        Token::Word("hello".to_string()),
        Token::Word("world".to_string()),
    ]);
}

#[test]
fn test_tokenizer_1number() {
    let test_str = "4.2";
    let tokens: Vec<Token> = tokenize(test_str).collect();
    assert_eq!(tokens, vec![
        Token::Number(4.2),
    ]);
}

#[test]
fn test_tokenizer_1number_plus() {
    let test_str = "+4.2";
    let tokens: Vec<Token> = tokenize(test_str).collect();
    assert_eq!(tokens, vec![
        Token::Number(4.2),
    ]);
}

#[test]
fn test_tokenizer_2numbers() {
    let test_str = ".4 -2";
    let tokens: Vec<Token> = tokenize(test_str).collect();
    assert_eq!(tokens, vec![
        Token::Number(0.4),
        Token::Number(-2.0),
    ]);
}

#[test]
fn test_tokenizer_point() {
    let test_str = "POINT (10 -20)";
    let tokens: Vec<Token> = tokenize(test_str).collect();
    assert_eq!(tokens, vec![
        Token::Word("POINT".to_string()),
        Token::ParenOpen,
        Token::Number(10.0),
        Token::Number(-20.0),
        Token::ParenClose,
    ]);
}
<|endoftext|>