<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

#![forbid(unsafe_code)]

use crate::{
    boogie_wrapper::BoogieWrapper,
    bytecode_translator::BoogieTranslator,
    cli::{Options, INLINE_PRELUDE},
    prelude_template_helpers::StratificationHelper,
};
use anyhow::anyhow;
use codespan_reporting::term::termcolor::{ColorChoice, StandardStream, WriteColor};
use docgen::docgen::Docgen;
use handlebars::Handlebars;
#[allow(unused_imports)]
use log::{debug, info, warn};
use move_lang::find_move_filenames;
use once_cell::sync::Lazy;
use regex::Regex;
use spec_lang::{code_writer::CodeWriter, emit, emitln, env::GlobalEnv, run_spec_lang_compiler};
use stackless_bytecode_generator::{
    borrow_analysis::BorrowAnalysisProcessor,
    eliminate_imm_refs::EliminateImmRefsProcessor,
    eliminate_mut_refs::EliminateMutRefsProcessor,
    function_target_pipeline::{FunctionTargetPipeline, FunctionTargetsHolder},
    livevar_analysis::LiveVarAnalysisProcessor,
    packref_analysis::PackrefAnalysisProcessor,
    writeback_analysis::WritebackAnalysisProcessor,
};
use std::{
    collections::{BTreeMap, BTreeSet},
    fs,
    fs::File,
    io::Read,
    path::{Path, PathBuf},
    time::Instant,
};

mod boogie_helpers;
mod boogie_wrapper;
mod bytecode_translator;
pub mod cli;
mod prelude_template_helpers;
mod spec_translator;

// =================================================================================================
// Entry Point

/// Content of the default prelude.
const DEFAULT_PRELUDE: &[u8] = include_bytes!("prelude.bpl");

pub fn run_move_prover<W: WriteColor>(
    error_writer: &mut W,
    options: Options,
) -> anyhow::Result<()> {
    let now = Instant::now();
    let sources = find_move_filenames(&options.move_sources)?;
    let deps = calculate_deps(&sources, &find_move_filenames(&options.move_deps)?)?;
    let address = Some(options.account_address.as_ref());
    debug!("parsing and checking sources");
    let mut env: GlobalEnv = run_spec_lang_compiler(sources, deps, address)?;
    if env.has_errors() {
        env.report_errors(error_writer);
        return Err(anyhow!("exiting with checking errors"));
    }
    if env.has_warnings() {
        env.report_warnings(error_writer);
    }

    // Until this point, prover and docgen have same code. Here we part ways.
    if options.run_docgen {
        return run_docgen(&env, &options, now);
    }
    let targets = create_and_process_bytecode(&options, &env);
    if env.has_errors() {
        env.report_errors(error_writer);
        return Err(anyhow!("exiting with transformation errors"));
    }
    let writer = CodeWriter::new(env.internal_loc());
    add_prelude(&options, &writer)?;
    let mut translator = BoogieTranslator::new(&env, &options, &targets, &writer);
    translator.translate();
    if env.has_errors() {
        env.report_errors(error_writer);
        return Err(anyhow!("exiting with boogie generation errors"));
    }
    debug!("writing boogie to `{}`", &options.output_path);
    writer.process_result(|result| fs::write(&options.output_path, result))?;
    let translator_elapsed = now.elapsed();
    if !options.prover.generate_only {
        let boogie_file_id =
            writer.process_result(|result| env.add_source(&options.output_path, result, false));
        let boogie = BoogieWrapper {
            env: &env,
            targets: &targets,
            writer: &writer,
            options: &options,
            boogie_file_id,
        };
        boogie.call_boogie_and_verify_output(options.backend.bench_repeat, &options.output_path)?;
        let boogie_elapsed = now.elapsed();
        if options.backend.bench_repeat <= 1 {
            info!(
                "{:.3}s translator, {:.3}s solver",
                translator_elapsed.as_secs_f64(),
                (boogie_elapsed - translator_elapsed).as_secs_f64()
            );
        } else {
            info!(
                "{:.3}s translator, {:.3}s solver (average over {} solver runs)",
                translator_elapsed.as_secs_f64(),
                (boogie_elapsed - translator_elapsed).as_secs_f64()
                    / (options.backend.bench_repeat as f64),
                options.backend.bench_repeat
            );
        }

        if env.has_errors() {
            env.report_errors(error_writer);
            return Err(anyhow!("exiting with boogie verification errors"));
        }
    }
    Ok(())
}

pub fn run_move_prover_errors_to_stderr(options: Options) -> anyhow::Result<()> {
    let mut error_writer = StandardStream::stderr(ColorChoice::Auto);
    run_move_prover(&mut error_writer, options)
}

fn run_docgen(env: &GlobalEnv, options: &Options, now: Instant) -> anyhow::Result<()> {
    let mut generator = Docgen::new(env, &options.docgen);
    let checking_elapsed = now.elapsed();
    info!("generating documentation");
    generator.gen();
    for (file, content) in generator.into_result() {
        let path = PathBuf::from(&file);
        fs::create_dir_all(path.parent().unwrap())?;
        fs::write(path.as_path(), content)?;
    }
    let generating_elapsed = now.elapsed();
    info!(
        "{:.3}s checking, {:.3}s generating",
        checking_elapsed.as_secs_f64(),
        (generating_elapsed - checking_elapsed).as_secs_f64()
    );
    Ok(())
}

/// Adds the prelude to the generated output.
fn add_prelude(options: &Options, writer: &CodeWriter) -> anyhow::Result<()> {
    emit!(writer, "\n// ** prelude from {}\n\n", &options.prelude_path);
    let content = if options.prelude_path == INLINE_PRELUDE {
        debug!("using inline prelude");
        String::from_utf8_lossy(DEFAULT_PRELUDE).to_string()
    } else {
        debug!("using prelude at {}", &options.prelude_path);
        fs::read_to_string(&options.prelude_path)?
    };
    let mut handlebars = Handlebars::new();
    handlebars.register_helper(
        "stratified",
        Box::new(StratificationHelper::new(
            options.backend.stratification_depth,
        )),
    );
    let expanded_content = handlebars.render_template(&content, &options)?;
    emitln!(writer, &expanded_content);
    Ok(())
}

/// Create bytecode and process it.
fn create_and_process_bytecode(options: &Options, env: &GlobalEnv) -> FunctionTargetsHolder {
    let mut targets = FunctionTargetsHolder::default();

    // Add function targets for all functions in the environment.
    for module_env in env.get_modules() {
        for func_env in module_env.get_functions() {
            targets.add_target(&func_env)
        }
    }

    // Create processing pipeline and run it.
    let pipeline = create_bytecode_processing_pipeline(options);
    pipeline.run(env, &mut targets);

    targets
}

/// Function to create the transformation pipeline.
fn create_bytecode_processing_pipeline(_options: &Options) -> FunctionTargetPipeline {
    let mut res = FunctionTargetPipeline::default();

    // Add processors in order they are executed.
    res.add_processor(EliminateImmRefsProcessor::new());
    res.add_processor(LiveVarAnalysisProcessor::new());
    res.add_processor(BorrowAnalysisProcessor::new());
    res.add_processor(WritebackAnalysisProcessor::new());
    res.add_processor(PackrefAnalysisProcessor::new());
    res.add_processor(EliminateMutRefsProcessor::new());

    res
}

/// Calculates transitive dependencies of the given move sources.
fn calculate_deps(sources: &[String], input_deps: &[String]) -> anyhow::Result<Vec<String>> {
    let file_map = calculate_file_map(input_deps)?;
    let mut deps = vec![];
    let mut visited = BTreeSet::new();
    for src in sources.iter() {
        calculate_deps_recursively(Path::new(src), &file_map, &mut visited, &mut deps)?;
    }
    Ok(deps)
}

/// Recursively calculate dependencies.
fn calculate_deps_recursively(
    path: &Path,
    file_map: &BTreeMap<String, PathBuf>,
    visited: &mut BTreeSet<String>,
    deps: &mut Vec<String>,
) -> anyhow::Result<()> {
    static REX: Lazy<Regex> =
        Lazy::new(|| Regex::new(r"(?m)0x[0-9abcdefABCDEF]+::\s*(\w+)").unwrap());
    if !visited.insert(path.to_string_lossy().to_string()) {
        return Ok(());
    }
    for dep in extract_matches(path, &*REX)? {
        if let Some(dep_path) = file_map.get(&dep) {
            let dep_str = dep_path.to_string_lossy().to_string();
            if !deps.contains(&dep_str) {
                deps.push(dep_str);
                calculate_deps_recursively(dep_path.as_path(), file_map, visited, deps)?;
            }
        }
    }
    Ok(())
}

/// Calculate a map of module names to files which define those modules.
fn calculate_file_map(deps: &[String]) -> anyhow::Result<BTreeMap<String, PathBuf>> {
    static REX: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m)module\s+(\w+)\s*\{").unwrap());
    let mut module_to_file = BTreeMap::new();
    for dep in deps {
        let dep_path = PathBuf::from(dep);
        for module in extract_matches(dep_path.as_path(), &*REX)? {
            module_to_file.insert(module, dep_path.clone());
        }
    }
    Ok(module_to_file)
}

/// Extracts matches out of some text file. `rex` must be a regular expression with one anonymous
/// group.
fn extract_matches(path: &Path, rex: &Regex) -> anyhow::Result<Vec<String>> {
    let mut content = String::new();
    let mut file = File::open(path)?;
    file.read_to_string(&mut content)?;
    let mut at = 0;
    let mut res = vec![];
    while let Some(cap) = rex.captures(&content[at..]) {
        res.push(cap.get(1).unwrap().as_str().to_string());
        at += cap.get(0).unwrap().end();
    }
    Ok(res)
}
<commit_msg>[move-prover] Fixing a bug in dependency analysis.<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

#![forbid(unsafe_code)]

use crate::{
    boogie_wrapper::BoogieWrapper,
    bytecode_translator::BoogieTranslator,
    cli::{Options, INLINE_PRELUDE},
    prelude_template_helpers::StratificationHelper,
};
use anyhow::anyhow;
use codespan_reporting::term::termcolor::{ColorChoice, StandardStream, WriteColor};
use docgen::docgen::Docgen;
use handlebars::Handlebars;
use itertools::Itertools;
#[allow(unused_imports)]
use log::{debug, info, warn};
use move_lang::find_move_filenames;
use once_cell::sync::Lazy;
use regex::Regex;
use spec_lang::{code_writer::CodeWriter, emit, emitln, env::GlobalEnv, run_spec_lang_compiler};
use stackless_bytecode_generator::{
    borrow_analysis::BorrowAnalysisProcessor,
    eliminate_imm_refs::EliminateImmRefsProcessor,
    eliminate_mut_refs::EliminateMutRefsProcessor,
    function_target_pipeline::{FunctionTargetPipeline, FunctionTargetsHolder},
    livevar_analysis::LiveVarAnalysisProcessor,
    packref_analysis::PackrefAnalysisProcessor,
    writeback_analysis::WritebackAnalysisProcessor,
};
use std::{
    collections::{BTreeMap, BTreeSet},
    fs,
    fs::File,
    io::Read,
    path::{Path, PathBuf},
    time::Instant,
};

mod boogie_helpers;
mod boogie_wrapper;
mod bytecode_translator;
pub mod cli;
mod prelude_template_helpers;
mod spec_translator;

// =================================================================================================
// Entry Point

/// Content of the default prelude.
const DEFAULT_PRELUDE: &[u8] = include_bytes!("prelude.bpl");

pub fn run_move_prover<W: WriteColor>(
    error_writer: &mut W,
    options: Options,
) -> anyhow::Result<()> {
    let now = Instant::now();
    let sources = find_move_filenames(&options.move_sources)?;
    let deps = calculate_deps(&sources, &find_move_filenames(&options.move_deps)?)?;
    let address = Some(options.account_address.as_ref());
    debug!("parsing and checking sources");
    let mut env: GlobalEnv = run_spec_lang_compiler(sources, deps, address)?;
    if env.has_errors() {
        env.report_errors(error_writer);
        return Err(anyhow!("exiting with checking errors"));
    }
    if env.has_warnings() {
        env.report_warnings(error_writer);
    }

    // Until this point, prover and docgen have same code. Here we part ways.
    if options.run_docgen {
        return run_docgen(&env, &options, now);
    }
    let targets = create_and_process_bytecode(&options, &env);
    if env.has_errors() {
        env.report_errors(error_writer);
        return Err(anyhow!("exiting with transformation errors"));
    }
    let writer = CodeWriter::new(env.internal_loc());
    add_prelude(&options, &writer)?;
    let mut translator = BoogieTranslator::new(&env, &options, &targets, &writer);
    translator.translate();
    if env.has_errors() {
        env.report_errors(error_writer);
        return Err(anyhow!("exiting with boogie generation errors"));
    }
    debug!("writing boogie to `{}`", &options.output_path);
    writer.process_result(|result| fs::write(&options.output_path, result))?;
    let translator_elapsed = now.elapsed();
    if !options.prover.generate_only {
        let boogie_file_id =
            writer.process_result(|result| env.add_source(&options.output_path, result, false));
        let boogie = BoogieWrapper {
            env: &env,
            targets: &targets,
            writer: &writer,
            options: &options,
            boogie_file_id,
        };
        boogie.call_boogie_and_verify_output(options.backend.bench_repeat, &options.output_path)?;
        let boogie_elapsed = now.elapsed();
        if options.backend.bench_repeat <= 1 {
            info!(
                "{:.3}s translator, {:.3}s solver",
                translator_elapsed.as_secs_f64(),
                (boogie_elapsed - translator_elapsed).as_secs_f64()
            );
        } else {
            info!(
                "{:.3}s translator, {:.3}s solver (average over {} solver runs)",
                translator_elapsed.as_secs_f64(),
                (boogie_elapsed - translator_elapsed).as_secs_f64()
                    / (options.backend.bench_repeat as f64),
                options.backend.bench_repeat
            );
        }

        if env.has_errors() {
            env.report_errors(error_writer);
            return Err(anyhow!("exiting with boogie verification errors"));
        }
    }
    Ok(())
}

pub fn run_move_prover_errors_to_stderr(options: Options) -> anyhow::Result<()> {
    let mut error_writer = StandardStream::stderr(ColorChoice::Auto);
    run_move_prover(&mut error_writer, options)
}

fn run_docgen(env: &GlobalEnv, options: &Options, now: Instant) -> anyhow::Result<()> {
    let mut generator = Docgen::new(env, &options.docgen);
    let checking_elapsed = now.elapsed();
    info!("generating documentation");
    generator.gen();
    for (file, content) in generator.into_result() {
        let path = PathBuf::from(&file);
        fs::create_dir_all(path.parent().unwrap())?;
        fs::write(path.as_path(), content)?;
    }
    let generating_elapsed = now.elapsed();
    info!(
        "{:.3}s checking, {:.3}s generating",
        checking_elapsed.as_secs_f64(),
        (generating_elapsed - checking_elapsed).as_secs_f64()
    );
    Ok(())
}

/// Adds the prelude to the generated output.
fn add_prelude(options: &Options, writer: &CodeWriter) -> anyhow::Result<()> {
    emit!(writer, "\n// ** prelude from {}\n\n", &options.prelude_path);
    let content = if options.prelude_path == INLINE_PRELUDE {
        debug!("using inline prelude");
        String::from_utf8_lossy(DEFAULT_PRELUDE).to_string()
    } else {
        debug!("using prelude at {}", &options.prelude_path);
        fs::read_to_string(&options.prelude_path)?
    };
    let mut handlebars = Handlebars::new();
    handlebars.register_helper(
        "stratified",
        Box::new(StratificationHelper::new(
            options.backend.stratification_depth,
        )),
    );
    let expanded_content = handlebars.render_template(&content, &options)?;
    emitln!(writer, &expanded_content);
    Ok(())
}

/// Create bytecode and process it.
fn create_and_process_bytecode(options: &Options, env: &GlobalEnv) -> FunctionTargetsHolder {
    let mut targets = FunctionTargetsHolder::default();

    // Add function targets for all functions in the environment.
    for module_env in env.get_modules() {
        for func_env in module_env.get_functions() {
            targets.add_target(&func_env)
        }
    }

    // Create processing pipeline and run it.
    let pipeline = create_bytecode_processing_pipeline(options);
    pipeline.run(env, &mut targets);

    targets
}

/// Function to create the transformation pipeline.
fn create_bytecode_processing_pipeline(_options: &Options) -> FunctionTargetPipeline {
    let mut res = FunctionTargetPipeline::default();

    // Add processors in order they are executed.
    res.add_processor(EliminateImmRefsProcessor::new());
    res.add_processor(LiveVarAnalysisProcessor::new());
    res.add_processor(BorrowAnalysisProcessor::new());
    res.add_processor(WritebackAnalysisProcessor::new());
    res.add_processor(PackrefAnalysisProcessor::new());
    res.add_processor(EliminateMutRefsProcessor::new());

    res
}

/// Calculates transitive dependencies of the given move sources.
fn calculate_deps(sources: &[String], input_deps: &[String]) -> anyhow::Result<Vec<String>> {
    let file_map = calculate_file_map(input_deps)?;
    let mut deps = vec![];
    let mut visited = BTreeSet::new();
    for src in sources.iter() {
        calculate_deps_recursively(Path::new(src), &file_map, &mut visited, &mut deps)?;
    }
    // Remove input sources from deps. They can end here because our dep analysis is an
    // over-approximation and for example cannot distinguish between references inside
    // and outside comments.
    let deps = deps
        .into_iter()
        .filter(|d| !sources.contains(d))
        .collect_vec();
    Ok(deps)
}

/// Recursively calculate dependencies.
fn calculate_deps_recursively(
    path: &Path,
    file_map: &BTreeMap<String, PathBuf>,
    visited: &mut BTreeSet<String>,
    deps: &mut Vec<String>,
) -> anyhow::Result<()> {
    static REX: Lazy<Regex> =
        Lazy::new(|| Regex::new(r"(?m)0x[0-9abcdefABCDEF]+::\s*(\w+)").unwrap());
    if !visited.insert(path.to_string_lossy().to_string()) {
        return Ok(());
    }
    debug!("including `{}`", path.display());
    for dep in extract_matches(path, &*REX)? {
        if let Some(dep_path) = file_map.get(&dep) {
            let dep_str = dep_path.to_string_lossy().to_string();
            if !deps.contains(&dep_str) {
                deps.push(dep_str);
                calculate_deps_recursively(dep_path.as_path(), file_map, visited, deps)?;
            }
        }
    }
    Ok(())
}

/// Calculate a map of module names to files which define those modules.
fn calculate_file_map(deps: &[String]) -> anyhow::Result<BTreeMap<String, PathBuf>> {
    static REX: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m)module\s+(\w+)\s*\{").unwrap());
    let mut module_to_file = BTreeMap::new();
    for dep in deps {
        let dep_path = PathBuf::from(dep);
        for module in extract_matches(dep_path.as_path(), &*REX)? {
            module_to_file.insert(module, dep_path.clone());
        }
    }
    Ok(module_to_file)
}

/// Extracts matches out of some text file. `rex` must be a regular expression with one anonymous
/// group.
fn extract_matches(path: &Path, rex: &Regex) -> anyhow::Result<Vec<String>> {
    let mut content = String::new();
    let mut file = File::open(path)?;
    file.read_to_string(&mut content)?;
    let mut at = 0;
    let mut res = vec![];
    while let Some(cap) = rex.captures(&content[at..]) {
        res.push(cap.get(1).unwrap().as_str().to_string());
        at += cap.get(0).unwrap().end();
    }
    Ok(res)
}
<|endoftext|>


<commit_before>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

//! According to the [Arrow Metadata Specification](https://arrow.apache.org/docs/metadata.html):
//!
//! > A record batch is a collection of top-level named, equal length Arrow arrays
//! > (or vectors). If one of the arrays contains nested data, its child arrays are not
//! > required to be the same length as the top-level arrays.

use std::sync::Arc;

use crate::array::*;
use crate::datatypes::*;
use crate::error::{ArrowError, Result};

/// A batch of column-oriented data
#[derive(Clone)]
pub struct RecordBatch {
    schema: Arc<Schema>,
    columns: Vec<Arc<Array>>,
}

impl RecordBatch {
    /// Creates a `RecordBatch` from a schema and columns
    ///
    /// Expects the following:
    ///  * the vec of columns to not be empty
    ///  * the schema and column data types to have equal lengths and match
    ///  * each array in columns to have the same length
    pub fn try_new(schema: Arc<Schema>, columns: Vec<ArrayRef>) -> Result<Self> {
        // check that there are some columns
        if columns.is_empty() {
            return Err(ArrowError::InvalidArgumentError(
                "at least one column must be defined to create a record batch"
                    .to_string(),
            ));
        }
        // check that number of fields in schema match column length
        if schema.fields().len() != columns.len() {
            return Err(ArrowError::InvalidArgumentError(
                "number of columns must match number of fields in schema".to_string(),
            ));
        }
        // check that all columns have the same row count, and match the schema
        let len = columns[0].data().len();
        for i in 0..columns.len() {
            if columns[i].len() != len {
                return Err(ArrowError::InvalidArgumentError(
                    "all columns in a record batch must have the same length".to_string(),
                ));
            }
            if columns[i].data_type() != schema.field(i).data_type() {
                return Err(ArrowError::InvalidArgumentError(format!(
                    "column types must match schema types, expected {:?} but found {:?} at column index {}",
                    schema.field(i).data_type(),
                    columns[i].data_type(),
                    i)));
            }
        }
        Ok(RecordBatch { schema, columns })
    }

    /// Returns the schema of the record batch
    pub fn schema(&self) -> &Arc<Schema> {
        &self.schema
    }

    /// Number of columns in the record batch
    pub fn num_columns(&self) -> usize {
        self.columns.len()
    }

    /// Number of rows in each column
    pub fn num_rows(&self) -> usize {
        self.columns[0].data().len()
    }

    /// Get a reference to a column's array by index
    pub fn column(&self, i: usize) -> &ArrayRef {
        &self.columns[i]
    }
}

unsafe impl Send for RecordBatch {}
unsafe impl Sync for RecordBatch {}

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

    use crate::array_data::*;
    use crate::buffer::*;

    #[test]
    fn create_record_batch() {
        let schema = Schema::new(vec![
            Field::new("a", DataType::Int32, false),
            Field::new("b", DataType::Utf8, false),
        ]);

        let v = vec![1, 2, 3, 4, 5];
        let array_data = ArrayData::builder(DataType::Int32)
            .len(5)
            .add_buffer(Buffer::from(v.to_byte_slice()))
            .build();
        let a = Int32Array::from(array_data);

        let v = vec![b'a', b'b', b'c', b'd', b'e'];
        let offset_data = vec![0, 1, 2, 3, 4, 5, 6];
        let array_data = ArrayData::builder(DataType::Utf8)
            .len(5)
            .add_buffer(Buffer::from(offset_data.to_byte_slice()))
            .add_buffer(Buffer::from(v.to_byte_slice()))
            .build();
        let b = BinaryArray::from(array_data);

        let record_batch =
            RecordBatch::try_new(Arc::new(schema), vec![Arc::new(a), Arc::new(b)])
                .unwrap();

        assert_eq!(5, record_batch.num_rows());
        assert_eq!(2, record_batch.num_columns());
        assert_eq!(&DataType::Int32, record_batch.schema().field(0).data_type());
        assert_eq!(&DataType::Utf8, record_batch.schema().field(1).data_type());
        assert_eq!(5, record_batch.column(0).data().len());
        assert_eq!(5, record_batch.column(1).data().len());
    }

    #[test]
    fn create_record_batch_schema_mismatch() {
        let schema = Schema::new(vec![Field::new("a", DataType::Int32, false)]);

        let a = Int64Array::from(vec![1, 2, 3, 4, 5]);

        let batch = RecordBatch::try_new(Arc::new(schema), vec![Arc::new(a)]);
        assert!(!batch.is_ok());
    }

    #[test]
    fn create_record_batch_record_mismatch() {
        let schema = Schema::new(vec![Field::new("a", DataType::Int32, false)]);

        let a = Int32Array::from(vec![1, 2, 3, 4, 5]);
        let b = Int32Array::from(vec![1, 2, 3, 4, 5]);

        let batch =
            RecordBatch::try_new(Arc::new(schema), vec![Arc::new(a), Arc::new(b)]);
        assert!(!batch.is_ok());
    }
}
<commit_msg>ARROW-5187: [Rust] Add ability to convert StructArray to RecordBatch<commit_after>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

//! According to the [Arrow Metadata Specification](https://arrow.apache.org/docs/metadata.html):
//!
//! > A record batch is a collection of top-level named, equal length Arrow arrays
//! > (or vectors). If one of the arrays contains nested data, its child arrays are not
//! > required to be the same length as the top-level arrays.

use std::sync::Arc;

use crate::array::*;
use crate::datatypes::*;
use crate::error::{ArrowError, Result};

/// A batch of column-oriented data
#[derive(Clone)]
pub struct RecordBatch {
    schema: Arc<Schema>,
    columns: Vec<Arc<Array>>,
}

impl RecordBatch {
    /// Creates a `RecordBatch` from a schema and columns
    ///
    /// Expects the following:
    ///  * the vec of columns to not be empty
    ///  * the schema and column data types to have equal lengths and match
    ///  * each array in columns to have the same length
    pub fn try_new(schema: Arc<Schema>, columns: Vec<ArrayRef>) -> Result<Self> {
        // check that there are some columns
        if columns.is_empty() {
            return Err(ArrowError::InvalidArgumentError(
                "at least one column must be defined to create a record batch"
                    .to_string(),
            ));
        }
        // check that number of fields in schema match column length
        if schema.fields().len() != columns.len() {
            return Err(ArrowError::InvalidArgumentError(
                "number of columns must match number of fields in schema".to_string(),
            ));
        }
        // check that all columns have the same row count, and match the schema
        let len = columns[0].data().len();
        for i in 0..columns.len() {
            if columns[i].len() != len {
                return Err(ArrowError::InvalidArgumentError(
                    "all columns in a record batch must have the same length".to_string(),
                ));
            }
            if columns[i].data_type() != schema.field(i).data_type() {
                return Err(ArrowError::InvalidArgumentError(format!(
                    "column types must match schema types, expected {:?} but found {:?} at column index {}",
                    schema.field(i).data_type(),
                    columns[i].data_type(),
                    i)));
            }
        }
        Ok(RecordBatch { schema, columns })
    }

    /// Returns the schema of the record batch
    pub fn schema(&self) -> &Arc<Schema> {
        &self.schema
    }

    /// Number of columns in the record batch
    pub fn num_columns(&self) -> usize {
        self.columns.len()
    }

    /// Number of rows in each column
    pub fn num_rows(&self) -> usize {
        self.columns[0].data().len()
    }

    /// Get a reference to a column's array by index
    pub fn column(&self, i: usize) -> &ArrayRef {
        &self.columns[i]
    }
}

impl From<&StructArray> for RecordBatch {
    /// Create a record batch from struct array.
    ///
    /// This currently does not flatten and nested struct types
    fn from(struct_array: &StructArray) -> Self {
        if let DataType::Struct(fields) = struct_array.data_type() {
            let schema = Schema::new(fields.clone());
            let columns = struct_array.boxed_fields.clone();
            RecordBatch {
                schema: Arc::new(schema),
                columns,
            }
        } else {
            unreachable!("unable to get datatype as struct")
        }
    }
}

unsafe impl Send for RecordBatch {}
unsafe impl Sync for RecordBatch {}

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

    use crate::array_data::*;
    use crate::buffer::*;

    #[test]
    fn create_record_batch() {
        let schema = Schema::new(vec![
            Field::new("a", DataType::Int32, false),
            Field::new("b", DataType::Utf8, false),
        ]);

        let v = vec![1, 2, 3, 4, 5];
        let array_data = ArrayData::builder(DataType::Int32)
            .len(5)
            .add_buffer(Buffer::from(v.to_byte_slice()))
            .build();
        let a = Int32Array::from(array_data);

        let v = vec![b'a', b'b', b'c', b'd', b'e'];
        let offset_data = vec![0, 1, 2, 3, 4, 5, 6];
        let array_data = ArrayData::builder(DataType::Utf8)
            .len(5)
            .add_buffer(Buffer::from(offset_data.to_byte_slice()))
            .add_buffer(Buffer::from(v.to_byte_slice()))
            .build();
        let b = BinaryArray::from(array_data);

        let record_batch =
            RecordBatch::try_new(Arc::new(schema), vec![Arc::new(a), Arc::new(b)])
                .unwrap();

        assert_eq!(5, record_batch.num_rows());
        assert_eq!(2, record_batch.num_columns());
        assert_eq!(&DataType::Int32, record_batch.schema().field(0).data_type());
        assert_eq!(&DataType::Utf8, record_batch.schema().field(1).data_type());
        assert_eq!(5, record_batch.column(0).data().len());
        assert_eq!(5, record_batch.column(1).data().len());
    }

    #[test]
    fn create_record_batch_schema_mismatch() {
        let schema = Schema::new(vec![Field::new("a", DataType::Int32, false)]);

        let a = Int64Array::from(vec![1, 2, 3, 4, 5]);

        let batch = RecordBatch::try_new(Arc::new(schema), vec![Arc::new(a)]);
        assert!(!batch.is_ok());
    }

    #[test]
    fn create_record_batch_record_mismatch() {
        let schema = Schema::new(vec![Field::new("a", DataType::Int32, false)]);

        let a = Int32Array::from(vec![1, 2, 3, 4, 5]);
        let b = Int32Array::from(vec![1, 2, 3, 4, 5]);

        let batch =
            RecordBatch::try_new(Arc::new(schema), vec![Arc::new(a), Arc::new(b)]);
        assert!(!batch.is_ok());
    }

    #[test]
    fn create_record_batch_from_struct_array() {
        let boolean_data = ArrayData::builder(DataType::Boolean)
            .len(4)
            .add_buffer(Buffer::from([12_u8]))
            .build();
        let int_data = ArrayData::builder(DataType::Int32)
            .len(4)
            .add_buffer(Buffer::from([42, 28, 19, 31].to_byte_slice()))
            .build();
        let struct_array = StructArray::from(vec![
            (
                Field::new("b", DataType::Boolean, false),
                Arc::new(BooleanArray::from(vec![false, false, true, true]))
                    as Arc<Array>,
            ),
            (
                Field::new("c", DataType::Int32, false),
                Arc::new(Int32Array::from(vec![42, 28, 19, 31])),
            ),
        ]);

        let batch = RecordBatch::from(&struct_array);
        assert_eq!(2, batch.num_columns());
        assert_eq!(4, batch.num_rows());
        assert_eq!(
            struct_array.data_type(),
            &DataType::Struct(batch.schema().fields().to_vec())
        );
        assert_eq!(batch.column(0).data(), boolean_data);
        assert_eq!(batch.column(1).data(), int_data);
    }
}
<|endoftext|>


<commit_before>#![allow(dead_code)]

extern crate jam;

use alto::*;
use std::sync::Arc;

extern crate alto;
extern crate ogg;
extern crate lewton;

use lewton::VorbisError;
use lewton::inside_ogg::OggStreamReader;

use std::path::Path;
use std::fs::File;
use std::iter::Extend;

pub type SampleRate = u32;

#[derive(Clone, Debug)]
struct Sound {
    data : Vec<i16>,
    sample_rate: u32,
    channels: u8,
}

fn load_ogg<P: AsRef<Path>>(path: P) -> Result<Sound, VorbisError> {
    let f = try!(File::open(path));

	// Prepare the reading
    let packet_reader = ogg::PacketReader::new(f);
	let mut srr = try!(OggStreamReader::new(packet_reader));

    if srr.ident_hdr.audio_channels > 2 {
		// the openal crate can't process these many channels directly
        // std::vec::Vec<i16>
		println!("Stream error: {} channels are too many!", srr.ident_hdr.audio_channels);
	}

    // let mut len_play = 0.0;
    let mut data : Vec<i16> = Vec::new();
    while let Some(pck_samples) = try!(srr.read_dec_packet_itl()) {
        // println!("I got some shit {:?}", pck_samples);
        // len_play += pck_samples.len() as f32 / srr.ident_hdr.audio_sample_rate as f32;
        data.extend(pck_samples.iter());
    }

    Ok(Sound {
        data: data,
        sample_rate: srr.ident_hdr.audio_sample_rate,
        channels: srr.ident_hdr.audio_channels,
    })
}

fn main() {
//    let alto = Alto::load_default().unwrap();
    let alto = Alto::load("./OpenAL64.dll").unwrap();
 	println!("Using output: {:?}", alto.default_output().unwrap());

	let dev = alto.open(None).unwrap();
	let ctx = dev.new_context(None).unwrap();

    let sound = load_ogg("../coins.ogg").unwrap();
    // println!("sound -> {:?}", sound);
    println!("gonna get buff");
    let mut buf = ctx.new_buffer().unwrap();
    println!("got buff");
    buf.set_data::<Mono<i16>, _>(sound.data, sound.sample_rate as i32).unwrap();
    let buf = Arc::new(buf);

    let mut src = ctx.new_static_source().unwrap();
    src.set_buffer(buf).unwrap();
    src.set_looping(true).unwrap();

    println!("Playing static 440hz sine wave...");
    src.play().unwrap();

    std::thread::sleep(std::time::Duration::new(2, 0));
    // {
	// 	let mut buf = ctx.new_buffer().unwrap();
	// 	// buf.set_data(SinWave::new(44_000 / 440, 0.25).render().take(44_000 / 440).collect::<Vec<_>>(), 44_000).unwrap();
	// 	let buf = Arc::new(buf);

	// 	let mut src = ctx.new_static_source().unwrap();
	// 	src.set_buffer(buf).unwrap();
	// 	src.set_looping(true).unwrap();

	// 	println!("Playing static 440hz sine wave...");
	// 	src.play().unwrap();

	// 	std::thread::sleep(std::time::Duration::new(2, 0));
	// }
}

struct SinWave {
	len: i32,
	vol: f32,
	cursor: i32,
}

struct SinWaveRenderer<'w>(&'w mut SinWave);

impl SinWave {
	pub fn new(len: i32, vol: f32) -> SinWave {
		SinWave{len: len, vol: vol, cursor: 0}
	}

	pub fn render(&mut self) -> SinWaveRenderer {
		SinWaveRenderer(self)
	}
}

impl<'w> Iterator for SinWaveRenderer<'w> {
	type Item = Mono<i16>;

	fn next(&mut self) -> Option<Mono<i16>> {
		let cursor = self.0.cursor;
		self.0.cursor += 1;
		if self.0.cursor == self.0.len { self.0.cursor = 0 }

		Some(Mono{center: ((cursor as f32 / self.0.len as f32 * 2.0 * std::f32::consts::PI).sin() * self.0.vol * std::i16::MAX as f32) as i16})
	}
}
<commit_msg>Update sound path<commit_after>#![allow(dead_code)]

extern crate jam;

use alto::*;
use std::sync::Arc;

extern crate alto;
extern crate ogg;
extern crate lewton;

use lewton::VorbisError;
use lewton::inside_ogg::OggStreamReader;

use std::path::Path;
use std::fs::File;
use std::iter::Extend;

pub type SampleRate = u32;

#[derive(Clone, Debug)]
struct Sound {
    data : Vec<i16>,
    sample_rate: u32,
    channels: u8,
}

fn load_ogg<P: AsRef<Path>>(path: P) -> Result<Sound, VorbisError> {
    let f = try!(File::open(path));

	// Prepare the reading
    let packet_reader = ogg::PacketReader::new(f);
	let mut srr = try!(OggStreamReader::new(packet_reader));

    if srr.ident_hdr.audio_channels > 2 {
		// the openal crate can't process these many channels directly
        // std::vec::Vec<i16>
		println!("Stream error: {} channels are too many!", srr.ident_hdr.audio_channels);
	}

    // let mut len_play = 0.0;
    let mut data : Vec<i16> = Vec::new();
    while let Some(pck_samples) = try!(srr.read_dec_packet_itl()) {
        // println!("I got some shit {:?}", pck_samples);
        // len_play += pck_samples.len() as f32 / srr.ident_hdr.audio_sample_rate as f32;
        data.extend(pck_samples.iter());
    }

    Ok(Sound {
        data: data,
        sample_rate: srr.ident_hdr.audio_sample_rate,
        channels: srr.ident_hdr.audio_channels,
    })
}

fn main() {
//    let alto = Alto::load_default().unwrap();
    let alto = Alto::load("./OpenAL64.dll").unwrap();
 	println!("Using output: {:?}", alto.default_output().unwrap());

	let dev = alto.open(None).unwrap();
	let ctx = dev.new_context(None).unwrap();

    let sound = load_ogg("../resources/sound/coins.ogg").unwrap();
    // println!("sound -> {:?}", sound);
    println!("gonna get buff");
    let mut buf = ctx.new_buffer().unwrap();
    println!("got buff");
    buf.set_data::<Mono<i16>, _>(sound.data, sound.sample_rate as i32).unwrap();
    let buf = Arc::new(buf);

    let mut src = ctx.new_static_source().unwrap();
    src.set_buffer(buf).unwrap();
    src.set_looping(true).unwrap();

    println!("Playing static 440hz sine wave...");
    src.play().unwrap();

    std::thread::sleep(std::time::Duration::new(2, 0));
    // {
	// 	let mut buf = ctx.new_buffer().unwrap();
	// 	// buf.set_data(SinWave::new(44_000 / 440, 0.25).render().take(44_000 / 440).collect::<Vec<_>>(), 44_000).unwrap();
	// 	let buf = Arc::new(buf);

	// 	let mut src = ctx.new_static_source().unwrap();
	// 	src.set_buffer(buf).unwrap();
	// 	src.set_looping(true).unwrap();

	// 	println!("Playing static 440hz sine wave...");
	// 	src.play().unwrap();

	// 	std::thread::sleep(std::time::Duration::new(2, 0));
	// }
}

struct SinWave {
	len: i32,
	vol: f32,
	cursor: i32,
}

struct SinWaveRenderer<'w>(&'w mut SinWave);

impl SinWave {
	pub fn new(len: i32, vol: f32) -> SinWave {
		SinWave{len: len, vol: vol, cursor: 0}
	}

	pub fn render(&mut self) -> SinWaveRenderer {
		SinWaveRenderer(self)
	}
}

impl<'w> Iterator for SinWaveRenderer<'w> {
	type Item = Mono<i16>;

	fn next(&mut self) -> Option<Mono<i16>> {
		let cursor = self.0.cursor;
		self.0.cursor += 1;
		if self.0.cursor == self.0.len { self.0.cursor = 0 }

		Some(Mono{center: ((cursor as f32 / self.0.len as f32 * 2.0 * std::f32::consts::PI).sin() * self.0.vol * std::i16::MAX as f32) as i16})
	}
}
<|endoftext|>


<commit_before>
use std::io::{Write,Read};
use hyper::server::{Request, Response};
use hyper::uri::RequestUri;
use hyper::header::{Authorization, Basic, Location, ContentType, CacheDirective, CacheControl,
                    Pragma};
use hyper::status::StatusCode;
use url::Url;
use {ClientData, OAuthError, AuthzError, AuthzErrorCode, TokenError, TokenErrorCode,
     AuthzRequest, TokenData, ClientId, RedirectUri};

header! { (WwwAuthenticate, "WWW-Authenticate") => [String] }

macro_rules! token_response_fail {
    ($response:ident, $status_opt:expr, $error:expr, $description:expr, $uri:expr) => {
        {
            *$response.status_mut() = match $status_opt {
                Some(s) => s,
                None => StatusCode::BadRequest
            };
            let error_data = TokenError {
                error: $error,
                error_description: $description.map(|s: &str| s.to_owned()),
                error_uri: $uri,
            };
            let body = error_data.as_json();
            let mut response = $response.start().unwrap();
            response.write_all(body.as_bytes()).unwrap();
            let _ = response.end();
            return;
        }
    };
    ($response:ident, $status_opt:expr, $error:expr, $description:expr) => {
        token_response_fail!($response, $status_opt, $error, $description, None)
    };
    ($response:ident, $status_opt:expr, $error:expr) => {
        token_response_fail!($response, $status_opt, $error, None, None)
    };
}

pub trait AuthzServer<C>
{
    /// Fetch data about a registered OAuth 2.0 client (clients are the other websites
    /// which are trying to login to your website on behalf of the user, and should have
    /// been registered with your site ahead of time).
    ///
    /// Should return Ok(Some(ClientData)) if found, Ok(None) if not found, and
    /// Err(OAuthError) on error.
    ///
    /// `context` comes from whatever you pass into `handle_authz_request()`,
    /// `finish_authz_request()` or `handle_token_request()`
    fn fetch_client_data(&self, context: &mut C, client_id: &ClientId)
                         -> Result<Option<ClientData>, OAuthError>;

    /// Retrieve the data associated with an issued authentication code.
    fn retrieve_client_authorization(&self, context: &mut C, code: &str)
                                     -> Result<(ClientId, RedirectUri), OAuthError>;

    /// Issue token to client, recording the issuance internally.
    fn issue_token_to_client(&mut self, context: &mut C, code: &str, client_id: &ClientId)
                             -> Result<TokenData, OAuthError>;

    /// Handle an HTTP request at the authorization endpoint
    /// (From a user-agent, redirected by a client)
    ///
    /// This function parses and validates the request.  Then it forms the request data
    /// and returns it to the caller.  The caller should then:
    ///  1) Check if the return value has error set.  If so, call back into
    ///     finish_authz_request() to pass that error on.
    ///  2) Authenticate the user (this may involve multiple HTTP round trips).  If failed,
    ///     set error to AccessDenied and pass on to finish_authz_request().
    ///  3) Authorize the request (generally by asking the user if this is what they want)
    ///     If denied, set error to AccessDenied and pass on to finish_authz_request().
    ///  4) If all went well, set authorization_code and pass on to finish_authz_request().
    ///
    /// Refer to rfc6749 section 3.1 as to the requirements of the URL endpoint that
    /// performs this task (TLS, no fragment, support of GET with POST optional)
    fn handle_authz_request(&self, context: &mut C, request: Request)
                            -> Result<(AuthzRequest, Option<AuthzError>), OAuthError>
    {
        // Get request URI, so we can get parameters out of it's query string
        let uri_string: &String = match request.uri {
            RequestUri::AbsolutePath(ref s) => s,
            _ => {
                // rfc6749, section 4.1.2.1 paragraph 1, instructs us to redirect onwards
                // with error = 'invalid_request', but since we cannot get parameters out
                // of such a bad request, we have to fail early.
                return Err(OAuthError::AuthzBadRequest);
            },
        };

        // Get expected (and optional) request parameters
        let mut response_type: Option<String> = None; // required
        let mut client_id: Option<ClientId> = None; // required
        let mut redirect_uri: Option<RedirectUri> = None; // optional
        let mut scope: Option<String> = None; // optional
        let mut state: Option<String> = None; // recommended, used for CSRF prevention
        let url = try!( Url::parse( &*format!("http://DUMMY{}",uri_string)) );
        for (key,val) in url.query_pairs() {
            match &*key {
                "client_id" => client_id = Some(ClientId(val.into_owned())),
                "response_type" => response_type = Some(val.into_owned()),
                "redirect_uri" => redirect_uri = Some(RedirectUri(val.into_owned())),
                "scope" => scope = Some(val.into_owned()),
                "state" => state = Some(val.into_owned()),
                _ => {} // MUST ignore unknown parameters
            }
        }

        // FIXME -- IF redirect_uri IS MISSING
        //       -- IF redirect_uri IS INVALID
        //       -- IF redirect_uri IS MISMATCHED
        //       -- IF client_id IS MISSING
        //       -- IF client_id IS INVALID
        //   Then inform user-agent directly, DO NOT redirect.

        // Require `client_id`
        let client_id = match client_id {
            None => {
                // rfc6749, section 4.1.2.1 paragraph 1: "If the request fails due to a
                // missing, invalid, or mismatching redirection URI, or if the client
                // identifier is missing or invalid, the authorization server SHOULD
                // inform the resource owner of the error and MUST NOT automatically
                // redirect the user-agent to the invalid redirection URI.
                return Err(OAuthError::AuthzMissingClientId);
            },
            Some(cid) => cid
        };

        // Verify the `client_id` matches a known client
        // (and fetch client_data for further use later on)
        if let None = try!(self.fetch_client_data(context, &client_id))
        {
            // rfc6749, section 4.1.2.1 paragraph 1: "If the request fails due to a
            // missing, invalid, or mismatching redirection URI, or if the client
            // identifier is missing or invalid, the authorization server SHOULD
            // inform the resource owner of the error and MUST NOT automatically
            // redirect the user-agent to the invalid redirection URI.
            return Err(OAuthError::AuthzUnknownClient);
        };

        let mut error: Option<AuthzError> = None; // Error to pass through, if any

        // Require `response_type` and check it
        match response_type {
            None => error = Some(AuthzError {
                error: AuthzErrorCode::InvalidRequest,
                error_description: Some("Missing `response_type` parameter.".to_owned()),
                error_uri: None,
                state: state.clone(),
            }),
            Some(rt) => if &*rt != "code" {
                error = Some(AuthzError {
                    error: AuthzErrorCode::UnsupportedResponseType,
                    error_description: Some("Respose type must be `code`.".to_owned()),
                    error_uri: None,
                    state: state.clone(),
                });
            }
        }

        Ok((AuthzRequest {
            client_id: client_id,
            redirect_uri: redirect_uri,
            scope: scope,
            state: state,
        }, error))
    }

    /// This resolves the redirect_uri by using the one from the request (you should
    /// pass that in from AuthzRequest.redirect_uri) if it is valid, or else using
    /// the first one registered with the client if the request did not specify one.
    fn resolve_redirect_uri(&mut self, context: &mut C, client_id: &ClientId,
                            request_redirect_uri: Option<&RedirectUri>)
                            -> Result<RedirectUri, OAuthError>
    {
        // Look up the client data
        let client_data = match try!(self.fetch_client_data(
            context, client_id))
        {
            Some(cd) => cd,
            None => return Err(OAuthError::AuthzUnknownClient),
        };

        // Get the redirect_uri
        let cru: &RedirectUri = match request_redirect_uri {
            Some(ref uri) => uri,
            None => return Ok(client_data.redirect_uri[0].clone())
        };

        // Verify cru is a valid redirect uri
        for validuri in &client_data.redirect_uri {
            if cru == validuri {
                return Ok(cru.clone())
            }
        }
        // rfc6749, section 4.1.2.1 paragraph 1: "If the request fails due to a
        // missing, invalid, or mismatching redirection URI, or if the client
        // identifier is missing or invalid, the authorization server SHOULD
        // inform the resource owner of the error and MUST NOT automatically
        // redirect the user-agent to the invalid redirection URI.
        Err(OAuthError::AuthzRedirectUrlNotRegistered)
    }

    /// This finishes an Authorization Request sequence if you have granted the
    /// request.  It should be called after the user-agent end user has been
    /// authenticated and has approved or denied the request.
    fn grant_authz_request(&mut self, mut response: Response,
                           redirect_uri: &RedirectUri, authorization_code: String,
                           state: Option<String>) -> Result<(), OAuthError>
    {
        // Start the redirect URL
        let mut url = try!(Url::parse(&***redirect_uri));

        // Put the code into the redirect url
        url.query_pairs_mut()
            .append_pair("code", &*authorization_code);

        // Put the state into the redirect url, if some
        if let Some(ref s) = state {
            url.query_pairs_mut()
                .append_pair("state", s);
        }

        // Do the redirect
        response.headers_mut().set(Location(url.into_string()));
        *response.status_mut() = StatusCode::Found;
        let streaming_response = response.start().unwrap();
        let _ = streaming_response.end();
        Ok(())
    }

    /// This finishes an Authorization Request sequence if you have denied the
    /// request.
    fn deny_authz_request(&mut self, mut response: Response,
                          redirect_uri: &RedirectUri, error: AuthzError)
                          -> Result<(), OAuthError>
    {
        // Start the redirect URL
        let mut url = try!(Url::parse(&***redirect_uri));

        // Put error details into the redirect url
        error.put_into_query_string(&mut url);

        // Do the redirect
        response.headers_mut().set(Location(url.into_string()));
        *response.status_mut() = StatusCode::Found;
        let streaming_response = response.start().unwrap();
        let _ = streaming_response.end();
        Ok(())
    }

    /// Handle an HTTP request at the token endpoint
    /// (from a client directly, via POST only)
    ///
    /// Refer to rfc6749 section 3.2 as to the requirements of the URL endpoint that
    /// performs this task (TLS, no fragment, must use POST)
    fn handle_token_request(&mut self, context: &mut C,
                            mut request: Request, mut response: Response)
    {
        // Start preparing the response, as we set some response data regardless
        // of success or failure.
        response.headers_mut().set(ContentType::json());
        response.headers_mut().set(CacheControl(vec![ CacheDirective::NoStore ]));
        response.headers_mut().set(Pragma::NoCache);

        // Authenticate the client using HTTP Basic Authorization
        let basic: Basic = if let Some(&Authorization(ref basic)) =
            request.headers.get::<Authorization<Basic>>()
        {
            basic.clone()
        } else {
            token_response_fail!(response, None, TokenErrorCode::InvalidClient,
                                 Some("Authorization header missing"));
        };
        let (auth_client_id, authz_credentials): (ClientId, String) =
            match ClientData::http_basic_authentication_deconstruct(basic) {
                Ok(stuff) => stuff,
                Err(_) => token_response_fail!(response, None, TokenErrorCode::InvalidRequest,
                                               Some("Authorization header failed UTF-8 check")),
            };

        let client_data = match self.fetch_client_data(context, &auth_client_id) {
            Err(_) => token_response_fail!(response, None, TokenErrorCode::InvalidClient,
                                           Some("No such client")),
            Ok(v) => match v {
                Some(cd) => cd,
                None => token_response_fail!(response, None, TokenErrorCode::InvalidClient,
                                             Some("No such client")),
            }
        };

        if authz_credentials != client_data.credentials {
            response.headers_mut().set(WwwAuthenticate("Basic".to_owned()));
            token_response_fail!(response, Some(StatusCode::Unauthorized),
                                 TokenErrorCode::InvalidClient,
                                 Some("Client credentials do not match"));
        }

        // Fail if the request is bad
        match request.uri {
            RequestUri::AbsolutePath(_) => {},
            _ => token_response_fail!(response, None, TokenErrorCode::InvalidRequest,
                                      Some("Only AbsolutePath URLs are allowed")),
        };

        // Read the body as it will contain the url-encoded parameters
        let mut body: Vec<u8> = Vec::new();
        if let Err(_) = request.read_to_end(&mut body) {
            token_response_fail!(response, None, TokenErrorCode::InvalidRequest,
                                 Some("Failed to read request body"));
        }
        let body = match String::from_utf8(body) {
            Ok(b) => b,
            Err(_) => token_response_fail!(response, None, TokenErrorCode::InvalidRequest,
                                           Some("Body did not pass UTF-8 check")),
        };

        // Get expected (and optional) request parameters
        let mut grant_type: Option<String> = None;
        let mut code: Option<String> = None;
        let mut redirect_uri: Option<RedirectUri> = None;

        let url = match Url::parse( &*format!("http://DUMMY?{}",body)) {
            Ok(url) => url,
            Err(_) => token_response_fail!(response, None, TokenErrorCode::InvalidRequest,
                                           Some("Unable to parse body as www-form-urlencoded")),
        };

        for (key,val) in url.query_pairs() {
            match &*key {
                "grant_type" => grant_type = Some(val.into_owned()),
                "code" => code = Some(val.into_owned()),
                "redirect_uri" => redirect_uri = Some(RedirectUri(val.into_owned())),
                _ => {} // MUST ignore unknown parameters
            }
        }

        // Require grant_type = "authorization_code"
        match grant_type {
            None => token_response_fail!(response, None, TokenErrorCode::InvalidRequest,
                                         Some("grant_type parameter must be supplied in body")),
            Some(gt) => if gt != "authorization_code" {
                token_response_fail!(response, None, TokenErrorCode::UnsupportedGrantType);
            }
        }

        // Require code, and retrieve the data we issued with the code
        // This also verifies that the code is valid, and was issued to the
        // client in question.
        let (stored_client_id, stored_redirect_uri): (ClientId, RedirectUri) = match code {
            None => token_response_fail!(response, None, TokenErrorCode::InvalidRequest,
                                         Some("code parameter must be supplied in body")),
            Some(ref c) => match self.retrieve_client_authorization(context, c)
            {
                Ok(pair) => pair,
                _ => token_response_fail!(response, None, TokenErrorCode::InvalidGrant,
                                          Some("Invalid authorization code")),
            }
        };

        // Verify the client_id matches
        if stored_client_id != client_data.client_id {
            // FIXME: also delete the stored code and related tokens.
            token_response_fail!(response, None, TokenErrorCode::InvalidGrant,
                                 Some("client_id mismatch"));
        }

        // Verify the redirect_uri matches, if it was used originally
        if redirect_uri.is_some() {
            match redirect_uri {
                None => token_response_fail!(response, None, TokenErrorCode::InvalidGrant,
                                             Some("redirect_uri parameter must be \
                                                   supplied in body")),
                Some(ru) => if &ru != &stored_redirect_uri {
                    token_response_fail!(response, None, TokenErrorCode::InvalidGrant,
                                         Some("redirect_uri parameter mismatch"));
                }
            }
        }

        // Issue token
        let token = match self.issue_token_to_client(context, code.as_ref().unwrap(),
                                                     &client_data.client_id) {
            Ok(t) => t,
            Err(_) => token_response_fail!(response, None, TokenErrorCode::InvalidGrant,
                                           None),
        };

        // JSON-ify the response.
        let body = token.as_json();

        *response.status_mut() = StatusCode::Ok;

        let mut response = response.start().unwrap();
        response.write_all(body.as_bytes()).unwrap();
        let _ = response.end();
    }
}

<commit_msg>handle_authz_request to take URI str, not entire request<commit_after>
use std::io::{Write,Read};
use hyper::server::{Request, Response};
use hyper::uri::RequestUri;
use hyper::header::{Authorization, Basic, Location, ContentType, CacheDirective, CacheControl,
                    Pragma};
use hyper::status::StatusCode;
use url::Url;
use {ClientData, OAuthError, AuthzError, AuthzErrorCode, TokenError, TokenErrorCode,
     AuthzRequest, TokenData, ClientId, RedirectUri};

header! { (WwwAuthenticate, "WWW-Authenticate") => [String] }

macro_rules! token_response_fail {
    ($response:ident, $status_opt:expr, $error:expr, $description:expr, $uri:expr) => {
        {
            *$response.status_mut() = match $status_opt {
                Some(s) => s,
                None => StatusCode::BadRequest
            };
            let error_data = TokenError {
                error: $error,
                error_description: $description.map(|s: &str| s.to_owned()),
                error_uri: $uri,
            };
            let body = error_data.as_json();
            let mut response = $response.start().unwrap();
            response.write_all(body.as_bytes()).unwrap();
            let _ = response.end();
            return;
        }
    };
    ($response:ident, $status_opt:expr, $error:expr, $description:expr) => {
        token_response_fail!($response, $status_opt, $error, $description, None)
    };
    ($response:ident, $status_opt:expr, $error:expr) => {
        token_response_fail!($response, $status_opt, $error, None, None)
    };
}

pub trait AuthzServer<C>
{
    /// Fetch data about a registered OAuth 2.0 client (clients are the other websites
    /// which are trying to login to your website on behalf of the user, and should have
    /// been registered with your site ahead of time).
    ///
    /// Should return Ok(Some(ClientData)) if found, Ok(None) if not found, and
    /// Err(OAuthError) on error.
    ///
    /// `context` comes from whatever you pass into `handle_authz_request()`,
    /// `finish_authz_request()` or `handle_token_request()`
    fn fetch_client_data(&self, context: &mut C, client_id: &ClientId)
                         -> Result<Option<ClientData>, OAuthError>;

    /// Retrieve the data associated with an issued authentication code.
    fn retrieve_client_authorization(&self, context: &mut C, code: &str)
                                     -> Result<(ClientId, RedirectUri), OAuthError>;

    /// Issue token to client, recording the issuance internally.
    fn issue_token_to_client(&mut self, context: &mut C, code: &str, client_id: &ClientId)
                             -> Result<TokenData, OAuthError>;

    /// Handle an HTTP request at the authorization endpoint
    /// (From a user-agent, redirected by a client)
    ///
    /// This function parses and validates the request.  Then it forms the request data
    /// and returns it to the caller.  The caller should then:
    ///  1) Check if the return value has error set.  If so, call back into
    ///     finish_authz_request() to pass that error on.
    ///  2) Authenticate the user (this may involve multiple HTTP round trips).  If failed,
    ///     set error to AccessDenied and pass on to finish_authz_request().
    ///  3) Authorize the request (generally by asking the user if this is what they want)
    ///     If denied, set error to AccessDenied and pass on to finish_authz_request().
    ///  4) If all went well, set authorization_code and pass on to finish_authz_request().
    ///
    /// Refer to rfc6749 section 3.1 as to the requirements of the URL endpoint that
    /// performs this task (TLS, no fragment, support of GET with POST optional)
    fn handle_authz_request(&self, context: &mut C, uri_string: &str)
                            -> Result<(AuthzRequest, Option<AuthzError>), OAuthError>
    {
        // Get expected (and optional) request parameters
        let mut response_type: Option<String> = None; // required
        let mut client_id: Option<ClientId> = None; // required
        let mut redirect_uri: Option<RedirectUri> = None; // optional
        let mut scope: Option<String> = None; // optional
        let mut state: Option<String> = None; // recommended, used for CSRF prevention
        let url = try!( Url::parse( uri_string) );
        for (key,val) in url.query_pairs() {
            match &*key {
                "client_id" => client_id = Some(ClientId(val.into_owned())),
                "response_type" => response_type = Some(val.into_owned()),
                "redirect_uri" => redirect_uri = Some(RedirectUri(val.into_owned())),
                "scope" => scope = Some(val.into_owned()),
                "state" => state = Some(val.into_owned()),
                _ => {} // MUST ignore unknown parameters
            }
        }

        // FIXME -- IF redirect_uri IS MISSING
        //       -- IF redirect_uri IS INVALID
        //       -- IF redirect_uri IS MISMATCHED
        //       -- IF client_id IS MISSING
        //       -- IF client_id IS INVALID
        //   Then inform user-agent directly, DO NOT redirect.

        // Require `client_id`
        let client_id = match client_id {
            None => {
                // rfc6749, section 4.1.2.1 paragraph 1: "If the request fails due to a
                // missing, invalid, or mismatching redirection URI, or if the client
                // identifier is missing or invalid, the authorization server SHOULD
                // inform the resource owner of the error and MUST NOT automatically
                // redirect the user-agent to the invalid redirection URI.
                return Err(OAuthError::AuthzMissingClientId);
            },
            Some(cid) => cid
        };

        // Verify the `client_id` matches a known client
        // (and fetch client_data for further use later on)
        if let None = try!(self.fetch_client_data(context, &client_id))
        {
            // rfc6749, section 4.1.2.1 paragraph 1: "If the request fails due to a
            // missing, invalid, or mismatching redirection URI, or if the client
            // identifier is missing or invalid, the authorization server SHOULD
            // inform the resource owner of the error and MUST NOT automatically
            // redirect the user-agent to the invalid redirection URI.
            return Err(OAuthError::AuthzUnknownClient);
        };

        let mut error: Option<AuthzError> = None; // Error to pass through, if any

        // Require `response_type` and check it
        match response_type {
            None => error = Some(AuthzError {
                error: AuthzErrorCode::InvalidRequest,
                error_description: Some("Missing `response_type` parameter.".to_owned()),
                error_uri: None,
                state: state.clone(),
            }),
            Some(rt) => if &*rt != "code" {
                error = Some(AuthzError {
                    error: AuthzErrorCode::UnsupportedResponseType,
                    error_description: Some("Respose type must be `code`.".to_owned()),
                    error_uri: None,
                    state: state.clone(),
                });
            }
        }

        Ok((AuthzRequest {
            client_id: client_id,
            redirect_uri: redirect_uri,
            scope: scope,
            state: state,
        }, error))
    }

    /// This resolves the redirect_uri by using the one from the request (you should
    /// pass that in from AuthzRequest.redirect_uri) if it is valid, or else using
    /// the first one registered with the client if the request did not specify one.
    fn resolve_redirect_uri(&mut self, context: &mut C, client_id: &ClientId,
                            request_redirect_uri: Option<&RedirectUri>)
                            -> Result<RedirectUri, OAuthError>
    {
        // Look up the client data
        let client_data = match try!(self.fetch_client_data(
            context, client_id))
        {
            Some(cd) => cd,
            None => return Err(OAuthError::AuthzUnknownClient),
        };

        // Get the redirect_uri
        let cru: &RedirectUri = match request_redirect_uri {
            Some(ref uri) => uri,
            None => return Ok(client_data.redirect_uri[0].clone())
        };

        // Verify cru is a valid redirect uri
        for validuri in &client_data.redirect_uri {
            if cru == validuri {
                return Ok(cru.clone())
            }
        }
        // rfc6749, section 4.1.2.1 paragraph 1: "If the request fails due to a
        // missing, invalid, or mismatching redirection URI, or if the client
        // identifier is missing or invalid, the authorization server SHOULD
        // inform the resource owner of the error and MUST NOT automatically
        // redirect the user-agent to the invalid redirection URI.
        Err(OAuthError::AuthzRedirectUrlNotRegistered)
    }

    /// This finishes an Authorization Request sequence if you have granted the
    /// request.  It should be called after the user-agent end user has been
    /// authenticated and has approved or denied the request.
    fn grant_authz_request(&mut self, mut response: Response,
                           redirect_uri: &RedirectUri, authorization_code: String,
                           state: Option<String>) -> Result<(), OAuthError>
    {
        // Start the redirect URL
        let mut url = try!(Url::parse(&***redirect_uri));

        // Put the code into the redirect url
        url.query_pairs_mut()
            .append_pair("code", &*authorization_code);

        // Put the state into the redirect url, if some
        if let Some(ref s) = state {
            url.query_pairs_mut()
                .append_pair("state", s);
        }

        // Do the redirect
        response.headers_mut().set(Location(url.into_string()));
        *response.status_mut() = StatusCode::Found;
        let streaming_response = response.start().unwrap();
        let _ = streaming_response.end();
        Ok(())
    }

    /// This finishes an Authorization Request sequence if you have denied the
    /// request.
    fn deny_authz_request(&mut self, mut response: Response,
                          redirect_uri: &RedirectUri, error: AuthzError)
                          -> Result<(), OAuthError>
    {
        // Start the redirect URL
        let mut url = try!(Url::parse(&***redirect_uri));

        // Put error details into the redirect url
        error.put_into_query_string(&mut url);

        // Do the redirect
        response.headers_mut().set(Location(url.into_string()));
        *response.status_mut() = StatusCode::Found;
        let streaming_response = response.start().unwrap();
        let _ = streaming_response.end();
        Ok(())
    }

    /// Handle an HTTP request at the token endpoint
    /// (from a client directly, via POST only)
    ///
    /// Refer to rfc6749 section 3.2 as to the requirements of the URL endpoint that
    /// performs this task (TLS, no fragment, must use POST)
    fn handle_token_request(&mut self, context: &mut C,
                            mut request: Request, mut response: Response)
    {
        // Start preparing the response, as we set some response data regardless
        // of success or failure.
        response.headers_mut().set(ContentType::json());
        response.headers_mut().set(CacheControl(vec![ CacheDirective::NoStore ]));
        response.headers_mut().set(Pragma::NoCache);

        // Authenticate the client using HTTP Basic Authorization
        let basic: Basic = if let Some(&Authorization(ref basic)) =
            request.headers.get::<Authorization<Basic>>()
        {
            basic.clone()
        } else {
            token_response_fail!(response, None, TokenErrorCode::InvalidClient,
                                 Some("Authorization header missing"));
        };
        let (auth_client_id, authz_credentials): (ClientId, String) =
            match ClientData::http_basic_authentication_deconstruct(basic) {
                Ok(stuff) => stuff,
                Err(_) => token_response_fail!(response, None, TokenErrorCode::InvalidRequest,
                                               Some("Authorization header failed UTF-8 check")),
            };

        let client_data = match self.fetch_client_data(context, &auth_client_id) {
            Err(_) => token_response_fail!(response, None, TokenErrorCode::InvalidClient,
                                           Some("No such client")),
            Ok(v) => match v {
                Some(cd) => cd,
                None => token_response_fail!(response, None, TokenErrorCode::InvalidClient,
                                             Some("No such client")),
            }
        };

        if authz_credentials != client_data.credentials {
            response.headers_mut().set(WwwAuthenticate("Basic".to_owned()));
            token_response_fail!(response, Some(StatusCode::Unauthorized),
                                 TokenErrorCode::InvalidClient,
                                 Some("Client credentials do not match"));
        }

        // Fail if the request is bad
        match request.uri {
            RequestUri::AbsolutePath(_) => {},
            _ => token_response_fail!(response, None, TokenErrorCode::InvalidRequest,
                                      Some("Only AbsolutePath URLs are allowed")),
        };

        // Read the body as it will contain the url-encoded parameters
        let mut body: Vec<u8> = Vec::new();
        if let Err(_) = request.read_to_end(&mut body) {
            token_response_fail!(response, None, TokenErrorCode::InvalidRequest,
                                 Some("Failed to read request body"));
        }
        let body = match String::from_utf8(body) {
            Ok(b) => b,
            Err(_) => token_response_fail!(response, None, TokenErrorCode::InvalidRequest,
                                           Some("Body did not pass UTF-8 check")),
        };

        // Get expected (and optional) request parameters
        let mut grant_type: Option<String> = None;
        let mut code: Option<String> = None;
        let mut redirect_uri: Option<RedirectUri> = None;

        let url = match Url::parse( &*format!("http://DUMMY?{}",body)) {
            Ok(url) => url,
            Err(_) => token_response_fail!(response, None, TokenErrorCode::InvalidRequest,
                                           Some("Unable to parse body as www-form-urlencoded")),
        };

        for (key,val) in url.query_pairs() {
            match &*key {
                "grant_type" => grant_type = Some(val.into_owned()),
                "code" => code = Some(val.into_owned()),
                "redirect_uri" => redirect_uri = Some(RedirectUri(val.into_owned())),
                _ => {} // MUST ignore unknown parameters
            }
        }

        // Require grant_type = "authorization_code"
        match grant_type {
            None => token_response_fail!(response, None, TokenErrorCode::InvalidRequest,
                                         Some("grant_type parameter must be supplied in body")),
            Some(gt) => if gt != "authorization_code" {
                token_response_fail!(response, None, TokenErrorCode::UnsupportedGrantType);
            }
        }

        // Require code, and retrieve the data we issued with the code
        // This also verifies that the code is valid, and was issued to the
        // client in question.
        let (stored_client_id, stored_redirect_uri): (ClientId, RedirectUri) = match code {
            None => token_response_fail!(response, None, TokenErrorCode::InvalidRequest,
                                         Some("code parameter must be supplied in body")),
            Some(ref c) => match self.retrieve_client_authorization(context, c)
            {
                Ok(pair) => pair,
                _ => token_response_fail!(response, None, TokenErrorCode::InvalidGrant,
                                          Some("Invalid authorization code")),
            }
        };

        // Verify the client_id matches
        if stored_client_id != client_data.client_id {
            // FIXME: also delete the stored code and related tokens.
            token_response_fail!(response, None, TokenErrorCode::InvalidGrant,
                                 Some("client_id mismatch"));
        }

        // Verify the redirect_uri matches, if it was used originally
        if redirect_uri.is_some() {
            match redirect_uri {
                None => token_response_fail!(response, None, TokenErrorCode::InvalidGrant,
                                             Some("redirect_uri parameter must be \
                                                   supplied in body")),
                Some(ru) => if &ru != &stored_redirect_uri {
                    token_response_fail!(response, None, TokenErrorCode::InvalidGrant,
                                         Some("redirect_uri parameter mismatch"));
                }
            }
        }

        // Issue token
        let token = match self.issue_token_to_client(context, code.as_ref().unwrap(),
                                                     &client_data.client_id) {
            Ok(t) => t,
            Err(_) => token_response_fail!(response, None, TokenErrorCode::InvalidGrant,
                                           None),
        };

        // JSON-ify the response.
        let body = token.as_json();

        *response.status_mut() = StatusCode::Ok;

        let mut response = response.start().unwrap();
        response.write_all(body.as_bytes()).unwrap();
        let _ = response.end();
    }
}

<|endoftext|>


<commit_before>//! TODO Fill in

use super::class::{Class, ClassBuilder};
use super::object::{self, Object, Objectable};
use cairo_sys::cairo_pattern_t;
use rlua::{self, LightUserData, Lua, Table, ToLua, UserData, UserDataMethods, Value};
use std::default::Default;
use std::fmt::{self, Display, Formatter};

/// Handle to the list of global key bindings
pub const ROOT_KEYS_HANDLE: &'static str = "__ROOT_KEYS";

#[derive(Clone, Debug)]
pub struct RootState {
    // TODO Fill in
    dummy: i32
}

pub struct Root<'lua>(Object<'lua>);

impl Default for RootState {
    fn default() -> Self {
        RootState { dummy: 0 }
    }
}

impl Display for RootState {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "Root: {:p}", self)
    }
}

impl<'lua> ToLua<'lua> for Root<'lua> {
    fn to_lua(self, lua: &'lua Lua) -> rlua::Result<Value<'lua>> {
        self.0.to_lua(lua)
    }
}

impl UserData for RootState {
    fn add_methods(methods: &mut UserDataMethods<Self>) {
        object::default_add_methods(methods);
    }
}

pub fn init(lua: &Lua) -> rlua::Result<Class> {
    // FIXME: In awesome there is no root class
    method_setup(lua, Class::builder(lua, "FIXME", None)?)?.save_class("root")?
                                                           .build()
}

fn method_setup<'lua>(lua: &'lua Lua,
                      builder: ClassBuilder<'lua>)
                      -> rlua::Result<ClassBuilder<'lua>> {
    // TODO Do properly
    use super::dummy;
    builder.method("connect_signal".into(), lua.create_function(dummy)?)?
           .method("buttons".into(), lua.create_function(dummy)?)?
           .method("wallpaper".into(), lua.create_function(wallpaper)?)?
           .method("tags".into(), lua.create_function(tags)?)?
           .method("keys".into(), lua.create_function(root_keys)?)?
           .method("size".into(), lua.create_function(dummy_double)?)?
           .method("size_mm".into(), lua.create_function(dummy_double)?)?
           .method("cursor".into(), lua.create_function(dummy)?)
}

impl_objectable!(Root, RootState);

fn dummy_double<'lua>(_: &'lua Lua, _: rlua::Value) -> rlua::Result<(i32, i32)> {
    Ok((0, 0))
}

/// Gets the wallpaper as a cairo surface or set it as a cairo pattern
fn wallpaper<'lua>(lua: &'lua Lua, pattern: Option<LightUserData>) -> rlua::Result<Value<'lua>> {
    // TODO FIXME Implement for realz
    if let Some(pattern) = pattern {
        // TODO Wrap before giving it to set_wallpaper
        let pattern = pattern.0 as *mut cairo_pattern_t;
        return set_wallpaper(lua, pattern)?.to_lua(lua)
    }
    // TODO Look it up in global conf (e.g probably super secret lua value)
    return Ok(Value::Nil)
}

fn set_wallpaper<'lua>(_: &'lua Lua, _pattern: *mut cairo_pattern_t) -> rlua::Result<bool> {
    warn!("Fake setting the wallpaper");
    Ok(true)
}

fn tags<'lua>(lua: &'lua Lua, _: ()) -> rlua::Result<Table<'lua>> {
    let table = lua.create_table()?;
    let activated_tags = lua.named_registry_value::<Table>(super::tag::TAG_LIST)?;
    for pair in activated_tags.pairs::<Value, Value>() {
        let (key, value) = pair?;
        table.set(key, value)?;
    }
    Ok(table)
}

/// Get or set global key bindings.
///
/// These bindings will be available when you press keys on the root window.
fn root_keys<'lua>(lua: &'lua Lua, key_array: rlua::Value) -> rlua::Result<rlua::Value<'lua>> {
    match key_array {
        // Set the global keys
        Value::Table(key_array) => {
            lua.set_named_registry_value(ROOT_KEYS_HANDLE, key_array)?;
            Ok(Value::Nil)
        }
        // Get the global keys
        Value::Nil => {
            // TODO Make a deep clone
            lua.globals().get(ROOT_KEYS_HANDLE)
        }
        v => {
            Err(rlua::Error::RuntimeError(format!("Expected nil or array \
                                                   of keys, got {:?}",
                                                  v)))
        }
    }
}

#[cfg(test)]
mod test {
    use super::super::root;
    use super::super::tag;
    use rlua::Lua;

    #[test]
    fn tags_none() {
        let lua = Lua::new();
        tag::init(&lua).unwrap();
        root::init(&lua).unwrap();
        lua.eval(
                 r#"
local t = root.tags()
assert(type(t) == "table")
assert(type(next(t)) == "nil")
"#,
                 None
        ).unwrap()
    }

    #[test]
    fn tags_does_not_copy() {
        let lua = Lua::new();
        tag::init(&lua).unwrap();
        root::init(&lua).unwrap();
        lua.eval(
                 r#"
local t = tag{ activated = true }
local t2 = root.tags()[1]
assert(t == t2)
t2.name = "Foo"
assert(t.name == "Foo")
"#,
                 None
        ).unwrap()
    }

    #[test]
    fn tags_some() {
        let lua = Lua::new();
        tag::init(&lua).unwrap();
        root::init(&lua).unwrap();
        lua.eval(
                 r#"
local first = tag{ activated = true }
local second = tag{ activated = true }
local t = root.tags()
assert(t[1] == first)
assert(t[2] == second)
"#,
                 None
        ).unwrap()
    }

    #[test]
    fn tags_removal() {
        let lua = Lua::new();
        tag::init(&lua).unwrap();
        root::init(&lua).unwrap();
        lua.eval(
                 r#"
local first = tag{ activated = true }
local second = tag{ activated = true }
first.activated = false
local t = root.tags()
assert(t[1] == second)
assert(type(t[2]) == "nil")
"#,
                 None
        ).unwrap()
    }
}
<commit_msg>Take deep clone of root keybindings getter<commit_after>//! TODO Fill in

use super::class::{Class, ClassBuilder};
use super::object::{self, Object, Objectable};
use cairo_sys::cairo_pattern_t;
use rlua::{self, LightUserData, Lua, Table, ToLua, UserData, UserDataMethods, Value};
use std::default::Default;
use std::fmt::{self, Display, Formatter};

/// Handle to the list of global key bindings
pub const ROOT_KEYS_HANDLE: &'static str = "__ROOT_KEYS";

#[derive(Clone, Debug)]
pub struct RootState {
    // TODO Fill in
    dummy: i32
}

pub struct Root<'lua>(Object<'lua>);

impl Default for RootState {
    fn default() -> Self {
        RootState { dummy: 0 }
    }
}

impl Display for RootState {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "Root: {:p}", self)
    }
}

impl<'lua> ToLua<'lua> for Root<'lua> {
    fn to_lua(self, lua: &'lua Lua) -> rlua::Result<Value<'lua>> {
        self.0.to_lua(lua)
    }
}

impl UserData for RootState {
    fn add_methods(methods: &mut UserDataMethods<Self>) {
        object::default_add_methods(methods);
    }
}

pub fn init(lua: &Lua) -> rlua::Result<Class> {
    // FIXME: In awesome there is no root class
    method_setup(lua, Class::builder(lua, "FIXME", None)?)?.save_class("root")?
                                                           .build()
}

fn method_setup<'lua>(lua: &'lua Lua,
                      builder: ClassBuilder<'lua>)
                      -> rlua::Result<ClassBuilder<'lua>> {
    // TODO Do properly
    use super::dummy;
    builder.method("connect_signal".into(), lua.create_function(dummy)?)?
           .method("buttons".into(), lua.create_function(dummy)?)?
           .method("wallpaper".into(), lua.create_function(wallpaper)?)?
           .method("tags".into(), lua.create_function(tags)?)?
           .method("keys".into(), lua.create_function(root_keys)?)?
           .method("size".into(), lua.create_function(dummy_double)?)?
           .method("size_mm".into(), lua.create_function(dummy_double)?)?
           .method("cursor".into(), lua.create_function(dummy)?)
}

impl_objectable!(Root, RootState);

fn dummy_double<'lua>(_: &'lua Lua, _: rlua::Value) -> rlua::Result<(i32, i32)> {
    Ok((0, 0))
}

/// Gets the wallpaper as a cairo surface or set it as a cairo pattern
fn wallpaper<'lua>(lua: &'lua Lua, pattern: Option<LightUserData>) -> rlua::Result<Value<'lua>> {
    // TODO FIXME Implement for realz
    if let Some(pattern) = pattern {
        // TODO Wrap before giving it to set_wallpaper
        let pattern = pattern.0 as *mut cairo_pattern_t;
        return set_wallpaper(lua, pattern)?.to_lua(lua)
    }
    // TODO Look it up in global conf (e.g probably super secret lua value)
    return Ok(Value::Nil)
}

fn set_wallpaper<'lua>(_: &'lua Lua, _pattern: *mut cairo_pattern_t) -> rlua::Result<bool> {
    warn!("Fake setting the wallpaper");
    Ok(true)
}

fn tags<'lua>(lua: &'lua Lua, _: ()) -> rlua::Result<Table<'lua>> {
    let table = lua.create_table()?;
    let activated_tags = lua.named_registry_value::<Table>(super::tag::TAG_LIST)?;
    for pair in activated_tags.pairs::<Value, Value>() {
        let (key, value) = pair?;
        table.set(key, value)?;
    }
    Ok(table)
}

/// Get or set global key bindings.
///
/// These bindings will be available when you press keys on the root window.
fn root_keys<'lua>(lua: &'lua Lua, key_array: rlua::Value) -> rlua::Result<rlua::Value<'lua>> {
    match key_array {
        // Set the global keys
        Value::Table(key_array) => {
            lua.set_named_registry_value(ROOT_KEYS_HANDLE, key_array)?;
            Ok(Value::Nil)
        }
        // Get the global keys
        Value::Nil => {
            // NOTE We make a deep clone so they can't modify references.
            let res = lua.create_table()?;
            for entry in lua.globals().get::<_, Table>(ROOT_KEYS_HANDLE)?.pairs() {
                let (key, value) = entry?;
                res.set::<Value, Value>(key, value)?;
            }
            Ok(Value::Table(res))
        }
        v => {
            Err(rlua::Error::RuntimeError(format!("Expected nil or array \
                                                   of keys, got {:?}",
                                                  v)))
        }
    }
}

#[cfg(test)]
mod test {
    use super::super::root;
    use super::super::tag;
    use rlua::Lua;

    #[test]
    fn tags_none() {
        let lua = Lua::new();
        tag::init(&lua).unwrap();
        root::init(&lua).unwrap();
        lua.eval(
                 r#"
local t = root.tags()
assert(type(t) == "table")
assert(type(next(t)) == "nil")
"#,
                 None
        ).unwrap()
    }

    #[test]
    fn tags_does_not_copy() {
        let lua = Lua::new();
        tag::init(&lua).unwrap();
        root::init(&lua).unwrap();
        lua.eval(
                 r#"
local t = tag{ activated = true }
local t2 = root.tags()[1]
assert(t == t2)
t2.name = "Foo"
assert(t.name == "Foo")
"#,
                 None
        ).unwrap()
    }

    #[test]
    fn tags_some() {
        let lua = Lua::new();
        tag::init(&lua).unwrap();
        root::init(&lua).unwrap();
        lua.eval(
                 r#"
local first = tag{ activated = true }
local second = tag{ activated = true }
local t = root.tags()
assert(t[1] == first)
assert(t[2] == second)
"#,
                 None
        ).unwrap()
    }

    #[test]
    fn tags_removal() {
        let lua = Lua::new();
        tag::init(&lua).unwrap();
        root::init(&lua).unwrap();
        lua.eval(
                 r#"
local first = tag{ activated = true }
local second = tag{ activated = true }
first.activated = false
local t = root.tags()
assert(t[1] == second)
assert(type(t[2]) == "nil")
"#,
                 None
        ).unwrap()
    }
}
<|endoftext|>


<commit_before>/// Lazy iterator over bits of a vector of bytes, starting with the LSB
/// (least-significat bit) of the first element of the vector.
pub struct BitIterator<'a> {
    object: &'a Vec<u8>,
    current_byte: uint,
    current_bit: uint
}

impl<'a> BitIterator<'a> {
    pub fn new<'a>(obj: &'a Vec<u8>) -> BitIterator {
        BitIterator { object: obj, current_byte: 0, current_bit: 0 }
    }
}

impl<'a> Iterator<u8> for BitIterator<'a> {
    fn next(&mut self) -> Option<u8> {
        let result = self.object[self.current_byte] >> self.current_bit & 0x1;

        if self.current_bit + 1 == ::std::u8::BITS {
            self.current_byte += 1;
        }
        self.current_bit = (self.current_bit + 1) % ::std::u8::BITS;

        if self.current_byte == self.object.len() {
            return None;
        } else {
            return Some(result);
        }
    }
}

#[test]
fn test_iterator() {
    let bytes = vec!(0xCA, 0xFE);
    let expected_bits = vec!(0,1,0,1, 0,0,1,1, 0,1,1,1, 1,1,1,1);
    let mut i = 0;

    for bit in BitIterator::new(&bytes) {
        println!("{} == {}", bit, expected_bits[i]);
        assert_eq!(bit, expected_bits[i]);
        i += 1;
    }
}
<commit_msg>Fix lifetime parameter shadowing.<commit_after>/// Lazy iterator over bits of a vector of bytes, starting with the LSB
/// (least-significat bit) of the first element of the vector.
pub struct BitIterator<'a> {
    object: &'a Vec<u8>,
    current_byte: uint,
    current_bit: uint
}

impl<'a> BitIterator<'a> {
    pub fn new(obj: &'a Vec<u8>) -> BitIterator {
        BitIterator { object: obj, current_byte: 0, current_bit: 0 }
    }
}

impl<'a> Iterator<u8> for BitIterator<'a> {
    fn next(&mut self) -> Option<u8> {
        let result = self.object[self.current_byte] >> self.current_bit & 0x1;

        if self.current_bit + 1 == ::std::u8::BITS {
            self.current_byte += 1;
        }
        self.current_bit = (self.current_bit + 1) % ::std::u8::BITS;

        if self.current_byte == self.object.len() {
            return None;
        } else {
            return Some(result);
        }
    }
}

#[test]
fn test_iterator() {
    let bytes = vec!(0xCA, 0xFE);
    let expected_bits = vec!(0,1,0,1, 0,0,1,1, 0,1,1,1, 1,1,1,1);
    let mut i = 0;

    for bit in BitIterator::new(&bytes) {
        println!("{} == {}", bit, expected_bits[i]);
        assert_eq!(bit, expected_bits[i]);
        i += 1;
    }
}
<|endoftext|>


<commit_before>//! Compute an SVG document's size with the legacy logic.
//!
//! See the documentation for [`LegacySize`](trait.LegacySize.html).  The legacy C API
//! functions like `rsvg_handle_render_cairo()` do not take a viewport argument: they do
//! not know how big the caller would like to render the document; instead they compute a
//! "natural size" for the document based on its `width`/`height`/`viewBox` and some
//! heuristics for when they are missing.
//!
//! The new style C functions like `rsvg_handle_render_document()` actually take a
//! viewport, which indicates how big the result should be.  This matches the expectations
//! of the web platform, which assumes that all embedded content goes into a viewport.

use crate::api::{CairoRenderer, IntrinsicDimensions, Length, RenderingError};
use float_cmp::approx_eq;

use super::handle::CairoRectangleExt;

/// Extension methods to compute the SVG's size suitable for the legacy C API.
///
/// The legacy C API can compute an SVG document's size from the
/// `width`, `height`, and `viewBox` attributes of the toplevel `<svg>`
/// element.  If these are not available, then the size must be computed
/// by actually measuring the geometries of elements in the document.
///
/// See https://www.w3.org/TR/css-images-3/#sizing-terms for terminology and logic.
pub trait LegacySize {
    fn legacy_document_size(&self) -> Result<(f64, f64), RenderingError> {
        let (ink_r, _) = self.legacy_layer_geometry(None)?;
        Ok((ink_r.width, ink_r.height))
    }

    fn legacy_layer_geometry(
        &self,
        id: Option<&str>,
    ) -> Result<(cairo::Rectangle, cairo::Rectangle), RenderingError>;
}

impl<'a> LegacySize for CairoRenderer<'a> {
    fn legacy_layer_geometry(
        &self,
        id: Option<&str>,
    ) -> Result<(cairo::Rectangle, cairo::Rectangle), RenderingError> {
        match id {
            Some(id) => Ok(self.geometry_for_layer(Some(id), &unit_rectangle())?),

            None => {
                let size_from_intrinsic_dimensions =
                    self.intrinsic_size_in_pixels().or_else(|| {
                        size_in_pixels_from_percentage_width_and_height(
                            &self.intrinsic_dimensions(),
                        )
                    });

                if let Some((w, h)) = size_from_intrinsic_dimensions {
                    // We have a size directly computed from the <svg> attributes
                    let rect = cairo::Rectangle::from_size(w, h);
                    Ok((rect, rect))
                } else {
                    self.geometry_for_layer(None, &unit_rectangle())
                }
            }
        }
    }
}

fn unit_rectangle() -> cairo::Rectangle {
    cairo::Rectangle::from_size(1.0, 1.0)
}

/// If the width and height are in percentage units, computes a size equal to the
/// `viewBox`'s aspect ratio if it exists, or else returns None.
///
/// For example, a `viewBox="0 0 100 200"` will yield `Some(100.0, 200.0)`.
///
/// Note that this only checks that the width and height are in percentage units, but
/// it actually ignores their values.  This is because at the point this function is
/// called, there is no viewport to embed the SVG document in, so those percentage
/// units cannot be resolved against anything in particular.  The idea is to return
/// some dimensions with the correct aspect ratio.
fn size_in_pixels_from_percentage_width_and_height(
    dim: &IntrinsicDimensions,
) -> Option<(f64, f64)> {
    let IntrinsicDimensions {
        width,
        height,
        vbox,
    } = *dim;

    use crate::api::LengthUnit::*;

    // If both width and height are 100%, just use the vbox size as a pixel size.
    // This gives a size with the correct aspect ratio.

    match (width, height, vbox) {
        (None, None, Some(vbox)) => Some((vbox.width, vbox.height)),

        (
            Some(Length {
                length: w,
                unit: Percent,
            }),
            Some(Length {
                length: h,
                unit: Percent,
            }),
            Some(vbox),
        ) if approx_eq!(f64, w, 1.0) && approx_eq!(f64, h, 1.0) => Some((vbox.width, vbox.height)),

        _ => None,
    }
}
<commit_msg>c_api/sizing.rs: links to doc items by name<commit_after>//! Compute an SVG document's size with the legacy logic.
//!
//! See the documentation for [`LegacySize`].  The legacy C API functions like
//! `rsvg_handle_render_cairo()` do not take a viewport argument: they do not know how big
//! the caller would like to render the document; instead they compute a "natural size"
//! for the document based on its `width`/`height`/`viewBox` and some heuristics for when
//! they are missing.
//!
//! The new style C functions like `rsvg_handle_render_document()` actually take a
//! viewport, which indicates how big the result should be.  This matches the expectations
//! of the web platform, which assumes that all embedded content goes into a viewport.

use crate::api::{CairoRenderer, IntrinsicDimensions, Length, RenderingError};
use float_cmp::approx_eq;

use super::handle::CairoRectangleExt;

/// Extension methods to compute the SVG's size suitable for the legacy C API.
///
/// The legacy C API can compute an SVG document's size from the
/// `width`, `height`, and `viewBox` attributes of the toplevel `<svg>`
/// element.  If these are not available, then the size must be computed
/// by actually measuring the geometries of elements in the document.
///
/// See https://www.w3.org/TR/css-images-3/#sizing-terms for terminology and logic.
pub trait LegacySize {
    fn legacy_document_size(&self) -> Result<(f64, f64), RenderingError> {
        let (ink_r, _) = self.legacy_layer_geometry(None)?;
        Ok((ink_r.width, ink_r.height))
    }

    fn legacy_layer_geometry(
        &self,
        id: Option<&str>,
    ) -> Result<(cairo::Rectangle, cairo::Rectangle), RenderingError>;
}

impl<'a> LegacySize for CairoRenderer<'a> {
    fn legacy_layer_geometry(
        &self,
        id: Option<&str>,
    ) -> Result<(cairo::Rectangle, cairo::Rectangle), RenderingError> {
        match id {
            Some(id) => Ok(self.geometry_for_layer(Some(id), &unit_rectangle())?),

            None => {
                let size_from_intrinsic_dimensions =
                    self.intrinsic_size_in_pixels().or_else(|| {
                        size_in_pixels_from_percentage_width_and_height(
                            &self.intrinsic_dimensions(),
                        )
                    });

                if let Some((w, h)) = size_from_intrinsic_dimensions {
                    // We have a size directly computed from the <svg> attributes
                    let rect = cairo::Rectangle::from_size(w, h);
                    Ok((rect, rect))
                } else {
                    self.geometry_for_layer(None, &unit_rectangle())
                }
            }
        }
    }
}

fn unit_rectangle() -> cairo::Rectangle {
    cairo::Rectangle::from_size(1.0, 1.0)
}

/// If the width and height are in percentage units, computes a size equal to the
/// `viewBox`'s aspect ratio if it exists, or else returns None.
///
/// For example, a `viewBox="0 0 100 200"` will yield `Some(100.0, 200.0)`.
///
/// Note that this only checks that the width and height are in percentage units, but
/// it actually ignores their values.  This is because at the point this function is
/// called, there is no viewport to embed the SVG document in, so those percentage
/// units cannot be resolved against anything in particular.  The idea is to return
/// some dimensions with the correct aspect ratio.
fn size_in_pixels_from_percentage_width_and_height(
    dim: &IntrinsicDimensions,
) -> Option<(f64, f64)> {
    let IntrinsicDimensions {
        width,
        height,
        vbox,
    } = *dim;

    use crate::api::LengthUnit::*;

    // If both width and height are 100%, just use the vbox size as a pixel size.
    // This gives a size with the correct aspect ratio.

    match (width, height, vbox) {
        (None, None, Some(vbox)) => Some((vbox.width, vbox.height)),

        (
            Some(Length {
                length: w,
                unit: Percent,
            }),
            Some(Length {
                length: h,
                unit: Percent,
            }),
            Some(vbox),
        ) if approx_eq!(f64, w, 1.0) && approx_eq!(f64, h, 1.0) => Some((vbox.width, vbox.height)),

        _ => None,
    }
}
<|endoftext|>