<commit_before>//! A wrapper around the uchardet library. Detects character encodings.
//!
//! Note that the underlying implemention is written in C and C++, and I'm
//! not aware of any security audits which have been performed against it.
//!
//! ```
//! use uchardet::detect_encoding_name;
//!
//! assert_eq!(Some("ISO-8859-1".to_string()),
//!            detect_encoding_name(&[0x46, 0x72, 0x61, 0x6e, 0xe7, 0x6f, 0x69, 0x73, 0xe9]).unwrap());
//! ```
//!
//! For more information, see [this project on
//! GitHub](https://github.com/emk/rust-uchardet).

#![deny(missing_docs)]

extern crate libc;
extern crate uchardet_sys as ffi;

use libc::size_t;
use std::error::Error;
use std::fmt;
use std::result::Result;
use std::ffi::CStr;
use std::str::from_utf8;

/// An error occurred while trying to detect the character encoding.
#[derive(Debug)]
pub struct EncodingDetectorError {
    message: String
}

impl Error for EncodingDetectorError {
    fn description(&self) -> &str { "encoding detector error" }
    fn cause(&self) -> Option<&Error> { None }
}

impl fmt::Display for EncodingDetectorError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", &self.message)
    }
}

/// Either a return value, or an encoding detection error.
pub type EncodingDetectorResult<T> = Result<T, EncodingDetectorError>;

/// Detects the encoding of text using the uchardet library.
///
/// EXPERIMENTAL: This may be replaced by a better API soon.
struct EncodingDetector {
    ptr: ffi::uchardet_t
}

/// Return the name of the charset used in `data`, or `None` if the
/// charset if the encoding can't be detected. This is
/// the value returned by the underlying `uchardet` library, with
/// the empty string mapped to `None`.
///
/// ```
/// use uchardet::detect_encoding_name;
///
/// assert_eq!(Some("ASCII".to_string()),
///            detect_encoding_name("ascii".as_bytes()).unwrap());
/// assert_eq!(Some("UTF-8".to_string()),
///            detect_encoding_name("©français".as_bytes()).unwrap());
/// assert_eq!(Some("ISO-8859-1".to_string()),
///            detect_encoding_name(&[0x46, 0x72, 0x61, 0x6e, 0xe7, 0x6f, 0x69, 0x73, 0xe9]).unwrap());

/// ```
pub fn detect_encoding_name(data: &[u8]) ->
    EncodingDetectorResult<Option<String>>
{
    let mut detector = EncodingDetector::new();
    try!(detector.handle_data(data));
    detector.data_end();
    Ok(detector.charset())
}

impl EncodingDetector {
    /// Create a new EncodingDetector.
    fn new() -> EncodingDetector {
        let ptr = unsafe { ffi::uchardet_new() };
        assert!(!ptr.is_null());
        EncodingDetector{ptr: ptr}
    }

    /// Pass a chunk of raw bytes to the detector. This is a no-op if a
    /// charset has been detected.
    fn handle_data(&mut self, data: &[u8]) -> EncodingDetectorResult<()> {
        let result = unsafe {
            ffi::uchardet_handle_data(self.ptr, data.as_ptr() as *const i8,
                                      data.len() as size_t)
        };
        match result {
            0 => Ok(()),
            _ => {
                let msg =  "Error handling data".to_string();
                Err(EncodingDetectorError{message: msg})
            }
        }
    }

    /// Notify the detector that we're done calling `handle_data`, and that
    /// we want it to make a guess as to our encoding. This is a no-op if
    /// no data has been passed yet, or if an encoding has been detected
    /// for certain. From reading the code, it appears that you can safely
    /// call `handle_data` after calling this, but I'm not certain.
    fn data_end(&mut self) {
        unsafe { ffi::uchardet_data_end(self.ptr); }
    }

    /// Reset the detector's internal state.
    //fn reset(&mut self) {
    //    unsafe { ffi::uchardet_reset(self.ptr); }
    //}

    /// Get the decoder's current best guess as to the encoding. Returns
    /// `None` on error, or if the data appears to be ASCII.
    fn charset(&self) -> Option<String> {
        unsafe {
            let internal_str = ffi::uchardet_get_charset(self.ptr);
            assert!(!internal_str.is_null());
            let bytes = CStr::from_ptr(internal_str).to_bytes();
            let charset = from_utf8(bytes);
            match charset {
                Err(_) =>
                    panic!("uchardet_get_charset returned invalid value"),
                Ok("") => None,
                Ok(encoding) => Some(encoding.to_string())
            }
        }
    }
}

impl Drop for EncodingDetector {
    fn drop(&mut self) {
        unsafe { ffi::uchardet_delete(self.ptr) };
    }
}
<commit_msg>Remove unnecessary Option around the charset name<commit_after>//! A wrapper around the uchardet library. Detects character encodings.
//!
//! Note that the underlying implemention is written in C and C++, and I'm
//! not aware of any security audits which have been performed against it.
//!
//! ```
//! use uchardet::detect_encoding_name;
//!
//! assert_eq!(Ok("ISO-8859-1".to_string()),
//!            detect_encoding_name(&[0x46, 0x72, 0x61, 0x6e, 0xe7, 0x6f, 0x69, 0x73, 0xe9]));
//! ```
//!
//! For more information, see [this project on
//! GitHub](https://github.com/emk/rust-uchardet).

#![deny(missing_docs)]

extern crate libc;
extern crate uchardet_sys as ffi;

use libc::size_t;
use std::error::Error;
use std::fmt;
use std::result::Result;
use std::ffi::CStr;
use std::str::from_utf8;

/// An error occurred while trying to detect the character encoding.
#[derive(Debug, PartialEq)]
pub struct EncodingDetectorError {
    message: String
}

impl Error for EncodingDetectorError {
    fn description(&self) -> &str { "encoding detector error" }
    fn cause(&self) -> Option<&Error> { None }
}

impl fmt::Display for EncodingDetectorError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", &self.message)
    }
}

/// Either a return value, or an encoding detection error.
pub type EncodingDetectorResult<T> = Result<T, EncodingDetectorError>;

/// Detects the encoding of text using the uchardet library.
///
/// EXPERIMENTAL: This may be replaced by a better API soon.
struct EncodingDetector {
    ptr: ffi::uchardet_t
}

/// Return the name of the charset used in `data`, or `None` if the
/// charset if the encoding can't be detected. This is
/// the value returned by the underlying `uchardet` library, with
/// the empty string mapped to `None`.
///
/// ```
/// use uchardet::detect_encoding_name;
///
/// assert_eq!(Ok("ASCII".to_string()),
///            detect_encoding_name("ascii".as_bytes()));
/// assert_eq!(Ok("UTF-8".to_string()),
///            detect_encoding_name("©français".as_bytes()));
/// assert_eq!(Ok("ISO-8859-1".to_string()),
///            detect_encoding_name(&[0x46, 0x72, 0x61, 0x6e, 0xe7, 0x6f, 0x69, 0x73, 0xe9]));

/// ```
pub fn detect_encoding_name(data: &[u8]) ->
    EncodingDetectorResult<String>
{
    let mut detector = EncodingDetector::new();
    try!(detector.handle_data(data));
    detector.data_end();
    detector.charset()
}

impl EncodingDetector {
    /// Create a new EncodingDetector.
    fn new() -> EncodingDetector {
        let ptr = unsafe { ffi::uchardet_new() };
        assert!(!ptr.is_null());
        EncodingDetector{ptr: ptr}
    }

    /// Pass a chunk of raw bytes to the detector. This is a no-op if a
    /// charset has been detected.
    fn handle_data(&mut self, data: &[u8]) -> EncodingDetectorResult<()> {
        let result = unsafe {
            ffi::uchardet_handle_data(self.ptr, data.as_ptr() as *const i8,
                                      data.len() as size_t)
        };
        match result {
            0 => Ok(()),
            _ => {
                let msg =  "Error handling data".to_string();
                Err(EncodingDetectorError{message: msg})
            }
        }
    }

    /// Notify the detector that we're done calling `handle_data`, and that
    /// we want it to make a guess as to our encoding. This is a no-op if
    /// no data has been passed yet, or if an encoding has been detected
    /// for certain. From reading the code, it appears that you can safely
    /// call `handle_data` after calling this, but I'm not certain.
    fn data_end(&mut self) {
        unsafe { ffi::uchardet_data_end(self.ptr); }
    }

    /// Reset the detector's internal state.
    //fn reset(&mut self) {
    //    unsafe { ffi::uchardet_reset(self.ptr); }
    //}

    /// Get the decoder's current best guess as to the encoding. Returns
    /// `None` on error, or if the data appears to be ASCII.
    fn charset(&self) -> Result<String, EncodingDetectorError> {
        unsafe {
            let internal_str = ffi::uchardet_get_charset(self.ptr);
            assert!(!internal_str.is_null());
            let bytes = CStr::from_ptr(internal_str).to_bytes();
            let charset = from_utf8(bytes);
            match charset {
                Err(_) =>
                    panic!("uchardet_get_charset returned invalid value"),
                Ok("") => Err(EncodingDetectorError {
                    message: "uchardet failed to recognize a charset".to_string()
                }),
                Ok(encoding) => Ok(encoding.to_string())
            }
        }
    }
}

impl Drop for EncodingDetector {
    fn drop(&mut self) {
        unsafe { ffi::uchardet_delete(self.ptr) };
    }
}
<|endoftext|>


<commit_before>//! Helper crate around git2 with functions for common tasks related to git
//! repositories.

#![deny(missing_docs)]
#![warn(rust_2018_idioms)]

pub mod error;

pub use crate::error::Error;

use std::path::Path;

use git2::{
    self,
    Repository,
};
use walkdir::{
    DirEntry,
    WalkDir,
};

/// Commit current stage with given commit message.
pub fn commit<P: AsRef<Path>>(repo_path: P, message: &str) -> Result<(), Error> {
    let repository = Repository::open(&repo_path).map_err(Error::RepositoryOpen)?;
    let mut index = repository.index().map_err(Error::IndexOpen)?;
    let oid = index.write_tree().map_err(Error::IndexWriteTree)?;

    let mut parent_commit = vec![];
    if let Ok(head) = repository.head() {
        if let Ok(head_commit) = head.peel_to_commit() {
            parent_commit.push(head_commit);
        };
    };

    let tree = repository
        .find_tree(oid)
        .map_err(Error::RepositoryFindTree)?;

    let signature = repository.signature().map_err(Error::RepositorySignature)?;

    repository
        .commit(
            Some("HEAD"),
            &signature,
            &signature,
            message,
            &tree,
            &parent_commit.iter().collect::<Vec<_>>(),
        )
        .map_err(Error::RepositoryCommit)?;

    Ok(())
}

/// Create a new nonbare git repository in the given path.
pub fn init<P: AsRef<Path>>(repo_path: P) -> Result<(), Error> {
    let _ = Repository::init(&repo_path).map_err(Error::RepositoryInit)?;

    Ok(())
}

/// Stage given paths in the repository. Paths have to be relative to the
/// repo_path.
pub fn stage<P: AsRef<Path>, F: AsRef<Path>>(repo_path: P, paths: &[F]) -> Result<(), Error> {
    let repository = Repository::open(&repo_path).map_err(Error::RepositoryOpen)?;
    let mut index = repository.index().map_err(Error::IndexOpen)?;

    paths
        .iter()
        .map(|path| index.add_path(path.as_ref()).map_err(Error::IndexAddPath))
        .collect::<Result<_, Error>>()?;

    index.write().map_err(Error::IndexWrite)?;

    Ok(())
}

/// Stage all paths in the repository.
pub fn stage_all<P: AsRef<Path>>(repo_path: P) -> Result<(), Error> {
    fn is_not_hidden(entry: &DirEntry) -> bool {
        entry
            .file_name()
            .to_str()
            .map(|s| entry.depth() == 0 || !s.starts_with('.'))
            .unwrap_or(false)
    };

    let paths = WalkDir::new(&repo_path)
        .into_iter()
        .filter_entry(|entry| is_not_hidden(entry))
        .filter_map(|v| v.ok())
        .filter(|entry| entry.path() != repo_path.as_ref())
        .map(|entry| {
            entry
                .into_path()
                .strip_prefix(&repo_path)
                .map(|path| path.to_path_buf())
                .map_err(Error::StripRepositoryPrefix)
        })
        .collect::<Result<Vec<_>, _>>()?;

    stage(repo_path, &paths)?;

    Ok(())
}

/// Return status of the repository.
pub fn status<P: AsRef<Path>>(_repo_path: P) -> Result<(), Error> {
    unimplemented!()
}

#[cfg(test)]
mod test {
    use std::{
        fs,
        path::PathBuf,
    };
    use tempfile::tempdir;

    #[test]
    fn init() {
        let dir = tempdir().unwrap();
        super::init(&dir).unwrap();
    }

    #[test]
    fn stage_nonexistent_file() {
        let dir = tempdir().unwrap();
        super::init(&dir).unwrap();

        let files = vec![PathBuf::from("file_does_not_exist")];
        super::stage(&dir, &files).unwrap();
    }

    #[test]
    fn stage_existing_new_file() {
        let dir = tempdir().unwrap();
        super::init(&dir).unwrap();

        let first_file = dir.path().join("first_file");
        fs::write(&first_file, "first data").unwrap();

        let files = vec!["first_file"];
        super::stage(&dir, &files).unwrap();
    }

    #[test]
    fn commit_file_new_repo() {
        let dir = tempdir().unwrap();
        super::init(&dir).unwrap();

        let first_file = dir.path().join("first_file");
        fs::write(&first_file, "first data").unwrap();

        let files = vec!["first_file"];
        super::stage(&dir, &files).unwrap();

        super::commit(&dir, "Added first_file").unwrap();
    }

    #[test]
    fn commit_file_existing_repo() {
        let dir = tempdir().unwrap();
        super::init(&dir).unwrap();

        let first_file = dir.path().join("first_file");
        fs::write(&first_file, "first data").unwrap();

        let files = vec!["first_file"];
        super::stage(&dir, &files).unwrap();

        super::commit(&dir, "Added first_file").unwrap();

        let second_file = dir.path().join("second_file");
        fs::write(&second_file, "second data").unwrap();

        let files = vec!["second_file"];
        super::stage(&dir, &files).unwrap();

        super::commit(&dir, "Added second_file").unwrap();
    }

    #[test]
    fn commit_files_new_repo() {
        let dir = tempdir().unwrap();
        super::init(&dir).unwrap();

        let files = vec!["first_file", "second_file", "third_file"];
        for file in &files {
            let path = dir.path().join(file);
            fs::write(&path, file).unwrap();
        }

        super::stage(&dir, &files).unwrap();
        super::commit(&dir, "Added files").unwrap();
    }

    #[test]
    fn stage_all_files() {
        // let dir = tempdir().unwrap();
        let dir = PathBuf::from("/tmp/blasdasd");
        super::init(&dir).unwrap();

        let files = vec!["first_file", "second_file", "third_file"];
        for file in &files {
            let path = dir.as_path().join(file);
            fs::write(&path, file).unwrap();
        }

        super::stage_all(&dir).unwrap();
    }

    #[test]
    fn commit_all_files() {
        // let dir = tempdir().unwrap();
        let dir = PathBuf::from("/tmp/blasdasd");
        super::init(&dir).unwrap();

        let files = vec!["first_file", "second_file", "third_file"];
        for file in &files {
            let path = dir.as_path().join(file);
            fs::write(&path, file).unwrap();
        }

        super::stage_all(&dir).unwrap();
        super::commit(&dir, "added all files").unwrap();
    }

    #[test]
    fn status_not_a_repository() {
        let dir = tempdir().unwrap();
        super::status(&dir).unwrap();
    }
}
<commit_msg>Fixed staging of directories<commit_after>//! Helper crate around git2 with functions for common tasks related to git
//! repositories.

#![deny(missing_docs)]
#![warn(rust_2018_idioms)]

pub mod error;

pub use crate::error::Error;

use std::path::Path;

use git2::{
    self,
    Repository,
};
use walkdir::{
    DirEntry,
    WalkDir,
};

/// Commit current stage with given commit message.
pub fn commit<P: AsRef<Path>>(repo_path: P, message: &str) -> Result<(), Error> {
    let repository = Repository::open(&repo_path).map_err(Error::RepositoryOpen)?;
    let mut index = repository.index().map_err(Error::IndexOpen)?;
    let oid = index.write_tree().map_err(Error::IndexWriteTree)?;

    let mut parent_commit = vec![];
    if let Ok(head) = repository.head() {
        if let Ok(head_commit) = head.peel_to_commit() {
            parent_commit.push(head_commit);
        };
    };

    let tree = repository
        .find_tree(oid)
        .map_err(Error::RepositoryFindTree)?;

    let signature = repository.signature().map_err(Error::RepositorySignature)?;

    repository
        .commit(
            Some("HEAD"),
            &signature,
            &signature,
            message,
            &tree,
            &parent_commit.iter().collect::<Vec<_>>(),
        )
        .map_err(Error::RepositoryCommit)?;

    Ok(())
}

/// Create a new nonbare git repository in the given path.
pub fn init<P: AsRef<Path>>(repo_path: P) -> Result<(), Error> {
    let _ = Repository::init(&repo_path).map_err(Error::RepositoryInit)?;

    Ok(())
}

/// Stage given paths in the repository. Paths have to be relative to the
/// repo_path.
pub fn stage<P: AsRef<Path>, F: AsRef<Path>>(repo_path: P, paths: &[F]) -> Result<(), Error> {
    let repository = Repository::open(&repo_path).map_err(Error::RepositoryOpen)?;
    let mut index = repository.index().map_err(Error::IndexOpen)?;

    paths
        .iter()
        .filter(|path| {
            let fullpath = repo_path.as_ref().join(path);
            fullpath.exists() && fullpath.is_file()
        })
        .map(|path| index.add_path(path.as_ref()).map_err(Error::IndexAddPath))
        .collect::<Result<_, Error>>()?;

    index.write().map_err(Error::IndexWrite)?;

    Ok(())
}

/// Stage all paths in the repository.
pub fn stage_all<P: AsRef<Path>>(repo_path: P) -> Result<(), Error> {
    fn is_not_hidden(entry: &DirEntry) -> bool {
        entry
            .file_name()
            .to_str()
            .map(|s| entry.depth() == 0 || !s.starts_with('.'))
            .unwrap_or(false)
    };

    let paths = WalkDir::new(&repo_path)
        .into_iter()
        .filter_entry(|entry| is_not_hidden(entry))
        .filter_map(|v| v.ok())
        .filter(|entry| entry.path() != repo_path.as_ref())
        .map(|entry| {
            entry
                .into_path()
                .strip_prefix(&repo_path)
                .map(|path| path.to_path_buf())
                .map_err(Error::StripRepositoryPrefix)
        })
        .collect::<Result<Vec<_>, _>>()?;

    stage(repo_path, &paths)?;

    Ok(())
}

/// Return status of the repository.
pub fn status<P: AsRef<Path>>(_repo_path: P) -> Result<(), Error> {
    unimplemented!()
}

#[cfg(test)]
mod test {
    use std::{
        fs,
        path::PathBuf,
    };
    use tempfile::tempdir;

    #[test]
    fn init() {
        let dir = tempdir().unwrap();
        super::init(&dir).unwrap();
    }

    #[test]
    fn stage_nonexistent_file() {
        let dir = tempdir().unwrap();
        super::init(&dir).unwrap();

        let files = vec![PathBuf::from("file_does_not_exist")];
        super::stage(&dir, &files).unwrap();
    }

    #[test]
    fn stage_directory() {
        let dir = tempdir().unwrap();
        super::init(&dir).unwrap();

        let subdir = dir.path().join("subdir");
        std::fs::create_dir_all(&subdir).unwrap();

        let files = vec!["subdir"];
        super::stage(&dir, &files).unwrap();
    }

    #[test]
    fn stage_directory_with_subfile() {
        let dir = tempdir().unwrap();
        super::init(&dir).unwrap();

        let subdir = dir.path().join("subdir");
        std::fs::create_dir_all(&subdir).unwrap();

        let subfile = subdir.join("subfile");
        fs::write(&subfile, "first data").unwrap();

        let files = vec!["subdir", "subdir/subfile"];
        super::stage(&dir, &files).unwrap();
    }

    #[test]
    fn stage_existing_new_file() {
        let dir = tempdir().unwrap();
        super::init(&dir).unwrap();

        let first_file = dir.path().join("first_file");
        fs::write(&first_file, "first data").unwrap();

        let files = vec!["first_file"];
        super::stage(&dir, &files).unwrap();
    }

    #[test]
    fn commit_file_new_repo() {
        let dir = tempdir().unwrap();
        super::init(&dir).unwrap();

        let first_file = dir.path().join("first_file");
        fs::write(&first_file, "first data").unwrap();

        let files = vec!["first_file"];
        super::stage(&dir, &files).unwrap();

        super::commit(&dir, "Added first_file").unwrap();
    }

    #[test]
    fn commit_file_existing_repo() {
        let dir = tempdir().unwrap();
        super::init(&dir).unwrap();

        let first_file = dir.path().join("first_file");
        fs::write(&first_file, "first data").unwrap();

        let files = vec!["first_file"];
        super::stage(&dir, &files).unwrap();

        super::commit(&dir, "Added first_file").unwrap();

        let second_file = dir.path().join("second_file");
        fs::write(&second_file, "second data").unwrap();

        let files = vec!["second_file"];
        super::stage(&dir, &files).unwrap();

        super::commit(&dir, "Added second_file").unwrap();
    }

    #[test]
    fn commit_files_new_repo() {
        let dir = tempdir().unwrap();
        super::init(&dir).unwrap();

        let files = vec!["first_file", "second_file", "third_file"];
        for file in &files {
            let path = dir.path().join(file);
            fs::write(&path, file).unwrap();
        }

        super::stage(&dir, &files).unwrap();
        super::commit(&dir, "Added files").unwrap();
    }

    #[test]
    fn stage_all_files() {
        let dir = tempdir().unwrap();
        super::init(&dir).unwrap();

        let files = vec!["first_file", "second_file", "third_file"];
        for file in &files {
            let path = dir.path().join(file);
            fs::write(&path, file).unwrap();
        }

        super::stage_all(&dir).unwrap();
    }

    #[test]
    fn commit_all_files() {
        let dir = tempdir().unwrap();
        super::init(&dir).unwrap();

        let files = vec!["first_file", "second_file", "third_file"];
        for file in &files {
            let path = dir.path().join(file);
            fs::write(&path, file).unwrap();
        }

        super::stage_all(&dir).unwrap();
        super::commit(&dir, "added all files").unwrap();
    }

    #[test]
    #[should_panic]
    fn status_not_a_repository() {
        let dir = tempdir().unwrap();
        super::status(&dir).unwrap();
    }
}
<|endoftext|>


<commit_before>// Copyright 2017 Dmitry Tantsur <divius.inside@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     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.

//! OpenStack Client in Rust.
//!
//! The goal of this project is to provide a simple API for working with
//! OpenStack clouds.
//!
//! # Features
//!
//! * [Authentication](auth/index.html)
//! * Basic [compute API support](compute/index.html)

#![crate_name = "openstack"]
#![crate_type = "lib"]
// NOTE: we do not use generic deny(warnings) to avoid breakages with new
// versions of the compiler. Add more warnings here as you discover them.
// Taken from https://github.com/rust-unofficial/patterns/
#![deny(const_err,
        dead_code,
        extra_requirement_in_impl,
        improper_ctypes,
        legacy_directory_ownership,
        missing_copy_implementations,
        missing_debug_implementations,
        missing_docs,
        non_shorthand_field_patterns,
        no_mangle_generic_items,
        overflowing_literals,
        path_statements ,
        patterns_in_fns_without_body,
        plugin_as_library,
        private_in_public,
        private_no_mangle_fns,
        private_no_mangle_statics,
        safe_extern_statics,
        trivial_casts,
        trivial_numeric_casts,
        unconditional_recursion,
        unions_with_drop_fields,
        unsafe_code,
        unused,
        unused_allocation,
        unused_comparisons,
        unused_extern_crates,
        unused_import_braces,
        unused_parens,
        unused_qualifications,
        unused_results,
        while_true)]

extern crate chrono;
#[macro_use]
extern crate hyper;
#[cfg(feature = "tls")]
extern crate hyper_rustls;
#[macro_use]
extern crate log;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;

#[cfg(test)] #[macro_use]
extern crate yup_hyper_mock;

pub mod auth;
mod common;
#[cfg(feature = "compute")]
pub mod compute;
mod identity;
pub mod service;
mod session;
mod utils;

pub use common::ApiError;
pub use common::ApiResult;
pub use common::ApiVersion;
pub use common::ApiVersionRequest;
pub use common::Sort;
pub use session::Session;
<commit_msg>fix: only use chrono crate if compute is enabled (will be lifted later)<commit_after>// Copyright 2017 Dmitry Tantsur <divius.inside@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     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.

//! OpenStack Client in Rust.
//!
//! The goal of this project is to provide a simple API for working with
//! OpenStack clouds.
//!
//! # Features
//!
//! * [Authentication](auth/index.html)
//! * Basic [compute API support](compute/index.html)

#![crate_name = "openstack"]
#![crate_type = "lib"]
// NOTE: we do not use generic deny(warnings) to avoid breakages with new
// versions of the compiler. Add more warnings here as you discover them.
// Taken from https://github.com/rust-unofficial/patterns/
#![deny(const_err,
        dead_code,
        extra_requirement_in_impl,
        improper_ctypes,
        legacy_directory_ownership,
        missing_copy_implementations,
        missing_debug_implementations,
        missing_docs,
        non_shorthand_field_patterns,
        no_mangle_generic_items,
        overflowing_literals,
        path_statements ,
        patterns_in_fns_without_body,
        plugin_as_library,
        private_in_public,
        private_no_mangle_fns,
        private_no_mangle_statics,
        safe_extern_statics,
        trivial_casts,
        trivial_numeric_casts,
        unconditional_recursion,
        unions_with_drop_fields,
        unsafe_code,
        unused,
        unused_allocation,
        unused_comparisons,
        unused_extern_crates,
        unused_import_braces,
        unused_parens,
        unused_qualifications,
        unused_results,
        while_true)]

#[cfg(feature = "compute")]
extern crate chrono;
#[macro_use]
extern crate hyper;
#[cfg(feature = "tls")]
extern crate hyper_rustls;
#[macro_use]
extern crate log;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;

#[cfg(test)] #[macro_use]
extern crate yup_hyper_mock;

pub mod auth;
mod common;
#[cfg(feature = "compute")]
pub mod compute;
mod identity;
pub mod service;
mod session;
mod utils;

pub use common::ApiError;
pub use common::ApiResult;
pub use common::ApiVersion;
pub use common::ApiVersionRequest;
pub use common::Sort;
pub use session::Session;
<|endoftext|>


<commit_before>// Copyright 2017 Jeremy Wall <jeremy@marzhillstudios.com>
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//      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.
// #![feature(trace_macros,log_syntax)]

//! # ucg, A universal configuration grammar.
//!
//! Ucg defines a common grammar for describing a collection of configuration values.
//! ucg allows you to specify configuration values with a syntax that that is immutable,
//! comoposable with copy-on-write semantics, and safe.
//!
//! ## Example
//!
//! ```ucg
//! // named bindings
//! let host = "mysql.internal.net";
//! let port = 8080
//!
//! // format strings
//! let connstr = "mysql://@:@" % (host, port);
//!
//! // tuples
//! let dbconf = {
//!     connstr = connstr,
//!     database = "mydb",
//!     // lists
//!     tables = ["posts", "comments", "users"],
//! };
//! ```
//!
//! ## Syntax
//!
//! ucg is a safe language with type inference that tries to guarantee that it will halt.
//! A valid ucg file is composesed of a series of statements. Statements are any valid
//! ucg expression terminated by a semicolon.
//!
//! ### Reserved words
//!
//! The following words are reserved in ucg and can't be used as named bindings.
//!
//! * true
//! * false
//! * let
//! * import
//! * as
//! * select
//! * macro
//! * env
//! * map
//! * filter
//! * NULL
//!
//! ### Primitive types
//!
//! ucg has a relatively simple syntax with a few primitive types, Null, Boolean, Int, Float, and String.
//!
//! ### Boolean
//!
//! A Boolean is either `true` or `false`.
//!
//! ```uct
//! true;
//! false;
//! ```
//!
//! #### Int
//!
//! An Int is any integer number.
//!
//! ```ucg
//! 1; // a single Integer
//! ```
//!
//! #### Float
//!
//! A Float is any number with a decimal point.
//!
//! ```ucg
//! 1.0; // A typical float.
//! 1. // You can leave off the 0 after the decimal point.
//! .1 // the leading 0 is also optional.
//! ```
//!
//! #### String
//!
//! A String is any quoted text. Backslashes within a string escape the next preceding
//! character.
//!
//! ``` ucg
//! "foo"; // a simple string
//! "I'm a \"fine\" looking string"; // escaped quotes in a string.
//! ```
//!
//! #### NULL or the Empty type.
//!
//!  A NULL is an empty type. It represents no value.
//!
//! ```ucg
//! let empty = NULL;
//! ```
//!
//! ### Complex types
//!
//! ucg has two complex data types, Lists and Tuples.
//!
//! #### Lists
//!
//! Lists are surrounded with square brackets `[ ]` and have comma separated elements.
//!
//! ```ucg
//! [1, 2, 3]; // A simple list of numbers.
//!
//! [[1, 2], [3, 4]] // A deep list with embedded lists inside.
//! ```
//!
//! Lists are 0 indexed and you can index into them using the dotted selector syntax.
//!
//! ```ucg
//! let mylist = [0, 1, 2, 3];
//!
//! let zero = mylist.0;
//! ```
//!
//! ##### List macros
//!
//! ucg supports a couple of ways to use macros for mapping or filtering a list to a new list.
//!
//! A map expression starts with the map keyword followed by the name of a macro with exactly
//! one argument, a `.`, and the name of the output field for the macro. ucg will apply the macro
//! to each element of the list and then take the output field from the resulting tuple and add append
//! it to the resulting list. If the output field does not exist in the macro it will be a compile
//! error.
//!
//! ```ucg
//! let list = [1, 2, 3, 4];
//! let mapper = macro(item) => { result = item + 1 };
//!
//! // results in: [2, 3, 4, 5]
//! let mapped = map mapper.result list;
//! ```
//!
//
//! A filter expression starts with the filter keyword followed by the name of a macro with exactly
//! one argument, a `.`, and the name of the output field for the macro. The filter will apply the
//! macro to each element of the list and if the output field is a Value that is not NULL then the
//! list element is appended to the output list. If the output field returns a NULL Value then the
//! element is not appended to the output list. If the output field does not exist in the macro it
//! will be a compile error.
//!
//! ```ucg
//! let list = ["foo", "bar", "foo", "bar"];
//! let filtrator = macro(item) => {
//!   ok = select item NULL {
//!     foo = 1
//!   }
//! };
//!
//! // results in: ["foo", "foo"]
//! let filtered = filter filtrator.ok list;
//! ```
//!
//! #### Tuple
//!
//! Tuple's are an ordered collection of name, value pairs. They are bounded by curly braces `{ }`
//! and contain name = value pairs separated by commas. Trailing commas are permitted. The name must
//! be a bareword without quotes.
//!
//! ```ucg
//! let mytuple = {
//!     field1 = "value1",
//!     field2 = "value2",
//! };
//! ```
//!
//! Tuples can be indexed using dotted selector syntax.
//!
//! ```ucg
//! let field = mytuple.fields1;
//! ```
//!
//! ### Expressions
//!
//! #### Selectors
//!
//! Selectors are references to a bound value in ucg. They can index arbitrarily deep into either tuples or lists.
//! The head of a selector can be any expression that resolves to a tuple or list. Optionally a selector can also be
//! followed by either a bareword to index a tuple field or an integer to index a list position.
//!
//! The simplest selector is just a reference to a bound value by name.
//!
//! ```ucg
//! let mytuple = {
//!     field1 = "a string",
//!     field2 = [{
//!         subfield1 = 1,
//!     }];
//! };
//!
//! mytuple.field2.0; // descend into a deeply nested tuple and array.
//! ```
//!
//! The `env` variable is a reserved variable that always contains a tuple with any environment
//! variables in it.
//!
//! Attempting to reference an enviroment variable that does not exist is a compile error.
//!
//! #### Binary operators
//!
//! ##### Numeric operators
//!
//! ucg supports the following numeric operators, `+`, `-`, `*`, `/` Each one is type safe and infers the types
//! from the values they operate on. The operators expect both the left and right operands to be of the same
//! type.
//!
//! ```ucg
//! 1 + 1; // result is 2
//! ```
//!
//! ##### Concatenation
//!
//! ucg supports concatenation using the `+` operator. It is typesafe expecting both sides to be of the same type.
//! You can concatenate strings or lists but not tuples.
//!
//! ```ucg
//! "foo " + "bar" // result is "foo bar"
//! [1,2] + [3,4]; // result is [1,2,3,4]
//! ```
//!
//! ##### Comparison
//!
//! ucg supports comparison using the `==`, `!=`, `>`, `<`, `>=`, `<=` operators. They are type safe and expect both
//! sides to be of the same type.
//!
//! The `>`, `<`, `>=`, and `>=` operators are only supported on numeric types.
//!
//! ```ucg
//! 1 > 2; // result is false
//! 2 < 3; // result is true
//! 10 > "9"; // This is a compile error.
//! (1+2) == 3
//! ```
//! The comparison operators expect either a simple value or a grouped expression as their left operand.
//!
//! The equality operators `==` and `!=` are supported for all types and will perform deep equal comparisons on complex
//! types.
//!
//! ```ucg
//! let tpl1 = {
//!   foo = "bar",
//!   one = 1
//! };
//! let tpl2 = tpl1{}; // copy the tpl1 tuple
//! tpl1 == tpl2; // returns true
//! let tpl3 = tpl1{duck="quack"};
//! tpl1 == tpl3; // returns false
//! ```
//!
//! Note that tuple fields are ordered so a tuple will only be equal if the fields are both in the same order and
//! have the same values in them.
//!
//! ##### Operator Precedence
//!
//! UCG binary operators follow the typical operator precedence for math. `*` and `/` are higher precendence than
//! `+` and `-` which are higher precedence than any of the comparison operators.
//!
//! ```ucg
//! 1+1 == 2; // this will be a type error. The + expects an integer but it has a boolean expression
//!           // on the right.
//! (1+1) == 2; // this succeeds because the parentheses force evaluation of the addition first.
//! ```
//!
//! #### Copy expressions
//!
//! ucg Tuples support a form of reuse with copy on write semantics. You can copy a tuple and selectively overwrite fields or add new
//! fields to it with the copy expression. To perform a copy first reference the tuple by a bound name and then use `{ field = value, ... }`
//! syntax to copy with overridden fields  or add completely new fields. When replacing a preexisting field with a new value you cannot
//! change the type of the field. This allows you to define a typed shape for a tuple with default values and then provide new values for
//! some or all of the fields while still enforcing the same types for those fields. Adding completely new fields has no such restriction.
//!
//! ```ucg
//! let base = {
//!     field1 = "value1",
//!     field2 = 100,
//!     field3 = 5.6,
//! };
//!
//! let overridden = base{
//!     field1 = "new value"
//! };
//!
//! let expanded = base{
//!     field2 = 200,
//!     field3 = "look ma a new field",
//! };
//! ```
//!
//! The following will cause an error because the overriden field's value does not match the original.
//!
//! ```ucg
//! let bad = base{
//!     field1 = 300, // Error!!! must be a string.
//! };
//!
//! ```
//!
//! #### Conditional data
//!
//! ucg supports a limited form of conditional data selection of using the select expression. A select expression starts with the `select`
//! keyword and is followed by a an expression resolving to a string naming the field to select, an expression resolving to the default value,
//! and a tuple to select the field from. If the field selected is not in the tuple then the default value will be used.
//!
//! ```ucg
//! let want = "baz";
//!
//! //     field  default
//! select want, "quux", {
//!     baz = "foo",
//!     fuzz = "bang",
//! }; // result will be "foo"
//!
//! //     field    default
//! select "quack", "quux", {
//!     baz = "foo",
//!     fuzz = "bang",
//! }; // result will be "quux"
//! ```
//!
//! #### Macros
//!
//! Macros look like functions but they are resolved at compile time and configurations don't execute so they never appear in output.
//! They are useful for constructing tuples of a certain shape or otherwise promoting data reuse. You define a macro with the `macro`
//! keyword followed by the arguments in parentheses, a `=>`, and then a tuple.
//!
//! ```ucg
//! let myfunc = macro (arg1, arg2) => {
//!     host = arg1,
//!     port = arg2,
//!     connstr = "couchdb://@:@" % (arg1, arg2),
//! }
//!
//! let my_dbconf = myfunc("couchdb.example.org", "9090");
//!
//! let my_dbhost = dbconf.host;
//! ```
//!
//! macros always resolve to a tuple. If you want to get a single value out you can use selector syntax to retrieve it.
//!
//! ### Statements
//!
//! There are 3 kinds of statements in a ucg configuration file. expression statements, let statements, and import statements.
//! All ucg statements must be terminated by a semicolon.
//!
//! * expression statements
//!
//! The simplest and least useful is the expression statement. It is any valid expression followed by a semicolon.
//!
//! ```ucg
//! 1;
//! 4 / 2;
//! "foo";
//! "foo" + "bar";
//! ```
//!
//! Despite the fact that these are valid the results are thrown away and can essentially be considered a noop. If we
//! ever create a repl for ucg statements they may prove more useful.
//!
//!  * Let statements
//!
//! The let expression binds the result of any valid expression to a name. It starts with the `let` keyword and is followed by
//! the name of the binding, an `=`, and a valid ucg expression.
//!
//! ```ucg
//! let name = "foo";
//! ```
//!
//! * Import statement
//!
//! The import statement imports the contents of another ucg file into the current file with a name. The imported files bound
//! values are exposed as a tuple in the referencing file. It starts with the `import` keyword and is followed by a quoted path
//! to the ucg file, the keyword `as`, and a name for the imported values.
//!
//! ```ucg
//! import "dbconfigs.ucg" as dbconfigs;
//!
//! let mysqlconf = dbconfigs.mysql;
//! ```

// The following is necessary to allow the macros in tokenizer and parse modules
// to succeed.
#![recursion_limit = "128"]
#[macro_use]
extern crate nom;
#[macro_use]
extern crate nom_locate;
extern crate serde_json;

#[macro_use]
pub mod ast;
#[macro_use]
pub mod tokenizer;
pub mod build;
pub mod convert;
pub mod error;
pub mod parse;

mod format;

pub use ast::tree::Expression;
pub use ast::tree::Statement;
pub use ast::tree::Value;

pub use build::Builder;
pub use build::Val;
pub use parse::parse;
<commit_msg>Documentation fix.<commit_after>// Copyright 2017 Jeremy Wall <jeremy@marzhillstudios.com>
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//      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.
// #![feature(trace_macros,log_syntax)]

//! # ucg, A universal configuration grammar.
//!
//! Ucg defines a common grammar for describing a collection of configuration values.
//! ucg allows you to specify configuration values with a syntax that that is immutable,
//! comoposable with copy-on-write semantics, and safe.
//!
//! ## Example
//!
//! ```ucg
//! // named bindings
//! let host = "mysql.internal.net";
//! let port = 8080
//!
//! // format strings
//! let connstr = "mysql://@:@" % (host, port);
//!
//! // tuples
//! let dbconf = {
//!     connstr = connstr,
//!     database = "mydb",
//!     // lists
//!     tables = ["posts", "comments", "users"],
//! };
//! ```
//!
//! ## Syntax
//!
//! ucg is a safe language with type inference that tries to guarantee that it will halt.
//! A valid ucg file is composesed of a series of statements. Statements are any valid
//! ucg expression terminated by a semicolon.
//!
//! ### Reserved words
//!
//! The following words are reserved in ucg and can't be used as named bindings.
//!
//! * true
//! * false
//! * let
//! * import
//! * as
//! * select
//! * macro
//! * env
//! * map
//! * filter
//! * NULL
//!
//! ### Primitive types
//!
//! ucg has a relatively simple syntax with a few primitive types, Null, Boolean, Int, Float, and String.
//!
//! ### Boolean
//!
//! A Boolean is either `true` or `false`.
//!
//! ```uct
//! true;
//! false;
//! ```
//!
//! #### Int
//!
//! An Int is any integer number.
//!
//! ```ucg
//! 1; // a single Integer
//! ```
//!
//! #### Float
//!
//! A Float is any number with a decimal point.
//!
//! ```ucg
//! 1.0; // A typical float.
//! 1. // You can leave off the 0 after the decimal point.
//! .1 // the leading 0 is also optional.
//! ```
//!
//! #### String
//!
//! A String is any quoted text. Backslashes within a string escape the next preceding
//! character.
//!
//! ``` ucg
//! "foo"; // a simple string
//! "I'm a \"fine\" looking string"; // escaped quotes in a string.
//! ```
//!
//! #### NULL or the Empty type.
//!
//!  A NULL is an empty type. It represents no value.
//!
//! ```ucg
//! let empty = NULL;
//! ```
//!
//! ### Complex types
//!
//! ucg has two complex data types, Lists and Tuples.
//!
//! #### Lists
//!
//! Lists are surrounded with square brackets `[ ]` and have comma separated elements.
//!
//! ```ucg
//! [1, 2, 3]; // A simple list of numbers.
//!
//! [[1, 2], [3, 4]] // A deep list with embedded lists inside.
//! ```
//!
//! Lists are 0 indexed and you can index into them using the dotted selector syntax.
//!
//! ```ucg
//! let mylist = [0, 1, 2, 3];
//!
//! let zero = mylist.0;
//! ```
//!
//! ##### List macros
//!
//! ucg supports a couple of ways to use macros for mapping or filtering a list to a new list.
//!
//! A map expression starts with the map keyword followed by the name of a macro with exactly
//! one argument, a `.`, and the name of the output field for the macro. ucg will apply the macro
//! to each element of the list and then take the output field from the resulting tuple and add append
//! it to the resulting list. If the output field does not exist in the macro it will be a compile
//! error.
//!
//! ```ucg
//! let list = [1, 2, 3, 4];
//! let mapper = macro(item) => { result = item + 1 };
//!
//! // results in: [2, 3, 4, 5]
//! let mapped = map mapper.result list;
//! ```
//!
//
//! A filter expression starts with the filter keyword followed by the name of a macro with exactly
//! one argument, a `.`, and the name of the output field for the macro. The filter will apply the
//! macro to each element of the list and if the output field is a Value that is not NULL then the
//! list element is appended to the output list. If the output field returns a NULL Value then the
//! element is not appended to the output list. If the output field does not exist in the macro it
//! will be a compile error.
//!
//! ```ucg
//! let list = ["foo", "bar", "foo", "bar"];
//! let filtrator = macro(item) => {
//!   ok = select item NULL {
//!     foo = 1
//!   }
//! };
//!
//! // results in: ["foo", "foo"]
//! let filtered = filter filtrator.ok list;
//! ```
//!
//! #### Tuple
//!
//! Tuple's are an ordered collection of name, value pairs. They are bounded by curly braces `{ }`
//! and contain name = value pairs separated by commas. Trailing commas are permitted. The name must
//! be a bareword without quotes.
//!
//! ```ucg
//! let mytuple = {
//!     field1 = "value1",
//!     field2 = "value2",
//! };
//! ```
//!
//! Tuples can be indexed using dotted selector syntax.
//!
//! ```ucg
//! let field = mytuple.fields1;
//! ```
//!
//! ### Expressions
//!
//! #### Selectors
//!
//! Selectors are references to a bound value in ucg. They can index arbitrarily deep into either tuples or lists.
//! The head of a selector can be any expression that resolves to a tuple or list. Optionally a selector can also be
//! followed by either a bareword to index a tuple field or an integer to index a list position.
//!
//! The simplest selector is just a reference to a bound value by name.
//!
//! ```ucg
//! let mytuple = {
//!     field1 = "a string",
//!     field2 = [{
//!         subfield1 = 1,
//!     }];
//! };
//!
//! mytuple.field2.0; // descend into a deeply nested tuple and array.
//! ```
//!
//! The `env` variable is a reserved variable that always contains a tuple with any environment
//! variables in it.
//!
//! Attempting to reference an enviroment variable that does not exist is a compile error.
//!
//! #### Binary operators
//!
//! ##### Numeric operators
//!
//! ucg supports the following numeric operators, `+`, `-`, `*`, `/` Each one is type safe and infers the types
//! from the values they operate on. The operators expect both the left and right operands to be of the same
//! type.
//!
//! ```ucg
//! 1 + 1; // result is 2
//! ```
//!
//! ##### Concatenation
//!
//! ucg supports concatenation using the `+` operator. It is typesafe expecting both sides to be of the same type.
//! You can concatenate strings or lists but not tuples.
//!
//! ```ucg
//! "foo " + "bar" // result is "foo bar"
//! [1,2] + [3,4]; // result is [1,2,3,4]
//! ```
//!
//! ##### Comparison
//!
//! ucg supports comparison using the `==`, `!=`, `>`, `<`, `>=`, `<=` operators. They are type safe and expect both
//! sides to be of the same type.
//!
//! The `>`, `<`, `>=`, and `>=` operators are only supported on numeric types.
//!
//! ```ucg
//! 1 > 2; // result is false
//! 2 < 3; // result is true
//! 10 > "9"; // This is a compile error.
//! (1+2) == 3
//! ```
//! The comparison operators expect either a simple value or a grouped expression as their left operand.
//!
//! The equality operators `==` and `!=` are supported for all types and will perform deep equal comparisons on complex
//! types.
//!
//! ```ucg
//! let tpl1 = {
//!   foo = "bar",
//!   one = 1
//! };
//! let tpl2 = tpl1{}; // copy the tpl1 tuple
//! tpl1 == tpl2; // returns true
//! let tpl3 = tpl1{duck="quack"};
//! tpl1 == tpl3; // returns false
//! ```
//!
//! Note that tuple fields are ordered so a tuple will only be equal if the fields are both in the same order and
//! have the same values in them.
//!
//! ##### Operator Precedence
//!
//! UCG binary operators follow the typical operator precedence for math. `*` and `/` are higher precendence than
//! `+` and `-` which are higher precedence than any of the comparison operators.
//!
//! #### Copy expressions
//!
//! ucg Tuples support a form of reuse with copy on write semantics. You can copy a tuple and selectively overwrite fields or add new
//! fields to it with the copy expression. To perform a copy first reference the tuple by a bound name and then use `{ field = value, ... }`
//! syntax to copy with overridden fields  or add completely new fields. When replacing a preexisting field with a new value you cannot
//! change the type of the field. This allows you to define a typed shape for a tuple with default values and then provide new values for
//! some or all of the fields while still enforcing the same types for those fields. Adding completely new fields has no such restriction.
//!
//! ```ucg
//! let base = {
//!     field1 = "value1",
//!     field2 = 100,
//!     field3 = 5.6,
//! };
//!
//! let overridden = base{
//!     field1 = "new value"
//! };
//!
//! let expanded = base{
//!     field2 = 200,
//!     field3 = "look ma a new field",
//! };
//! ```
//!
//! The following will cause an error because the overriden field's value does not match the original.
//!
//! ```ucg
//! let bad = base{
//!     field1 = 300, // Error!!! must be a string.
//! };
//!
//! ```
//!
//! #### Conditional data
//!
//! ucg supports a limited form of conditional data selection of using the select expression. A select expression starts with the `select`
//! keyword and is followed by a an expression resolving to a string naming the field to select, an expression resolving to the default value,
//! and a tuple to select the field from. If the field selected is not in the tuple then the default value will be used.
//!
//! ```ucg
//! let want = "baz";
//!
//! //     field  default
//! select want, "quux", {
//!     baz = "foo",
//!     fuzz = "bang",
//! }; // result will be "foo"
//!
//! //     field    default
//! select "quack", "quux", {
//!     baz = "foo",
//!     fuzz = "bang",
//! }; // result will be "quux"
//! ```
//!
//! #### Macros
//!
//! Macros look like functions but they are resolved at compile time and configurations don't execute so they never appear in output.
//! They are useful for constructing tuples of a certain shape or otherwise promoting data reuse. You define a macro with the `macro`
//! keyword followed by the arguments in parentheses, a `=>`, and then a tuple.
//!
//! ```ucg
//! let myfunc = macro (arg1, arg2) => {
//!     host = arg1,
//!     port = arg2,
//!     connstr = "couchdb://@:@" % (arg1, arg2),
//! }
//!
//! let my_dbconf = myfunc("couchdb.example.org", "9090");
//!
//! let my_dbhost = dbconf.host;
//! ```
//!
//! macros always resolve to a tuple. If you want to get a single value out you can use selector syntax to retrieve it.
//!
//! ### Statements
//!
//! There are 3 kinds of statements in a ucg configuration file. expression statements, let statements, and import statements.
//! All ucg statements must be terminated by a semicolon.
//!
//! * expression statements
//!
//! The simplest and least useful is the expression statement. It is any valid expression followed by a semicolon.
//!
//! ```ucg
//! 1;
//! 4 / 2;
//! "foo";
//! "foo" + "bar";
//! ```
//!
//! Despite the fact that these are valid the results are thrown away and can essentially be considered a noop. If we
//! ever create a repl for ucg statements they may prove more useful.
//!
//!  * Let statements
//!
//! The let expression binds the result of any valid expression to a name. It starts with the `let` keyword and is followed by
//! the name of the binding, an `=`, and a valid ucg expression.
//!
//! ```ucg
//! let name = "foo";
//! ```
//!
//! * Import statement
//!
//! The import statement imports the contents of another ucg file into the current file with a name. The imported files bound
//! values are exposed as a tuple in the referencing file. It starts with the `import` keyword and is followed by a quoted path
//! to the ucg file, the keyword `as`, and a name for the imported values.
//!
//! ```ucg
//! import "dbconfigs.ucg" as dbconfigs;
//!
//! let mysqlconf = dbconfigs.mysql;
//! ```

// The following is necessary to allow the macros in tokenizer and parse modules
// to succeed.
#![recursion_limit = "128"]
#[macro_use]
extern crate nom;
#[macro_use]
extern crate nom_locate;
extern crate serde_json;

#[macro_use]
pub mod ast;
#[macro_use]
pub mod tokenizer;
pub mod build;
pub mod convert;
pub mod error;
pub mod parse;

mod format;

pub use ast::tree::Expression;
pub use ast::tree::Statement;
pub use ast::tree::Value;

pub use build::Builder;
pub use build::Val;
pub use parse::parse;
<|endoftext|>


<commit_before>// Copyright (c) 2017-2018 Rene van der Meer
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

//! Raspberry Pi system-related tools.
//!
//! Use [`DeviceInfo`] to identify what Raspberry Pi model and SoC the software is
//! running on.
//!
//! [`DeviceInfo`]: struct.DeviceInfo.html

use std::fmt;
use std::fs;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::result;

const PERIPHERAL_BASE_RPI: u32 = 0x2000_0000;
const PERIPHERAL_BASE_RPI2: u32 = 0x3f00_0000;
const GPIO_OFFSET: u32 = 0x20_0000;

quick_error! {
/// Errors that can occur when trying to identify the Raspberry Pi hardware.
    #[derive(Debug)]
    pub enum Error {
/// Unknown model.
///
/// `DeviceInfo` was unable to identify the Raspberry Pi model based on the
/// contents of `/proc/cpuinfo`, `/sys/firmware/devicetree/base/compatible`
/// and `/sys/firmware/devicetree/base/model`.
        UnknownModel { description("unknown Raspberry Pi model") }
    }
}

/// Result type returned from methods that can have `system::Error`s.
pub type Result<T> = result::Result<T, Error>;

/// Identifiable Raspberry Pi models.
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Model {
    RaspberryPiA,
    RaspberryPiAPlus,
    RaspberryPiBRev1,
    RaspberryPiBRev2,
    RaspberryPiBPlus,
    RaspberryPi2B,
    RaspberryPi3B,
    RaspberryPi3BPlus,
    RaspberryPiComputeModule,
    RaspberryPiComputeModule3,
    RaspberryPiZero,
    RaspberryPiZeroW,
}

impl fmt::Display for Model {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Model::RaspberryPiA => write!(f, "Raspberry Pi A"),
            Model::RaspberryPiAPlus => write!(f, "Raspberry Pi A+"),
            Model::RaspberryPiBRev1 => write!(f, "Raspberry Pi B Rev 1"),
            Model::RaspberryPiBRev2 => write!(f, "Raspberry Pi B Rev 2"),
            Model::RaspberryPiBPlus => write!(f, "Raspberry Pi B+"),
            Model::RaspberryPi2B => write!(f, "Raspberry Pi 2 B"),
            Model::RaspberryPi3B => write!(f, "Raspberry Pi 3 B"),
            Model::RaspberryPi3BPlus => write!(f, "Raspberry Pi 3 B+"),
            Model::RaspberryPiComputeModule => write!(f, "Raspberry Pi Compute Module"),
            Model::RaspberryPiComputeModule3 => write!(f, "Raspberry Pi Compute Module 3"),
            Model::RaspberryPiZero => write!(f, "Raspberry Pi Zero"),
            Model::RaspberryPiZeroW => write!(f, "Raspberry Pi Zero W"),
        }
    }
}

/// Identifiable Raspberry Pi SoCs.
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum SoC {
    Bcm2835,
    Bcm2836,
    Bcm2837A1,
    Bcm2837B0,
}

impl fmt::Display for SoC {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            SoC::Bcm2835 => write!(f, "BCM2835"),
            SoC::Bcm2836 => write!(f, "BCM2836"),
            SoC::Bcm2837A1 => write!(f, "BCM2837A1"),
            SoC::Bcm2837B0 => write!(f, "BCM2837B0"),
        }
    }
}

// Identify Pi model based on /proc/cpuinfo
fn parse_proc_cpuinfo() -> Result<Model> {
    let proc_cpuinfo = BufReader::new(match File::open("/proc/cpuinfo") {
        Ok(file) => file,
        Err(_) => return Err(Error::UnknownModel),
    });

    let mut hardware: String = String::new();
    let mut revision: String = String::new();
    for line_result in proc_cpuinfo.lines() {
        if let Ok(line) = line_result {
            if line.starts_with("Hardware\t: ") {
                hardware = String::from(&line[11..]);
            } else if line.starts_with("Revision\t: ") {
                revision = String::from(&line[11..]).to_lowercase();
            }
        }
    }

    // Return an error if we don't recognize the SoC. This check is
    // done to prevent accidentally identifying a non-Pi SBC as a Pi
    // solely based on the revision field.
    match &hardware[..] {
        "BCM2708" | "BCM2835" | "BCM2709" | "BCM2836" | "BCM2710" | "BCM2837" | "BCM2837A1"
        | "BCM2837B0" => {}
        _ => return Err(Error::UnknownModel),
    }

    let model = if (revision.len() == 4) || (revision.len() == 8) {
        // Older revisions are 4 characters long, or 8 if they've been over-volted
        match &revision[revision.len() - 4..] {
            "0007" | "0008" | "0009" | "0015" => Model::RaspberryPiA,
            "Beta" | "0002" | "0003" => Model::RaspberryPiBRev1,
            "0004" | "0005" | "0006" | "000d" | "000e" | "000f" => Model::RaspberryPiBRev2,
            "0012" => Model::RaspberryPiAPlus,
            "0010" | "0013" => Model::RaspberryPiBPlus,
            "0011" | "0014" => Model::RaspberryPiComputeModule,
            _ => return Err(Error::UnknownModel),
        }
    } else if revision.len() >= 6 {
        // Newer revisions consist of at least 6 characters
        match &revision[revision.len() - 3..revision.len() - 1] {
            "00" => Model::RaspberryPiA,
            "01" => Model::RaspberryPiBRev2,
            "02" => Model::RaspberryPiAPlus,
            "03" => Model::RaspberryPiBPlus,
            "04" => Model::RaspberryPi2B,
            "06" => Model::RaspberryPiComputeModule,
            "08" => Model::RaspberryPi3B,
            "09" => Model::RaspberryPiZero,
            "0a" => Model::RaspberryPiComputeModule3,
            "0c" => Model::RaspberryPiZeroW,
            "0d" => Model::RaspberryPi3BPlus,
            _ => return Err(Error::UnknownModel),
        }
    } else {
        return Err(Error::UnknownModel);
    };

    Ok(model)
}

// Identify Pi model based on /sys/firmware/devicetree/base/compatible
fn parse_base_compatible() -> Result<Model> {
    let base_compatible = match fs::read_to_string("/sys/firmware/devicetree/base/compatible") {
        Ok(buffer) => buffer,
        Err(_) => return Err(Error::UnknownModel),
    };

    // Based on /arch/arm/boot/dts/ and /Documentation/devicetree/bindings/arm/bcm/
    for comp_id in base_compatible.split('\0') {
        let model = match comp_id {
            "raspberrypi,model-b-i2c0" => Model::RaspberryPiBRev1,
            "raspberrypi,model-b" => Model::RaspberryPiBRev1,
            "raspberrypi,model-a" => Model::RaspberryPiA,
            "raspberrypi,model-b-rev2" => Model::RaspberryPiBRev2,
            "raspberrypi,model-a-plus" => Model::RaspberryPiAPlus,
            "raspberrypi,model-b-plus" => Model::RaspberryPiBPlus,
            "raspberrypi,2-model-b" => Model::RaspberryPi2B,
            "raspberrypi,compute-module" => Model::RaspberryPiComputeModule,
            "raspberrypi,3-model-b" => Model::RaspberryPi3B,
            "raspberrypi,model-zero" => Model::RaspberryPiZero,
            "raspberrypi,3-compute-module" => Model::RaspberryPiComputeModule3,
            "raspberrypi,model-zero-w" => Model::RaspberryPiZeroW,
            "raspberrypi,3-model-b-plus" => Model::RaspberryPi3BPlus,
            _ => continue,
        };

        return Ok(model);
    }

    Err(Error::UnknownModel)
}

// Identify Pi model based on /sys/firmware/devicetree/base/model
fn parse_base_model() -> Result<Model> {
    let mut base_model = match fs::read_to_string("/sys/firmware/devicetree/base/model") {
        Ok(mut buffer) => if let Some(idx) = buffer.find('\0') {
            buffer.truncate(idx);
            buffer
        } else {
            buffer
        },
        Err(_) => return Err(Error::UnknownModel),
    };

    // Check if this is a Pi B rev 2 before we remove the revision part, assuming the
    // PCB Revision numbers on https://elinux.org/RPi_HardwareHistory are correct, and
    // the installed distro appends the revision to the model name.
    match &base_model[..] {
        "Raspberry Pi Model B Rev 2.0" => return Ok(Model::RaspberryPiBRev2),
        "Raspberry Pi Model B rev2 Rev 2.0" => return Ok(Model::RaspberryPiBRev2),
        _ => (),
    }

    if let Some(idx) = base_model.find(" Rev ") {
        base_model.truncate(idx);
    }

    // Based on /arch/arm/boot/dts/ and /Documentation/devicetree/bindings/arm/bcm/
    let model = match &base_model[..] {
        "Raspberry Pi Model B (no P5)" => Model::RaspberryPiBRev1,
        "Raspberry Pi Model B" => Model::RaspberryPiBRev1,
        "Raspberry Pi Model A" => Model::RaspberryPiA,
        "Raspberry Pi Model B rev2" => Model::RaspberryPiBRev2,
        "Raspberry Pi Model A+" => Model::RaspberryPiAPlus,
        "Raspberry Pi Model A Plus" => Model::RaspberryPiAPlus,
        "Raspberry Pi Model B+" => Model::RaspberryPiBPlus,
        "Raspberry Pi Model B Plus" => Model::RaspberryPiBPlus,
        "Raspberry Pi 2 Model B" => Model::RaspberryPi2B,
        "Raspberry Pi Compute Module" => Model::RaspberryPiComputeModule,
        "Raspberry Pi 3 Model B" => Model::RaspberryPi3B,
        "Raspberry Pi Zero" => Model::RaspberryPiZero,
        "Raspberry Pi Compute Module 3" => Model::RaspberryPiComputeModule3,
        "Raspberry Pi Zero W" => Model::RaspberryPiZeroW,
        "Raspberry Pi 3 Model B+" => Model::RaspberryPi3BPlus,
        "Raspberry Pi 3 Model B Plus" => Model::RaspberryPi3BPlus,
        _ => return Err(Error::UnknownModel),
    };

    Ok(model)
}

/// Retrieves Raspberry Pi device information.
#[derive(Debug, PartialEq, Copy, Clone)]
pub struct DeviceInfo {
    model: Model,
    soc: SoC,
    peripheral_base: u32,
    gpio_offset: u32,
}

impl DeviceInfo {
    /// Constructs a new `DeviceInfo`.
    ///
    /// `new` automatically identifies the Raspberry Pi model and SoC based on
    /// the contents of `/proc/cpuinfo`, `/sys/firmware/devicetree/base/compatible`
    /// and `/sys/firmware/devicetree/base/model`.
    pub fn new() -> Result<DeviceInfo> {
        // Parse order from most-detailed to least-detailed info
        let model = parse_proc_cpuinfo()
            .or_else(|_| parse_base_compatible().or_else(|_| parse_base_model()))?;

        // Set SoC and memory offsets based on model
        match model {
            Model::RaspberryPiA
            | Model::RaspberryPiAPlus
            | Model::RaspberryPiBRev1
            | Model::RaspberryPiBRev2
            | Model::RaspberryPiBPlus
            | Model::RaspberryPiComputeModule
            | Model::RaspberryPiZero
            | Model::RaspberryPiZeroW => Ok(DeviceInfo {
                model,
                soc: SoC::Bcm2835,
                peripheral_base: PERIPHERAL_BASE_RPI,
                gpio_offset: GPIO_OFFSET,
            }),
            Model::RaspberryPi2B => Ok(DeviceInfo {
                model,
                soc: SoC::Bcm2836,
                peripheral_base: PERIPHERAL_BASE_RPI2,
                gpio_offset: GPIO_OFFSET,
            }),
            Model::RaspberryPi3B | Model::RaspberryPiComputeModule3 => Ok(DeviceInfo {
                model,
                soc: SoC::Bcm2837A1,
                peripheral_base: PERIPHERAL_BASE_RPI2,
                gpio_offset: GPIO_OFFSET,
            }),
            Model::RaspberryPi3BPlus => Ok(DeviceInfo {
                model,
                soc: SoC::Bcm2837B0,
                peripheral_base: PERIPHERAL_BASE_RPI2,
                gpio_offset: GPIO_OFFSET,
            }),
        }
    }

    /// Returns the Raspberry Pi's model.
    pub fn model(&self) -> Model {
        self.model
    }

    /// Returns the Raspberry Pi's SoC.
    pub fn soc(&self) -> SoC {
        self.soc
    }

    /// Returns the base memory address for the BCM283x peripherals.
    pub fn peripheral_base(&self) -> u32 {
        self.peripheral_base
    }

    /// Returns the offset from the base memory address for the GPIO section.
    pub fn gpio_offset(&self) -> u32 {
        self.gpio_offset
    }
}
<commit_msg>Add support for Raspberry Pi 3 A+<commit_after>// Copyright (c) 2017-2018 Rene van der Meer
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

//! Raspberry Pi system-related tools.
//!
//! Use [`DeviceInfo`] to identify what Raspberry Pi model and SoC the software is
//! running on.
//!
//! [`DeviceInfo`]: struct.DeviceInfo.html

use std::fmt;
use std::fs;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::result;

const PERIPHERAL_BASE_RPI: u32 = 0x2000_0000;
const PERIPHERAL_BASE_RPI2: u32 = 0x3f00_0000;
const GPIO_OFFSET: u32 = 0x20_0000;

quick_error! {
/// Errors that can occur when trying to identify the Raspberry Pi hardware.
    #[derive(Debug)]
    pub enum Error {
/// Unknown model.
///
/// `DeviceInfo` was unable to identify the Raspberry Pi model based on the
/// contents of `/proc/cpuinfo`, `/sys/firmware/devicetree/base/compatible`
/// and `/sys/firmware/devicetree/base/model`.
        UnknownModel { description("unknown Raspberry Pi model") }
    }
}

/// Result type returned from methods that can have `system::Error`s.
pub type Result<T> = result::Result<T, Error>;

/// Identifiable Raspberry Pi models.
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Model {
    RaspberryPiA,
    RaspberryPiAPlus,
    RaspberryPiBRev1,
    RaspberryPiBRev2,
    RaspberryPiBPlus,
    RaspberryPi2B,
    RaspberryPi3B,
    RaspberryPi3BPlus,
    RaspberryPi3APlus,
    RaspberryPiComputeModule,
    RaspberryPiComputeModule3,
    RaspberryPiZero,
    RaspberryPiZeroW,
}

impl fmt::Display for Model {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Model::RaspberryPiA => write!(f, "Raspberry Pi A"),
            Model::RaspberryPiAPlus => write!(f, "Raspberry Pi A+"),
            Model::RaspberryPiBRev1 => write!(f, "Raspberry Pi B Rev 1"),
            Model::RaspberryPiBRev2 => write!(f, "Raspberry Pi B Rev 2"),
            Model::RaspberryPiBPlus => write!(f, "Raspberry Pi B+"),
            Model::RaspberryPi2B => write!(f, "Raspberry Pi 2 B"),
            Model::RaspberryPi3B => write!(f, "Raspberry Pi 3 B"),
            Model::RaspberryPi3BPlus => write!(f, "Raspberry Pi 3 B+"),
            Model::RaspberryPi3APlus => write!(f, "Raspberry Pi 3 A+"),
            Model::RaspberryPiComputeModule => write!(f, "Raspberry Pi Compute Module"),
            Model::RaspberryPiComputeModule3 => write!(f, "Raspberry Pi Compute Module 3"),
            Model::RaspberryPiZero => write!(f, "Raspberry Pi Zero"),
            Model::RaspberryPiZeroW => write!(f, "Raspberry Pi Zero W"),
        }
    }
}

/// Identifiable Raspberry Pi SoCs.
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum SoC {
    Bcm2835,
    Bcm2836,
    Bcm2837A1,
    Bcm2837B0,
}

impl fmt::Display for SoC {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            SoC::Bcm2835 => write!(f, "BCM2835"),
            SoC::Bcm2836 => write!(f, "BCM2836"),
            SoC::Bcm2837A1 => write!(f, "BCM2837A1"),
            SoC::Bcm2837B0 => write!(f, "BCM2837B0"),
        }
    }
}

// Identify Pi model based on /proc/cpuinfo
fn parse_proc_cpuinfo() -> Result<Model> {
    let proc_cpuinfo = BufReader::new(match File::open("/proc/cpuinfo") {
        Ok(file) => file,
        Err(_) => return Err(Error::UnknownModel),
    });

    let mut hardware: String = String::new();
    let mut revision: String = String::new();
    for line_result in proc_cpuinfo.lines() {
        if let Ok(line) = line_result {
            if line.starts_with("Hardware\t: ") {
                hardware = String::from(&line[11..]);
            } else if line.starts_with("Revision\t: ") {
                revision = String::from(&line[11..]).to_lowercase();
            }
        }
    }

    // Return an error if we don't recognize the SoC. This check is
    // done to prevent accidentally identifying a non-Pi SBC as a Pi
    // solely based on the revision field.
    match &hardware[..] {
        "BCM2708" | "BCM2835" | "BCM2709" | "BCM2836" | "BCM2710" | "BCM2837" | "BCM2837A1"
        | "BCM2837B0" => {}
        _ => return Err(Error::UnknownModel),
    }

    let model = if (revision.len() == 4) || (revision.len() == 8) {
        // Older revisions are 4 characters long, or 8 if they've been over-volted
        match &revision[revision.len() - 4..] {
            "0007" | "0008" | "0009" | "0015" => Model::RaspberryPiA,
            "Beta" | "0002" | "0003" => Model::RaspberryPiBRev1,
            "0004" | "0005" | "0006" | "000d" | "000e" | "000f" => Model::RaspberryPiBRev2,
            "0012" => Model::RaspberryPiAPlus,
            "0010" | "0013" => Model::RaspberryPiBPlus,
            "0011" | "0014" => Model::RaspberryPiComputeModule,
            _ => return Err(Error::UnknownModel),
        }
    } else if revision.len() >= 6 {
        // Newer revisions consist of at least 6 characters
        match &revision[revision.len() - 3..revision.len() - 1] {
            "00" => Model::RaspberryPiA,
            "01" => Model::RaspberryPiBRev2,
            "02" => Model::RaspberryPiAPlus,
            "03" => Model::RaspberryPiBPlus,
            "04" => Model::RaspberryPi2B,
            "06" => Model::RaspberryPiComputeModule,
            "08" => Model::RaspberryPi3B,
            "09" => Model::RaspberryPiZero,
            "0a" => Model::RaspberryPiComputeModule3,
            "0c" => Model::RaspberryPiZeroW,
            "0d" => Model::RaspberryPi3BPlus,
            _ => return Err(Error::UnknownModel),
        }
    } else {
        return Err(Error::UnknownModel);
    };

    Ok(model)
}

// Identify Pi model based on /sys/firmware/devicetree/base/compatible
fn parse_base_compatible() -> Result<Model> {
    let base_compatible = match fs::read_to_string("/sys/firmware/devicetree/base/compatible") {
        Ok(buffer) => buffer,
        Err(_) => return Err(Error::UnknownModel),
    };

    // Based on /arch/arm/boot/dts/ and /Documentation/devicetree/bindings/arm/bcm/
    for comp_id in base_compatible.split('\0') {
        let model = match comp_id {
            "raspberrypi,model-b-i2c0" => Model::RaspberryPiBRev1,
            "raspberrypi,model-b" => Model::RaspberryPiBRev1,
            "raspberrypi,model-a" => Model::RaspberryPiA,
            "raspberrypi,model-b-rev2" => Model::RaspberryPiBRev2,
            "raspberrypi,model-a-plus" => Model::RaspberryPiAPlus,
            "raspberrypi,model-b-plus" => Model::RaspberryPiBPlus,
            "raspberrypi,2-model-b" => Model::RaspberryPi2B,
            "raspberrypi,compute-module" => Model::RaspberryPiComputeModule,
            "raspberrypi,3-model-b" => Model::RaspberryPi3B,
            "raspberrypi,model-zero" => Model::RaspberryPiZero,
            "raspberrypi,3-compute-module" => Model::RaspberryPiComputeModule3,
            "raspberrypi,model-zero-w" => Model::RaspberryPiZeroW,
            "raspberrypi,3-model-b-plus" => Model::RaspberryPi3BPlus,
            "raspberrypi,3-model-a-plus" => Model::RaspberryPi3APlus,
            _ => continue,
        };

        return Ok(model);
    }

    Err(Error::UnknownModel)
}

// Identify Pi model based on /sys/firmware/devicetree/base/model
fn parse_base_model() -> Result<Model> {
    let mut base_model = match fs::read_to_string("/sys/firmware/devicetree/base/model") {
        Ok(mut buffer) => if let Some(idx) = buffer.find('\0') {
            buffer.truncate(idx);
            buffer
        } else {
            buffer
        },
        Err(_) => return Err(Error::UnknownModel),
    };

    // Check if this is a Pi B rev 2 before we remove the revision part, assuming the
    // PCB Revision numbers on https://elinux.org/RPi_HardwareHistory are correct, and
    // the installed distro appends the revision to the model name.
    match &base_model[..] {
        "Raspberry Pi Model B Rev 2.0" => return Ok(Model::RaspberryPiBRev2),
        "Raspberry Pi Model B rev2 Rev 2.0" => return Ok(Model::RaspberryPiBRev2),
        _ => (),
    }

    if let Some(idx) = base_model.find(" Rev ") {
        base_model.truncate(idx);
    }

    // Based on /arch/arm/boot/dts/ and /Documentation/devicetree/bindings/arm/bcm/
    let model = match &base_model[..] {
        "Raspberry Pi Model B (no P5)" => Model::RaspberryPiBRev1,
        "Raspberry Pi Model B" => Model::RaspberryPiBRev1,
        "Raspberry Pi Model A" => Model::RaspberryPiA,
        "Raspberry Pi Model B rev2" => Model::RaspberryPiBRev2,
        "Raspberry Pi Model A+" => Model::RaspberryPiAPlus,
        "Raspberry Pi Model A Plus" => Model::RaspberryPiAPlus,
        "Raspberry Pi Model B+" => Model::RaspberryPiBPlus,
        "Raspberry Pi Model B Plus" => Model::RaspberryPiBPlus,
        "Raspberry Pi 2 Model B" => Model::RaspberryPi2B,
        "Raspberry Pi Compute Module" => Model::RaspberryPiComputeModule,
        "Raspberry Pi 3 Model B" => Model::RaspberryPi3B,
        "Raspberry Pi Zero" => Model::RaspberryPiZero,
        "Raspberry Pi Compute Module 3" => Model::RaspberryPiComputeModule3,
        "Raspberry Pi Zero W" => Model::RaspberryPiZeroW,
        "Raspberry Pi 3 Model B+" => Model::RaspberryPi3BPlus,
        "Raspberry Pi 3 Model B Plus" => Model::RaspberryPi3BPlus,
        "Raspberry Pi 3 Model A Plus" => Model::RaspberryPi3APlus,
        _ => return Err(Error::UnknownModel),
    };

    Ok(model)
}

/// Retrieves Raspberry Pi device information.
#[derive(Debug, PartialEq, Copy, Clone)]
pub struct DeviceInfo {
    model: Model,
    soc: SoC,
    peripheral_base: u32,
    gpio_offset: u32,
}

impl DeviceInfo {
    /// Constructs a new `DeviceInfo`.
    ///
    /// `new` automatically identifies the Raspberry Pi model and SoC based on
    /// the contents of `/proc/cpuinfo`, `/sys/firmware/devicetree/base/compatible`
    /// and `/sys/firmware/devicetree/base/model`.
    pub fn new() -> Result<DeviceInfo> {
        // Parse order from most-detailed to least-detailed info
        let model = parse_proc_cpuinfo()
            .or_else(|_| parse_base_compatible().or_else(|_| parse_base_model()))?;

        // Set SoC and memory offsets based on model
        match model {
            Model::RaspberryPiA
            | Model::RaspberryPiAPlus
            | Model::RaspberryPiBRev1
            | Model::RaspberryPiBRev2
            | Model::RaspberryPiBPlus
            | Model::RaspberryPiComputeModule
            | Model::RaspberryPiZero
            | Model::RaspberryPiZeroW => Ok(DeviceInfo {
                model,
                soc: SoC::Bcm2835,
                peripheral_base: PERIPHERAL_BASE_RPI,
                gpio_offset: GPIO_OFFSET,
            }),
            Model::RaspberryPi2B => Ok(DeviceInfo {
                model,
                soc: SoC::Bcm2836,
                peripheral_base: PERIPHERAL_BASE_RPI2,
                gpio_offset: GPIO_OFFSET,
            }),
            Model::RaspberryPi3B | Model::RaspberryPiComputeModule3 => Ok(DeviceInfo {
                model,
                soc: SoC::Bcm2837A1,
                peripheral_base: PERIPHERAL_BASE_RPI2,
                gpio_offset: GPIO_OFFSET,
            }),
            Model::RaspberryPi3BPlus | Model::RaspberryPi3APlus => Ok(DeviceInfo {
                model,
                soc: SoC::Bcm2837B0,
                peripheral_base: PERIPHERAL_BASE_RPI2,
                gpio_offset: GPIO_OFFSET,
            }),
        }
    }

    /// Returns the Raspberry Pi's model.
    pub fn model(&self) -> Model {
        self.model
    }

    /// Returns the Raspberry Pi's SoC.
    pub fn soc(&self) -> SoC {
        self.soc
    }

    /// Returns the base memory address for the BCM283x peripherals.
    pub fn peripheral_base(&self) -> u32 {
        self.peripheral_base
    }

    /// Returns the offset from the base memory address for the GPIO section.
    pub fn gpio_offset(&self) -> u32 {
        self.gpio_offset
    }
}
<|endoftext|>


<commit_before>use std::iter::repeat;

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

    #[test]
    fn zeros_creates_zero_filled_vector_of_correct_size_and_length() {
        let a1 = zeros(vec![0]);
        assert_eq!(a1.shape, vec![0]);
        assert_eq!(a1.data, vec![]);

        let a2 = zeros(vec![1]);
        assert_eq!(a2.shape, vec![1]);
        assert_eq!(a2.data, vec![0]);

        let a3 = zeros(vec![10]);
        assert_eq!(a3.shape, vec![10]);
        assert_eq!(a3.data, vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);

        let a4 = zeros(vec![1, 2, 3]);
        assert_eq!(a4.shape, vec![1, 2, 3]);
        assert_eq!(a4.data, vec![0, 0, 0, 0, 0, 0]);
    }

    #[test]
    fn index_test_1() {
        let array = QArray {
            shape: vec![1, 1, 1],
            data: vec![1],
        };

        assert_eq!(array.index(vec![0, 0, 0].as_slice()), Some(1));
    }

    #[test]
    fn index_test_2() {
        let array = QArray {
            shape: vec![2, 2, 1],
            data: vec![1, 2, 3, 4],
        };

        assert_eq!(array.index(vec![1, 0, 0].as_slice()), Some(2));
        assert_eq!(array.index(vec![0, 1, 0].as_slice()), Some(3));
    }

    #[test]
    fn index_test_3() {
        let array = QArray {
            shape: vec![2, 4, 3],
            data: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
                       22, 23, 24],
        };

        assert_eq!(array.index(vec![1, 2, 1].as_slice()), Some(14));
        assert_eq!(array.index(vec![0, 2, 2].as_slice()), Some(21));
    }

    #[test]
    fn test_1d_vec_to_qarray() {
        let v = vec![1, 2, 3];
        let array = v.to_qarray();

        assert_eq!(array.shape, vec![3]);
        assert_eq!(array.data, vec![1, 2, 3]);
    }

    #[test]
    fn test_2d_vec_to_qarray() {
        let v1 = vec![vec![1, 2], vec![3, 4]];
        let array1 = v1.to_qarray();

        assert_eq!(array1.shape, vec![2, 2]);
        assert_eq!(array1.data, vec![1, 2, 3, 4]);

        let v2 = vec![vec![1, 2, 3], vec![4, 5, 6]];
        let array2 = v2.to_qarray();

        assert_eq!(array2.shape, vec![2, 3]);
        assert_eq!(array2.data, vec![1, 2, 3, 4, 5, 6]);
    }

    #[test]
    fn test_3d_vec_to_qarray() {
        let v = vec![vec![vec![1, 2], vec![3, 4]], vec![vec![5, 6], vec![7, 8]]];
        let array = v.to_qarray();

        assert_eq!(array.shape, vec![2, 2, 2]);
        assert_eq!(array.data, vec![1, 2, 3, 4, 5, 6, 7, 8]);
    }
}

/// Return a new QArray of the given shape, filled with zeroes.
///
/// ```
/// use quota::zeros;
/// let qarray = zeros(vec![1, 2, 3]);
/// assert_eq!(qarray.shape, vec![1, 2, 3]);
/// assert_eq!(qarray.data, vec![0, 0, 0, 0, 0, 0])
/// ```
pub fn zeros(shape: Vec<usize>) -> QArray {
    let data = repeat(0).take(shape.iter().sum()).collect::<Vec<isize>>();
    QArray { shape, data }
}

#[derive(Debug)]
pub struct QArray {
    pub shape: Vec<usize>,
    pub data: Vec<isize>,
}

impl QArray {
    /// Return the element at the given coordinates.
    /// If the coordinates don't specify a single element (e.g. 2D coordinates
    /// are given for a 3D array), or the coordinates are invalid,
    /// this function will return None.
    ///
    /// ```
    /// use quota::*;
    ///
    /// let qarray = zeros(vec![2, 2]);
    /// assert_eq!(qarray.index(vec![0, 0].as_slice()), Some(0));
    /// assert_eq!(qarray.index(vec![1, 1].as_slice()), Some(0));
    /// assert_eq!(qarray.index(vec![3, 3].as_slice()), None);
    /// assert_eq!(qarray.index(vec![1].as_slice()), None);
    /// ```
    pub fn index(&self, coordinates: &[usize]) -> Option<isize> {
        // Check that the coordinates are valid
        // The number of coordinates should match the shape of the array.
        // Each coordinate should be less than the length of the array
        // in that dimension.
        if coordinates.len() != self.shape.len() { return None }
        for (coord, dim) in coordinates.iter().zip(self.shape.as_slice()) {
            if *coord >= *dim { return None }
        }

        return Some(self.data[offset(self, coordinates)])
    }
}

fn offset(array: &QArray, coordinates: &[usize]) -> usize {
    coordinates
        .into_iter()
        .enumerate()
        .map(|(idx, coord)| {
                 array
                     .shape
                     .iter()
                     .take(idx)
                     .fold(*coord, |acc, x| acc * x)
             })
        .fold(0, |acc, x| acc + x)
}

trait ToQArray {
    fn to_qarray(&self) -> QArray;
}

impl ToQArray for Vec<isize> {
    fn to_qarray(&self) -> QArray {
        QArray {
            shape: vec![self.len()],
            data: self.clone(),
        }
    }
}

impl ToQArray for Vec<Vec<isize>> {
    fn to_qarray(&self) -> QArray {
        let l1 = self.len();
        let l2 = match self.first() {
            Some(fst) => fst.len(),
            None => 0,
        };
        let shape = vec![l1, l2];

        let mut data = Vec::new();

        for row in self {
            for e in row {
                data.push(*e);
            }
        }

        QArray { shape, data }
    }
}

impl ToQArray for Vec<Vec<Vec<isize>>> {
    fn to_qarray(&self) -> QArray {
        let l1 = self.len();
        let (l2, l3) = match self.first() {
            Some(fst) => match fst.first() {
                Some(ffst) => (fst.len(), ffst.len()),
                None => (fst.len(), 0)
            },
            None => (0, 0)
        };
        let shape = vec![l1, l2, l3];

        let mut data = Vec::new();

        for row in self {
            for col in row {
                for e in col {
                    data.push(*e)
                }
            }
        };

        QArray { shape, data }
    }
}
<commit_msg>Implement `full`<commit_after>use std::iter::repeat;

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

    #[test]
    fn zeros_creates_zero_filled_vector_of_correct_size_and_length() {
        let a1 = zeros(vec![0]);
        assert_eq!(a1.shape, vec![0]);
        assert_eq!(a1.data, vec![]);

        let a2 = zeros(vec![1]);
        assert_eq!(a2.shape, vec![1]);
        assert_eq!(a2.data, vec![0]);

        let a3 = zeros(vec![10]);
        assert_eq!(a3.shape, vec![10]);
        assert_eq!(a3.data, vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);

        let a4 = zeros(vec![1, 2, 3]);
        assert_eq!(a4.shape, vec![1, 2, 3]);
        assert_eq!(a4.data, vec![0, 0, 0, 0, 0, 0]);
    }

    #[test]
    fn full_test() {
        let a1 = full(vec![0], 1);
        assert_eq!(a1.shape, vec![0]);
        assert_eq!(a1.data, vec![]);

        let a2 = full(vec![1], -3);
        assert_eq!(a2.shape, vec![1]);
        assert_eq!(a2.data, vec![-3]);

        let a3 = full(vec![10], 5);
        assert_eq!(a3.shape, vec![10]);
        assert_eq!(a3.data, vec![5, 5, 5, 5, 5, 5, 5, 5, 5, 5]);

        let a4 = full(vec![1, 2, 3], 0);
        assert_eq!(a4.shape, vec![1, 2, 3]);
        assert_eq!(a4.data, vec![0, 0, 0, 0, 0, 0]);
    }

    #[test]
    fn index_test_1() {
        let array = QArray {
            shape: vec![1, 1, 1],
            data: vec![1],
        };

        assert_eq!(array.index(vec![0, 0, 0].as_slice()), Some(1));
    }

    #[test]
    fn index_test_2() {
        let array = QArray {
            shape: vec![2, 2, 1],
            data: vec![1, 2, 3, 4],
        };

        assert_eq!(array.index(vec![1, 0, 0].as_slice()), Some(2));
        assert_eq!(array.index(vec![0, 1, 0].as_slice()), Some(3));
    }

    #[test]
    fn index_test_3() {
        let array = QArray {
            shape: vec![2, 4, 3],
            data: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
                       22, 23, 24],
        };

        assert_eq!(array.index(vec![1, 2, 1].as_slice()), Some(14));
        assert_eq!(array.index(vec![0, 2, 2].as_slice()), Some(21));
    }

    #[test]
    fn test_1d_vec_to_qarray() {
        let v = vec![1, 2, 3];
        let array = v.to_qarray();

        assert_eq!(array.shape, vec![3]);
        assert_eq!(array.data, vec![1, 2, 3]);
    }

    #[test]
    fn test_2d_vec_to_qarray() {
        let v1 = vec![vec![1, 2], vec![3, 4]];
        let array1 = v1.to_qarray();

        assert_eq!(array1.shape, vec![2, 2]);
        assert_eq!(array1.data, vec![1, 2, 3, 4]);

        let v2 = vec![vec![1, 2, 3], vec![4, 5, 6]];
        let array2 = v2.to_qarray();

        assert_eq!(array2.shape, vec![2, 3]);
        assert_eq!(array2.data, vec![1, 2, 3, 4, 5, 6]);
    }

    #[test]
    fn test_3d_vec_to_qarray() {
        let v = vec![vec![vec![1, 2], vec![3, 4]], vec![vec![5, 6], vec![7, 8]]];
        let array = v.to_qarray();

        assert_eq!(array.shape, vec![2, 2, 2]);
        assert_eq!(array.data, vec![1, 2, 3, 4, 5, 6, 7, 8]);
    }
}

/// Return a new QArray of the given shape, filled with zeroes.
///
/// ```
/// use quota::zeros;
/// let qarray = zeros(vec![1, 2, 3]);
/// assert_eq!(qarray.shape, vec![1, 2, 3]);
/// assert_eq!(qarray.data, vec![0, 0, 0, 0, 0, 0])
/// ```
pub fn zeros(shape: Vec<usize>) -> QArray {
    full(shape, 0)
}

/// Return a new QArray of the given shape, filled with the given fill value.
///
/// ```
/// use quota::full;
/// let qarray = full(vec![1, 2, 3], 2);
/// assert_eq!(qarray.shape, vec![1, 2, 3]);
/// assert_eq!(qarray.data, vec![2, 2, 2, 2, 2, 2])
/// ```
pub fn full(shape: Vec<usize>, fill_value: isize) -> QArray {
    let data = repeat(fill_value).take(shape.iter().sum()).collect::<Vec<isize>>();
    QArray { shape, data }
}

#[derive(Debug)]
pub struct QArray {
    pub shape: Vec<usize>,
    pub data: Vec<isize>,
}

impl QArray {
    /// Return the element at the given coordinates.
    /// If the coordinates don't specify a single element (e.g. 2D coordinates
    /// are given for a 3D array), or the coordinates are invalid,
    /// this function will return None.
    ///
    /// ```
    /// use quota::*;
    ///
    /// let qarray = zeros(vec![2, 2]);
    /// assert_eq!(qarray.index(vec![0, 0].as_slice()), Some(0));
    /// assert_eq!(qarray.index(vec![1, 1].as_slice()), Some(0));
    /// assert_eq!(qarray.index(vec![3, 3].as_slice()), None);
    /// assert_eq!(qarray.index(vec![1].as_slice()), None);
    /// ```
    pub fn index(&self, coordinates: &[usize]) -> Option<isize> {
        // Check that the coordinates are valid
        // The number of coordinates should match the shape of the array.
        // Each coordinate should be less than the length of the array
        // in that dimension.
        if coordinates.len() != self.shape.len() { return None }
        for (coord, dim) in coordinates.iter().zip(self.shape.as_slice()) {
            if *coord >= *dim { return None }
        }

        return Some(self.data[offset(self, coordinates)])
    }
}

fn offset(array: &QArray, coordinates: &[usize]) -> usize {
    coordinates
        .into_iter()
        .enumerate()
        .map(|(idx, coord)| {
                 array
                     .shape
                     .iter()
                     .take(idx)
                     .fold(*coord, |acc, x| acc * x)
             })
        .fold(0, |acc, x| acc + x)
}

trait ToQArray {
    fn to_qarray(&self) -> QArray;
}

impl ToQArray for Vec<isize> {
    fn to_qarray(&self) -> QArray {
        QArray {
            shape: vec![self.len()],
            data: self.clone(),
        }
    }
}

impl ToQArray for Vec<Vec<isize>> {
    fn to_qarray(&self) -> QArray {
        let l1 = self.len();
        let l2 = match self.first() {
            Some(fst) => fst.len(),
            None => 0,
        };
        let shape = vec![l1, l2];

        let mut data = Vec::new();

        for row in self {
            for e in row {
                data.push(*e);
            }
        }

        QArray { shape, data }
    }
}

impl ToQArray for Vec<Vec<Vec<isize>>> {
    fn to_qarray(&self) -> QArray {
        let l1 = self.len();
        let (l2, l3) = match self.first() {
            Some(fst) => match fst.first() {
                Some(ffst) => (fst.len(), ffst.len()),
                None => (fst.len(), 0)
            },
            None => (0, 0)
        };
        let shape = vec![l1, l2, l3];

        let mut data = Vec::new();

        for row in self {
            for col in row {
                for e in col {
                    data.push(*e)
                }
            }
        };

        QArray { shape, data }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 Yusuke Sasaki
//
// This software is released under the MIT License.
// See http://opensource.org/licenses/mit-license.php or <LICENSE>.

//! This crate provides primitive Rust APIs for Gurobi Optimizer.
//!
//! It supports some types of mathematical programming problems (e.g. Linear programming; LP,
//! Mixed Integer Linear Programming; MILP, and so on).
//!
//! ## Notices
//!
//! * Before using this crate, you should install Gurobi and obtain a license.
//!   The instruction can be found
//!   [here](http://www.gurobi.com/downloads/licenses/license-center).
//!
//! * Make sure that the environment variable `GUROBI_HOME` is set to the installation path of Gurobi
//!   (like `C:\gurobi652\win64`, `/opt/gurobi652/linux64`).
//!
//! * On Windows, the toolchain should be MSVC ABI (it also requires Visual Studio or
//!   Visual C++ Build Tools).
//!   If you want to use GNU ABI with MinGW-w64/MSYS2 toolchain, you should create the import
//!   library for Gurobi runtime DLL (e.g. `gurobi65.dll`) and put it into `GUROBI_HOME/lib`.
//!   Procedure of creating import library is as follows:
//!
//!   ```shell-session
//!   $ pacman -S mingw-w64-x86_64-tools-git
//!   $ gendef - $(cygpath $GUROBI_HOME)/bin/gurobi65.dll > gurobi65.def
//!   $ dlltool --dllname gurobi65.dll --def gurobi65.def --output-lib $(cygpath $GUROBI}HOME)/lib/libgurobi65.dll.a
//!   ```
//!
//! ## Examples
//!
//! ```
//! extern crate gurobi;
//! use gurobi::*;
//!
//! fn main() {
//!   let env = Env::new("logfile.log").unwrap();
//!
//!   // create an empty model which associated with `env`:
//!   let mut model = env.new_model("model1").unwrap();
//!
//!   // add decision variables.
//!   let x = model.add_var("x", Binary).unwrap();
//!   let y = model.add_var("y", Continuous(-10.0, 10.0)).unwrap();
//!   let z = model.add_var("z", Continuous(-INFINITY, INFINITY)).unwrap();
//!
//!   // integrate all the variables into the model.
//!   model.update().unwrap();
//!
//!   // add a linear constraint
//!   model.add_constr("c0", &x - &y + 2.0 * &z, Equal, 0.0).unwrap();
//!   // ...
//!
//!   model.set_objective(&x, Maximize).unwrap();
//!
//!   // optimize the model.
//!   model.optimize().unwrap();
//!   assert_eq!(model.status().unwrap(), Status::Optimal);
//!
//!   assert_eq!(model.get(attr::ObjVal).unwrap() , 0.0);
//!
//!   let val = model.get_values(attr::X, &[x, y, z]).unwrap();
//!   assert_eq!(val, [0.0, -10.0, -5.0]);
//! }
//! ```

extern crate gurobi_sys as ffi;
extern crate itertools;

mod attrib;
mod env;
mod error;
mod model;
mod parameter;
mod util;

// re-exports
pub use error::{Error, Result};

pub use env::Env;
pub use model::{Model, Var, Constr, QConstr, SOS, LinExpr, QuadExpr, Proxy};

pub use model::{VarType, ConstrSense, ModelSense, SOSType, Status, RelaxType};
pub use model::VarType::*;
pub use model::ConstrSense::*;
pub use model::ModelSense::*;
pub use model::SOSType::*;
pub use model::RelaxType::*;

pub use parameter::exports as param;
pub use attrib::exports as attr;

/// Large number used in C API
pub const INFINITY: f64 = 1e100;
<commit_msg>Add version()<commit_after>// Copyright (c) 2016 Yusuke Sasaki
//
// This software is released under the MIT License.
// See http://opensource.org/licenses/mit-license.php or <LICENSE>.

//! This crate provides primitive Rust APIs for Gurobi Optimizer.
//!
//! It supports some types of mathematical programming problems (e.g. Linear programming; LP,
//! Mixed Integer Linear Programming; MILP, and so on).
//!
//! ## Notices
//!
//! * Before using this crate, you should install Gurobi and obtain a license.
//!   The instruction can be found
//!   [here](http://www.gurobi.com/downloads/licenses/license-center).
//!
//! * Make sure that the environment variable `GUROBI_HOME` is set to the installation path of Gurobi
//!   (like `C:\gurobi652\win64`, `/opt/gurobi652/linux64`).
//!
//! * On Windows, the toolchain should be MSVC ABI (it also requires Visual Studio or
//!   Visual C++ Build Tools).
//!   If you want to use GNU ABI with MinGW-w64/MSYS2 toolchain, you should create the import
//!   library for Gurobi runtime DLL (e.g. `gurobi65.dll`) and put it into `GUROBI_HOME/lib`.
//!   Procedure of creating import library is as follows:
//!
//!   ```shell-session
//!   $ pacman -S mingw-w64-x86_64-tools-git
//!   $ gendef - $(cygpath $GUROBI_HOME)/bin/gurobi65.dll > gurobi65.def
//!   $ dlltool --dllname gurobi65.dll --def gurobi65.def --output-lib $(cygpath $GUROBI}HOME)/lib/libgurobi65.dll.a
//!   ```
//!
//! ## Examples
//!
//! ```
//! extern crate gurobi;
//! use gurobi::*;
//!
//! fn main() {
//!   let env = Env::new("logfile.log").unwrap();
//!
//!   // create an empty model which associated with `env`:
//!   let mut model = env.new_model("model1").unwrap();
//!
//!   // add decision variables.
//!   let x = model.add_var("x", Binary).unwrap();
//!   let y = model.add_var("y", Continuous(-10.0, 10.0)).unwrap();
//!   let z = model.add_var("z", Continuous(-INFINITY, INFINITY)).unwrap();
//!
//!   // integrate all the variables into the model.
//!   model.update().unwrap();
//!
//!   // add a linear constraint
//!   model.add_constr("c0", &x - &y + 2.0 * &z, Equal, 0.0).unwrap();
//!   // ...
//!
//!   model.set_objective(&x, Maximize).unwrap();
//!
//!   // optimize the model.
//!   model.optimize().unwrap();
//!   assert_eq!(model.status().unwrap(), Status::Optimal);
//!
//!   assert_eq!(model.get(attr::ObjVal).unwrap() , 0.0);
//!
//!   let val = model.get_values(attr::X, &[x, y, z]).unwrap();
//!   assert_eq!(val, [0.0, -10.0, -5.0]);
//! }
//! ```

extern crate gurobi_sys as ffi;
extern crate itertools;

mod attrib;
mod env;
mod error;
mod model;
mod parameter;
mod util;

// re-exports
pub use error::{Error, Result};

pub use env::Env;
pub use model::{Model, Var, Constr, QConstr, SOS, LinExpr, QuadExpr, Proxy};

pub use model::{VarType, ConstrSense, ModelSense, SOSType, Status, RelaxType};
pub use model::VarType::*;
pub use model::ConstrSense::*;
pub use model::ModelSense::*;
pub use model::SOSType::*;
pub use model::RelaxType::*;

pub use parameter::exports as param;
pub use attrib::exports as attr;

/// Large number used in C API
pub const INFINITY: f64 = 1e100;

/// Returns the version number of Gurobi
pub fn version() -> (i32, i32, i32) {
  let (mut major, mut minor, mut technical) = (0, 0, 0);
  unsafe { ffi::GRBversion(&mut major, &mut minor, &mut technical) };
  (major, minor, technical)
}
<|endoftext|>