<commit_before>use rsfml::graphics::{RectangleShape, RenderWindow, Color, Vertex, VertexArray, LinesStrip};
use rsfml::system::Vector2f;

pub struct HUD<'self> {
    priv window_size : Vector2f,
    priv background : RectangleShape<'self>,
    priv face : i32,
    priv hud_vertex_array : VertexArray
}

impl<'self> HUD<'self> {
    pub fn new(window_size : &Vector2f) -> HUD<'self> {
        HUD {
            window_size : window_size.clone(),
            background : RectangleShape::new().unwrap(),
            face : 0,
            hud_vertex_array : VertexArray::new().unwrap()
        }
    }

    pub fn update(&mut self) -> () {
        self.background.set_size2f(self.window_size.x - 20., 60.);
        self.background.set_fill_color(~Color::new_RGB(6, 1, 162));
        self.background.set_position2f(10., self.window_size.y - 70.);
    }

    fn build_line_vertex_array(&mut self, x1 : f32, x2 : f32, y1 : f32, y2 : f32, color : &Color, render_window : &mut RenderWindow) -> () {
        self.hud_vertex_array.set_primitive_type(LinesStrip);
        self.hud_vertex_array.clear();
        self.hud_vertex_array.append(&Vertex::new_with_pos_color(~Vector2f {x : x1, y : y1}, color));
        self.hud_vertex_array.append(&Vertex::new_with_pos_color(~Vector2f {x : x2, y : y2}, color));
        render_window.draw(&self.hud_vertex_array);
    }

    pub fn draw(&mut self, render_window : &mut RenderWindow) -> () {
        render_window.draw(&self.background);
        let mut border = VertexArray::new().unwrap();
        border.set_primitive_type(LinesStrip);
        border.append(&Vertex::new_with_pos_color(~Vector2f {x : self.window_size.x - 9., y : self.window_size.y - 70.}, &Color::new_RGBA(255, 255, 255, 75)));
        border.append(&Vertex::new_with_pos_color(~Vector2f {x : self.window_size.x - 9., y : self.window_size.y - 10.}, &Color::new_RGBA(255, 255, 255, 75)));
        border.append(&Vertex::new_with_pos_color(~Vector2f {x : 9., y : self.window_size.y - 10.}, &Color::new_RGBA(255, 255, 255, 75)));
        render_window.draw(&border);
        border.clear();
        border.append(&Vertex::new_with_pos_color(~Vector2f {x : self.window_size.x - 11., y : self.window_size.y - 70.}, &Color::black()));
        border.append(&Vertex::new_with_pos_color(~Vector2f {x : self.window_size.x - 11., y : self.window_size.y - 12.}, &Color::black()));
        border.append(&Vertex::new_with_pos_color(~Vector2f {x : 11., y : self.window_size.y - 12.}, &Color::black()));
        render_window.draw(&border);
        border.clear();
        border.append(&Vertex::new_with_pos_color(~Vector2f {x : 9., y : self.window_size.y - 12.}, &Color::black()));
        border.append(&Vertex::new_with_pos_color(~Vector2f {x : 9., y : self.window_size.y - 71.}, &Color::black()));
        border.append(&Vertex::new_with_pos_color(~Vector2f {x : self.window_size.x - 9., y : self.window_size.y - 71.}, &Color::black()));
        render_window.draw(&border);
        border.clear();
        border.append(&Vertex::new_with_pos_color(~Vector2f {x : 11., y : self.window_size.y - 11.}, &Color::new_RGBA(255, 255, 255, 75)));
        border.append(&Vertex::new_with_pos_color(~Vector2f {x : 11., y : self.window_size.y - 69.}, &Color::new_RGBA(255, 255, 255, 75)));
        border.append(&Vertex::new_with_pos_color(~Vector2f {x : self.window_size.x - 11., y : self.window_size.y - 69.}, &Color::new_RGBA(255, 255, 255, 75)));
        render_window.draw(&border);

        self.build_line_vertex_array(self.window_size.x, 0., self.window_size.y - 80., self.window_size.y - 80., &Color::new_RGBA(255, 255, 255, 50), render_window);
        self.build_line_vertex_array(self.window_size.x, 0., self.window_size.y - 79., self.window_size.y - 79., &Color::new_RGBA(255, 255, 255, 75), render_window);
    }
}<commit_msg>add function draw_2line to clean code<commit_after>use rsfml::graphics::{RectangleShape, RenderWindow, Color, Vertex, VertexArray, LinesStrip};
use rsfml::system::Vector2f;

pub struct HUD<'self> {
    priv window_size : Vector2f,
    priv background : RectangleShape<'self>,
    priv face : i32,
    priv hud_vertex_array : VertexArray
}

impl<'self> HUD<'self> {
    pub fn new(window_size : &Vector2f) -> HUD<'self> {
        let mut array = VertexArray::new().unwrap();
        array.set_primitive_type(LinesStrip);
        HUD {
            window_size : window_size.clone(),
            background : RectangleShape::new().unwrap(),
            face : 0,
            hud_vertex_array : array
        }
    }

    pub fn update(&mut self) -> () {
        self.background.set_size2f(self.window_size.x - 20., 60.);
        self.background.set_fill_color(~Color::new_RGB(6, 1, 162));
        self.background.set_position2f(10., self.window_size.y - 70.);
    }

    fn draw_line(&mut self, x1 : f32, x2 : f32, y1 : f32, y2 : f32, color : &Color, render_window : &mut RenderWindow) -> () {
        self.hud_vertex_array.clear();
        self.hud_vertex_array.append(&Vertex::new_with_pos_color(~Vector2f {x : x1, y : y1}, color));
        self.hud_vertex_array.append(&Vertex::new_with_pos_color(~Vector2f {x : x2, y : y2}, color));
        render_window.draw(&self.hud_vertex_array);
    }

    fn draw_2line(&mut self, x1 : f32, x2 : f32, x3 : f32, y1 : f32, y2 : f32, y3 : f32, color : &Color, render_window : &mut RenderWindow) -> () {
        self.hud_vertex_array.clear();
        self.hud_vertex_array.append(&Vertex::new_with_pos_color(~Vector2f {x : x1, y : y1}, color));
        self.hud_vertex_array.append(&Vertex::new_with_pos_color(~Vector2f {x : x2, y : y2}, color));
        self.hud_vertex_array.append(&Vertex::new_with_pos_color(~Vector2f {x : x3, y : y3}, color));
        render_window.draw(&self.hud_vertex_array);
    }

    pub fn draw(&mut self, render_window : &mut RenderWindow) -> () {
        // render_window.draw(&self.background);
        // let mut border = VertexArray::new().unwrap();
        // border.set_primitive_type(LinesStrip);
        // border.append(&Vertex::new_with_pos_color(~Vector2f {x : self.window_size.x - 9., y : self.window_size.y - 70.}, &Color::new_RGBA(255, 255, 255, 75)));
        // border.append(&Vertex::new_with_pos_color(~Vector2f {x : self.window_size.x - 9., y : self.window_size.y - 10.}, &Color::new_RGBA(255, 255, 255, 75)));
        // border.append(&Vertex::new_with_pos_color(~Vector2f {x : 9., y : self.window_size.y - 10.}, &Color::new_RGBA(255, 255, 255, 75)));
        // render_window.draw(&border);
        // border.clear();
        // border.append(&Vertex::new_with_pos_color(~Vector2f {x : self.window_size.x - 11., y : self.window_size.y - 70.}, &Color::black()));
        // border.append(&Vertex::new_with_pos_color(~Vector2f {x : self.window_size.x - 11., y : self.window_size.y - 12.}, &Color::black()));
        // border.append(&Vertex::new_with_pos_color(~Vector2f {x : 11., y : self.window_size.y - 12.}, &Color::black()));
        // render_window.draw(&border);
        // border.clear();
        // border.append(&Vertex::new_with_pos_color(~Vector2f {x : 9., y : self.window_size.y - 12.}, &Color::black()));
        // border.append(&Vertex::new_with_pos_color(~Vector2f {x : 9., y : self.window_size.y - 71.}, &Color::black()));
        // border.append(&Vertex::new_with_pos_color(~Vector2f {x : self.window_size.x - 9., y : self.window_size.y - 71.}, &Color::black()));
        // render_window.draw(&border);
        // border.clear();
        // border.append(&Vertex::new_with_pos_color(~Vector2f {x : 11., y : self.window_size.y - 11.}, &Color::new_RGBA(255, 255, 255, 75)));
        // border.append(&Vertex::new_with_pos_color(~Vector2f {x : 11., y : self.window_size.y - 69.}, &Color::new_RGBA(255, 255, 255, 75)));
        // border.append(&Vertex::new_with_pos_color(~Vector2f {x : self.window_size.x - 11., y : self.window_size.y - 69.}, &Color::new_RGBA(255, 255, 255, 75)));
        // render_window.draw(&border);
        self.draw_2line(self.window_size.x - 9., self.window_size.x - 9., 9., self.window_size.y - 70., self.window_size.y - 10., self.window_size.y - 10., &Color::new_RGBA(255, 255, 255, 75), render_window);
        self.draw_2line(self.window_size.x - 11., self.window_size.x - 11., 11., self.window_size.y - 70., self.window_size.y - 12., self.window_size.y - 12., &Color::black(), render_window);
        self.draw_2line(9., 9., self.window_size.x - 9., self.window_size.y - 12., self.window_size.y - 71., self.window_size.y - 71., &Color::black(), render_window);
        self.draw_2line(11., 11., self.window_size.x - 11., self.window_size.y - 11., self.window_size.y - 69., self.window_size.y - 69., &Color::new_RGBA(255, 255, 255, 75), render_window);
        self.draw_line(self.window_size.x, 0., self.window_size.y - 80., self.window_size.y - 80., &Color::new_RGBA(255, 255, 255, 50), render_window);
        self.draw_line(self.window_size.x, 0., self.window_size.y - 79., self.window_size.y - 79., &Color::new_RGBA(255, 255, 255, 75), render_window);
    }
}<|endoftext|>


<commit_before>//
// Copyright:: Copyright (c) 2015 Chef Software, Inc.
// License:: Apache License, Version 2.0
//
// 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.
//

use utils::{self, walk_tree_for_path, mkdir_recursive};
use utils::path_ext::is_dir;
use errors::{DeliveryError, Kind};
use types::DeliveryResult;
use std::path::{Path, PathBuf};
use http::APIClient;
use git::{self, ReviewResult};
use std::process::{Output, Command};
use std::fs;
use std::fs::File;
use std::io::prelude::*;

// README with a brief description of delivery and how to use it. This is added
// to a new project by `delivery init` so we have something to submit as the
// first change.
//
// We load this up as bytes since that's what std::io::Write takes for
// arguments.
static DELIVERY_DOT_MD_CONTENT: &'static [u8] = include_bytes!("DELIVERY.md");

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Type {
    Bitbucket,
    Github
}

#[derive(Debug, Clone)]
pub struct SourceCodeProvider {
    pub repo_name: String,
    pub organization: String,
    pub branch: String,
    pub verify_ssl: bool,
    pub kind: Type,
}

impl SourceCodeProvider {
    // Create a new `SourceCodeProvider`. Returns an error result if
    // required configuration values are missing. Expects to find
    // `scp`, `repository`, `scp-organization`, `branch`, and `ssl`.
    pub fn new(scp: &str, repo: &str, org: &str, branch: &str,
               no_ssl: bool) -> DeliveryResult<SourceCodeProvider> {
        let scp_kind = match scp {
            "github" => Type::Github,
            "bitbucket" => Type::Bitbucket,
            _ => return Err(DeliveryError{ kind: Kind::UnknownProjectType, detail:None })
        };
        if repo.to_string().is_empty()
            || org.to_string().is_empty()
            || branch.to_string().is_empty() {
            match scp_kind {
                Type::Github => return Err(
                    DeliveryError{
                        kind: Kind::OptionConstraint,
                        detail: Some(format!("Missing Github Source Code Provider attributes, specify: \
                                              repo-name, org-name and pipeline(default: master)"))
                    }
                ),
                Type::Bitbucket => return Err(
                    DeliveryError{
                        kind: Kind::OptionConstraint,
                        detail: Some(format!("Missing Bitbucket Source Code Provider attributes, specify: \
                                              repo-name, project-key and pipeline(default: master)"))
                    }
                ),
            }
        }
        Ok(SourceCodeProvider {
            kind: scp_kind,
            repo_name: repo.to_string(),
            organization: org.to_string(),
            branch: branch.to_string(),
            verify_ssl: !no_ssl,
        })
    }

    // Verify if the SCP is configured on the Delivery Server
    pub fn verify_server_config(&self, client: &APIClient) -> DeliveryResult<()> {
        match self.kind {
            Type::Github => {
                let scp_config = try!(client.get_github_server_config());
                if scp_config.is_empty() {
                    return Err(DeliveryError{ kind: Kind::NoGithubSCPConfig, detail: None })
                }
            },
            Type::Bitbucket => {
                let scp_config = try!(client.get_bitbucket_server_config());
                if scp_config.is_empty() {
                    return Err(DeliveryError{ kind: Kind::NoBitbucketSCPConfig, detail: None })
                }
            }
        }
        Ok(())
    }
}

// Create a Delivery Pipeline.
// Returns true if created, returns false if already exists.
pub fn create_delivery_pipeline(client: &APIClient, org: &String,
                                proj: &String, pipe: &String) -> DeliveryResult<bool> {
    if client.pipeline_exists(org, proj, pipe) {
        return Ok(false)
    } else {
        try!(client.create_pipeline(org, proj, pipe, Some(pipe)));
        return Ok(true)
    }
}

// Create a Delivery Project with Delivery as SCP (default).
// If the project is created, return true.
// If the project already exists, return false
pub fn create_delivery_project(client: &APIClient,
                               org: &String,
                               proj: &String) -> DeliveryResult<bool> {
    if client.project_exists(org, proj) {
        return Ok(false)
    } else {
        try!(client.create_delivery_project(org, proj));
        return Ok(true)
    }
}

// Push local content to the Delivery Server if no upstream commits.
// Returns true if commits pushed, returns false if upstream commits found.
pub fn push_project_content_to_delivery(pipeline: &str) -> DeliveryResult<bool> {
    if try!(git::server_content(pipeline)) {
        Ok(false)
    } else {
        try!(git::git_push(pipeline));
        Ok(true)
    }
}

// Create delivery remote if it doesn't exist. Returns true if created.
pub fn create_delivery_remote_if_missing(
      delivery_git_ssh_url: &String) -> DeliveryResult<bool> {
    if try!(git::config_repo(delivery_git_ssh_url, &project_path())) {
        return Ok(true)
    } else {
        return Ok(false)
    }
}

// Check to see if the origin remote is set up.
pub fn missing_github_remote() -> DeliveryResult<bool> {
    let git_remote_result = git::git_command(&["remote"], &project_path());
    match git_remote_result {
        Ok(git_result) => Ok(!git_result.stdout.contains("origin")),
        Err(e) => return Err(e)
    }
}

/// Search for the project root directory
///
/// We will walk through the provided path tree until we find the
/// git config (`.git/config`) annd then we will extract the root
/// directory.
///
/// # Examples
///
/// Having this directory tree:
/// /delivery-cli
///  ├── .git
///  │   └── config
///  ├── src
///  │   └── delivery
///  └── features
///
/// ```
/// use std::env;
/// use delivery::project::root_dir;
///
/// let root = env::current_dir().unwrap();
///
/// //Stepping into `delivery-cli/src/delivery`
/// let mut delivery_src = env::current_dir().unwrap();
/// delivery_src.push("src/delivery");
///
/// assert_eq!(root, root_dir(&delivery_src.as_path()).unwrap());
/// ```
pub fn root_dir(dir: &Path) -> DeliveryResult<PathBuf> {
    match walk_tree_for_path(&PathBuf::from(&dir), ".git/config") {
        Some(p) => {
           let git_d = p.parent().unwrap();
           let root_d = git_d.parent().unwrap();
           Ok(PathBuf::from(root_d))
        },
        None => Err(DeliveryError{kind: Kind::NoGitConfig,
                                  detail: None})
    }
}

pub fn project_path() -> PathBuf {
    root_dir(&utils::cwd()).unwrap()
}

// Return the project name from the current path
pub fn project_from_cwd() -> DeliveryResult<String> {
    let cwd = try!(self::root_dir(&utils::cwd()));
    Ok(cwd.file_name().unwrap().to_str().unwrap().to_string())
}

// Return the project name or try to extract it from the current path
pub fn project_or_from_cwd(proj: &str) -> DeliveryResult<String> {
    if proj.is_empty() {
        project_from_cwd()
    } else {
        Ok(proj.to_string())
    }
}

// Create the feature branch `add-delivery-config`
//
// This branch is created to start modifying the project repository
// In the case of a failure, we could roll back fearly easy by checking
// out master and deleting this feature branch.
//
// If feature branch created, return true, else return false.
pub fn create_feature_branch_if_missing(project_path: &PathBuf, branch_name: &str) -> DeliveryResult<bool> {
    match git::git_command(&["checkout", "-b", branch_name], project_path) {
        Ok(_) => {
            return Ok(true);
        },
        Err(e) => {
            match e.detail.clone() {
                Some(msg) => {
                    if msg.contains(&format!("A branch named '{}' already exists", branch_name)) {
                       try!(git::git_command(&["checkout", branch_name], project_path));
                        return Ok(false)
                    } else {
                        return Err(e)
                    }
                },
                // Unexpected error, raise.
                None => Err(e)
            }
        }
    }
}

// Add and commit the generated build_cookbook
pub fn add_commit_build_cookbook(custom_config_passed: &bool) -> DeliveryResult<()> {
    // .delivery is probably not yet under version control, so we have to add
    // the whole folder instead of .delivery/build_cookbook.
    try!(git::git_command(&["add", ".delivery"], &project_path()));
    let mut commit_msg = "Adds Delivery build cookbook".to_string();
    if !(*custom_config_passed) {
        commit_msg = commit_msg + " and config";
    }
    try!(git::git_command(&["commit", "-m", &commit_msg], &project_path()));
    Ok(())
}

// Create the delivery readme if it doesn't exist already.
pub fn create_delivery_readme() -> DeliveryResult<bool> {
    // NOTE: this isn't guaranteed to be in the project root; however it is only invoked via
    // `delivery init` which makes some assumptions elsewhere that the CWD is the project root.
    if !PathBuf::from("DELIVERY.md").exists() {
        let mut f = try!(File::create("DELIVERY.md"));
        try!(f.write_all(&DELIVERY_DOT_MD_CONTENT));
        Ok(true)
    } else {
        Ok(false)
    }
}

pub fn commit_delivery_readme() -> DeliveryResult<()> {
    try!(git::git_command(&["add", "DELIVERY.md"], &project_path()));
    let commit_msg = "New pipeline verification commit".to_string();
    try!(git::git_command(&["commit", "-m", &commit_msg], &project_path()));
    Ok(())
}

pub fn create_dot_delivery() -> &'static Path {
    // TODO: should we be doing some relative pathing here?
    let dot_delivery = Path::new(".delivery");
    fs::create_dir_all(dot_delivery).unwrap();
    dot_delivery
}

pub fn create_default_build_cookbook() -> DeliveryResult<Command> {
    let mut command = utils::make_command("chef");
    command.arg("generate")
        .arg("build-cookbook")
        .arg(".delivery/build_cookbook")
        .current_dir(&project_path());
    let output = try!(command.output());
    try!(handle_chef_generate_cookbook_cmd(output));
    Ok(command)
}

#[derive(Debug)]
pub enum CustomCookbookSource {
    Cached,
    Disk,
    Git
}

// Custom build_cookbook generation
//
// This method handles a custom generator which could be:
// 1) A local path
// 2) Or a git repo URL
// TODO) From Supermarket
pub fn download_or_mv_custom_build_cookbook_generator(
        generator: &Path,
        cache_path: &Path) -> DeliveryResult<CustomCookbookSource> {
    try!(mkdir_recursive(cache_path));
    if generator.has_root() {
        try!(utils::copy_recursive(&generator, &cache_path));
        return Ok(CustomCookbookSource::Disk)
    } else {
        let mut cache_generator_path: PathBuf = cache_path.to_path_buf();
        cache_generator_path.push(generator.file_name().unwrap());
        if is_dir(&cache_generator_path) {
            return Ok(CustomCookbookSource::Cached)
        } else {
            let cache_path_str = &cache_generator_path.to_string_lossy();
            let generator_str  = &generator.to_string_lossy();
            try!(git::clone(&cache_path_str, &generator_str));
            return Ok(CustomCookbookSource::Git)
        }
    }
}

// Generate the build_cookbook using ChefDK generate
pub fn chef_generate_build_cookbook_from_generator(
      generator: &Path, project_path: &Path) -> DeliveryResult<Command> {
    let mut command = utils::make_command("chef");
    command.arg("generate")
        .arg("cookbook")
        .arg(".delivery/build_cookbook")
        .arg("-g")
        .arg(generator)
        .current_dir(&project_path);

    let output = try!(command.output());
    try!(handle_chef_generate_cookbook_cmd(output));
    Ok(command)
}

// Default cookbooks generator cache path
pub fn generator_cache_path() -> DeliveryResult<PathBuf> {
    utils::home_dir(&[".delivery/cache/generator-cookbooks"])
}

fn handle_chef_generate_cookbook_cmd(output: Output) -> DeliveryResult<()> {
    if !output.status.success() {
        return Err(
            DeliveryError {
                kind: Kind::FailedToExecute,
                detail: Some(format!(
                            "Failed to execute chef generate:\n\
                            STDOUT: {}\nSTDERR: {}",
                            String::from_utf8_lossy(&output.stdout),
                            String::from_utf8_lossy(&output.stderr)
                        ))
            }
        )
    }
    Ok(())
}

pub fn review(target: &String, head: &String) -> DeliveryResult<ReviewResult> {
    if target == head {
        Err(DeliveryError{ kind: Kind::CannotReviewSameBranch, detail: None })
    } else {
        Ok(try!(git::git_push_review(head, target)))
    }
}

pub fn handle_review_result(review: &ReviewResult,
                            no_open: &bool) -> DeliveryResult<Option<String>> {
    match review.url {
        Some(ref url) => {
            if !no_open {
                try!(utils::open::item(&url));
            }
            Ok(Some(url.clone()))
        },
        None => Ok(None)
    }
}

#[cfg(test)]
mod tests {
    use std::path::Path;
    use super::root_dir;

    #[test]
    fn detect_error_if_root_project_is_not_a_git_repo() {
        // This path doesn't even exist
        // So we will expect to throw an Err(_)
        let lib_path = Path::new("/project/src/libraries");
        match root_dir(&lib_path) {
            Ok(_) => assert!(false),
            Err(_) => assert!(true)
        }
    }
}
<commit_msg>Generate build-cookbook with build-cookbook command<commit_after>//
// Copyright:: Copyright (c) 2015 Chef Software, Inc.
// License:: Apache License, Version 2.0
//
// 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.
//

use utils::{self, walk_tree_for_path, mkdir_recursive};
use utils::path_ext::is_dir;
use errors::{DeliveryError, Kind};
use types::DeliveryResult;
use std::path::{Path, PathBuf};
use http::APIClient;
use git::{self, ReviewResult};
use std::process::{Output, Command};
use std::fs;
use std::fs::File;
use std::io::prelude::*;

// README with a brief description of delivery and how to use it. This is added
// to a new project by `delivery init` so we have something to submit as the
// first change.
//
// We load this up as bytes since that's what std::io::Write takes for
// arguments.
static DELIVERY_DOT_MD_CONTENT: &'static [u8] = include_bytes!("DELIVERY.md");

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Type {
    Bitbucket,
    Github
}

#[derive(Debug, Clone)]
pub struct SourceCodeProvider {
    pub repo_name: String,
    pub organization: String,
    pub branch: String,
    pub verify_ssl: bool,
    pub kind: Type,
}

impl SourceCodeProvider {
    // Create a new `SourceCodeProvider`. Returns an error result if
    // required configuration values are missing. Expects to find
    // `scp`, `repository`, `scp-organization`, `branch`, and `ssl`.
    pub fn new(scp: &str, repo: &str, org: &str, branch: &str,
               no_ssl: bool) -> DeliveryResult<SourceCodeProvider> {
        let scp_kind = match scp {
            "github" => Type::Github,
            "bitbucket" => Type::Bitbucket,
            _ => return Err(DeliveryError{ kind: Kind::UnknownProjectType, detail:None })
        };
        if repo.to_string().is_empty()
            || org.to_string().is_empty()
            || branch.to_string().is_empty() {
            match scp_kind {
                Type::Github => return Err(
                    DeliveryError{
                        kind: Kind::OptionConstraint,
                        detail: Some(format!("Missing Github Source Code Provider attributes, specify: \
                                              repo-name, org-name and pipeline(default: master)"))
                    }
                ),
                Type::Bitbucket => return Err(
                    DeliveryError{
                        kind: Kind::OptionConstraint,
                        detail: Some(format!("Missing Bitbucket Source Code Provider attributes, specify: \
                                              repo-name, project-key and pipeline(default: master)"))
                    }
                ),
            }
        }
        Ok(SourceCodeProvider {
            kind: scp_kind,
            repo_name: repo.to_string(),
            organization: org.to_string(),
            branch: branch.to_string(),
            verify_ssl: !no_ssl,
        })
    }

    // Verify if the SCP is configured on the Delivery Server
    pub fn verify_server_config(&self, client: &APIClient) -> DeliveryResult<()> {
        match self.kind {
            Type::Github => {
                let scp_config = try!(client.get_github_server_config());
                if scp_config.is_empty() {
                    return Err(DeliveryError{ kind: Kind::NoGithubSCPConfig, detail: None })
                }
            },
            Type::Bitbucket => {
                let scp_config = try!(client.get_bitbucket_server_config());
                if scp_config.is_empty() {
                    return Err(DeliveryError{ kind: Kind::NoBitbucketSCPConfig, detail: None })
                }
            }
        }
        Ok(())
    }
}

// Create a Delivery Pipeline.
// Returns true if created, returns false if already exists.
pub fn create_delivery_pipeline(client: &APIClient, org: &String,
                                proj: &String, pipe: &String) -> DeliveryResult<bool> {
    if client.pipeline_exists(org, proj, pipe) {
        return Ok(false)
    } else {
        try!(client.create_pipeline(org, proj, pipe, Some(pipe)));
        return Ok(true)
    }
}

// Create a Delivery Project with Delivery as SCP (default).
// If the project is created, return true.
// If the project already exists, return false
pub fn create_delivery_project(client: &APIClient,
                               org: &String,
                               proj: &String) -> DeliveryResult<bool> {
    if client.project_exists(org, proj) {
        return Ok(false)
    } else {
        try!(client.create_delivery_project(org, proj));
        return Ok(true)
    }
}

// Push local content to the Delivery Server if no upstream commits.
// Returns true if commits pushed, returns false if upstream commits found.
pub fn push_project_content_to_delivery(pipeline: &str) -> DeliveryResult<bool> {
    if try!(git::server_content(pipeline)) {
        Ok(false)
    } else {
        try!(git::git_push(pipeline));
        Ok(true)
    }
}

// Create delivery remote if it doesn't exist. Returns true if created.
pub fn create_delivery_remote_if_missing(
      delivery_git_ssh_url: &String) -> DeliveryResult<bool> {
    if try!(git::config_repo(delivery_git_ssh_url, &project_path())) {
        return Ok(true)
    } else {
        return Ok(false)
    }
}

// Check to see if the origin remote is set up.
pub fn missing_github_remote() -> DeliveryResult<bool> {
    let git_remote_result = git::git_command(&["remote"], &project_path());
    match git_remote_result {
        Ok(git_result) => Ok(!git_result.stdout.contains("origin")),
        Err(e) => return Err(e)
    }
}

/// Search for the project root directory
///
/// We will walk through the provided path tree until we find the
/// git config (`.git/config`) annd then we will extract the root
/// directory.
///
/// # Examples
///
/// Having this directory tree:
/// /delivery-cli
///  ├── .git
///  │   └── config
///  ├── src
///  │   └── delivery
///  └── features
///
/// ```
/// use std::env;
/// use delivery::project::root_dir;
///
/// let root = env::current_dir().unwrap();
///
/// //Stepping into `delivery-cli/src/delivery`
/// let mut delivery_src = env::current_dir().unwrap();
/// delivery_src.push("src/delivery");
///
/// assert_eq!(root, root_dir(&delivery_src.as_path()).unwrap());
/// ```
pub fn root_dir(dir: &Path) -> DeliveryResult<PathBuf> {
    match walk_tree_for_path(&PathBuf::from(&dir), ".git/config") {
        Some(p) => {
           let git_d = p.parent().unwrap();
           let root_d = git_d.parent().unwrap();
           Ok(PathBuf::from(root_d))
        },
        None => Err(DeliveryError{kind: Kind::NoGitConfig,
                                  detail: None})
    }
}

pub fn project_path() -> PathBuf {
    root_dir(&utils::cwd()).unwrap()
}

// Return the project name from the current path
pub fn project_from_cwd() -> DeliveryResult<String> {
    let cwd = try!(self::root_dir(&utils::cwd()));
    Ok(cwd.file_name().unwrap().to_str().unwrap().to_string())
}

// Return the project name or try to extract it from the current path
pub fn project_or_from_cwd(proj: &str) -> DeliveryResult<String> {
    if proj.is_empty() {
        project_from_cwd()
    } else {
        Ok(proj.to_string())
    }
}

// Create the feature branch `add-delivery-config`
//
// This branch is created to start modifying the project repository
// In the case of a failure, we could roll back fearly easy by checking
// out master and deleting this feature branch.
//
// If feature branch created, return true, else return false.
pub fn create_feature_branch_if_missing(project_path: &PathBuf, branch_name: &str) -> DeliveryResult<bool> {
    match git::git_command(&["checkout", "-b", branch_name], project_path) {
        Ok(_) => {
            return Ok(true);
        },
        Err(e) => {
            match e.detail.clone() {
                Some(msg) => {
                    if msg.contains(&format!("A branch named '{}' already exists", branch_name)) {
                       try!(git::git_command(&["checkout", branch_name], project_path));
                        return Ok(false)
                    } else {
                        return Err(e)
                    }
                },
                // Unexpected error, raise.
                None => Err(e)
            }
        }
    }
}

// Add and commit the generated build_cookbook
pub fn add_commit_build_cookbook(custom_config_passed: &bool) -> DeliveryResult<()> {
    // .delivery is probably not yet under version control, so we have to add
    // the whole folder instead of .delivery/build_cookbook.
    try!(git::git_command(&["add", ".delivery"], &project_path()));
    let mut commit_msg = "Adds Delivery build cookbook".to_string();
    if !(*custom_config_passed) {
        commit_msg = commit_msg + " and config";
    }
    try!(git::git_command(&["commit", "-m", &commit_msg], &project_path()));
    Ok(())
}

// Create the delivery readme if it doesn't exist already.
pub fn create_delivery_readme() -> DeliveryResult<bool> {
    // NOTE: this isn't guaranteed to be in the project root; however it is only invoked via
    // `delivery init` which makes some assumptions elsewhere that the CWD is the project root.
    if !PathBuf::from("DELIVERY.md").exists() {
        let mut f = try!(File::create("DELIVERY.md"));
        try!(f.write_all(&DELIVERY_DOT_MD_CONTENT));
        Ok(true)
    } else {
        Ok(false)
    }
}

pub fn commit_delivery_readme() -> DeliveryResult<()> {
    try!(git::git_command(&["add", "DELIVERY.md"], &project_path()));
    let commit_msg = "New pipeline verification commit".to_string();
    try!(git::git_command(&["commit", "-m", &commit_msg], &project_path()));
    Ok(())
}

pub fn create_dot_delivery() -> &'static Path {
    // TODO: should we be doing some relative pathing here?
    let dot_delivery = Path::new(".delivery");
    fs::create_dir_all(dot_delivery).unwrap();
    dot_delivery
}

pub fn create_default_build_cookbook() -> DeliveryResult<Command> {
    let mut command = utils::make_command("chef");
    command.arg("generate")
        .arg("build-cookbook")
        .arg(".delivery/build_cookbook")
        .current_dir(&project_path());
    let output = try!(command.output());
    try!(handle_chef_generate_cookbook_cmd(output));
    Ok(command)
}

#[derive(Debug)]
pub enum CustomCookbookSource {
    Cached,
    Disk,
    Git
}

// Custom build_cookbook generation
//
// This method handles a custom generator which could be:
// 1) A local path
// 2) Or a git repo URL
// TODO) From Supermarket
pub fn download_or_mv_custom_build_cookbook_generator(
        generator: &Path,
        cache_path: &Path) -> DeliveryResult<CustomCookbookSource> {
    try!(mkdir_recursive(cache_path));
    if generator.has_root() {
        try!(utils::copy_recursive(&generator, &cache_path));
        return Ok(CustomCookbookSource::Disk)
    } else {
        let mut cache_generator_path: PathBuf = cache_path.to_path_buf();
        cache_generator_path.push(generator.file_name().unwrap());
        if is_dir(&cache_generator_path) {
            return Ok(CustomCookbookSource::Cached)
        } else {
            let cache_path_str = &cache_generator_path.to_string_lossy();
            let generator_str  = &generator.to_string_lossy();
            try!(git::clone(&cache_path_str, &generator_str));
            return Ok(CustomCookbookSource::Git)
        }
    }
}

// Generate the build_cookbook using ChefDK generate
pub fn chef_generate_build_cookbook_from_generator(
      generator: &Path, project_path: &Path) -> DeliveryResult<Command> {
    let mut command = utils::make_command("chef");
    command.arg("generate")
        .arg("build-cookbook")
        .arg(".")
        .arg("-g")
        .arg(generator)
        .current_dir(&project_path);

    let output = try!(command.output());
    try!(handle_chef_generate_cookbook_cmd(output));
    Ok(command)
}

// Default cookbooks generator cache path
pub fn generator_cache_path() -> DeliveryResult<PathBuf> {
    utils::home_dir(&[".delivery/cache/generator-cookbooks"])
}

fn handle_chef_generate_cookbook_cmd(output: Output) -> DeliveryResult<()> {
    if !output.status.success() {
        return Err(
            DeliveryError {
                kind: Kind::FailedToExecute,
                detail: Some(format!(
                            "Failed to execute chef generate:\n\
                            STDOUT: {}\nSTDERR: {}",
                            String::from_utf8_lossy(&output.stdout),
                            String::from_utf8_lossy(&output.stderr)
                        ))
            }
        )
    }
    Ok(())
}

pub fn review(target: &String, head: &String) -> DeliveryResult<ReviewResult> {
    if target == head {
        Err(DeliveryError{ kind: Kind::CannotReviewSameBranch, detail: None })
    } else {
        Ok(try!(git::git_push_review(head, target)))
    }
}

pub fn handle_review_result(review: &ReviewResult,
                            no_open: &bool) -> DeliveryResult<Option<String>> {
    match review.url {
        Some(ref url) => {
            if !no_open {
                try!(utils::open::item(&url));
            }
            Ok(Some(url.clone()))
        },
        None => Ok(None)
    }
}

#[cfg(test)]
mod tests {
    use std::path::Path;
    use super::root_dir;

    #[test]
    fn detect_error_if_root_project_is_not_a_git_repo() {
        // This path doesn't even exist
        // So we will expect to throw an Err(_)
        let lib_path = Path::new("/project/src/libraries");
        match root_dir(&lib_path) {
            Ok(_) => assert!(false),
            Err(_) => assert!(true)
        }
    }
}
<|endoftext|>


<commit_before>// Copyright © 2015, Peter Atashian
// Licensed under the MIT License <LICENSE.md>
//! FFI bindings to dsound.
#![cfg(windows)]
extern crate winapi;
use winapi::*;
extern "system" {
    pub fn DirectSoundCreate(
        pcGuidDevice: LPCGUID, ppDS: *mut LPDIRECTSOUND, pUnkOuter: LPUNKNOWN
    ) -> HRESULT;
}

<commit_msg>In progress dsound stuff<commit_after>// Copyright © 2015, Peter Atashian
// Licensed under the MIT License <LICENSE.md>
//! FFI bindings to dsound.
#![cfg(windows)]
extern crate winapi;
use winapi::*;
extern "system" {
    pub fn DirectSoundCaptureCreate(
        pcGuidDevice: LPCGUID, ppDSC: *mut LPDIRECTSOUNDCAPTURE, pUnkOuter: LPUNKNOWN,
    ) -> HRESULT;
    pub fn DirectSoundCaptureCreate8(
        pcGuidDevice: LPCGUID, ppDSC8: *mut LPDIRECTSOUNDCAPTURE8, pUnkOuter: LPUNKNOWN,
    ) -> HRESULT;
    pub fn DirectSoundCaptureEnumerateA(
        pDSEnumCallback: LPDSENUMCALLBACKA, pContext: LPVOID,
    ) -> HRESULT;
    pub fn DirectSoundCaptureEnumerateW(
        pDSEnumCallback: LPDSENUMCALLBACKW, pContext: LPVOID,
    ) -> HRESULT;
    pub fn DirectSoundCreate(
        pcGuidDevice: LPCGUID, ppDS: *mut LPDIRECTSOUND, pUnkOuter: LPUNKNOWN,
    ) -> HRESULT;
    pub fn DirectSoundCreate8(
        pcGuidDevice: LPCGUID, ppDS8: *mut LPDIRECTSOUND8, pUnkOuter: LPUNKNOWN,
    ) -> HRESULT;
    pub fn DirectSoundEnumerateA(
        pDSEnumCallback: LPDSENUMCALLBACKA, pContext: LPVOID,
    ) -> HRESULT;
    pub fn DirectSoundEnumerateA(
        pDSEnumCallback: LPDSENUMCALLBACKW, pContext: LPVOID,
    ) -> HRESULT;
    pub fn DirectSoundFullDuplexCreate(
        pcGuidCaptureDevice: LPCGUID, pcGuidRenderDevice: LPCGUID,
        pcDSCBufferDesc: LPCDSCBUFFERDESC, pcDSBufferDesc: LPCDSBUFFERDESC, hWnd: HWND,
        dwLevel: DWORD, ppDSFD: *mut LPDIRECTSOUNDFULLDUPLEX,
        ppDSCBuffer8: *mut LPDIRECTSOUNDCAPTUREBUFFER8, ppDSBuffer8: *mut LPDIRECTSOUNDBUFFER8,
        pUnkOuter: LPUNKNOWN,
    ) -> HRESULT;
    pub fn GetDeviceID(pGuidSrc: LPCGUID, pGuidDest: LPGUID) -> HRESULT;
}
<|endoftext|>


<commit_before>use std::process::exit;
use std::{io, thread, time};

use cpal::traits::{DeviceTrait, HostTrait};
use thiserror::Error;

use super::Sink;
use crate::audio::{AudioPacket, SamplesConverter};
use crate::config::AudioFormat;
use crate::player::{NUM_CHANNELS, SAMPLE_RATE};

#[cfg(all(
    feature = "rodiojack-backend",
    not(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd"))
))]
compile_error!("Rodio JACK backend is currently only supported on linux.");

#[cfg(feature = "rodio-backend")]
pub fn mk_rodio(device: Option<String>, format: AudioFormat) -> Box<dyn Sink> {
    Box::new(open(cpal::default_host(), device, format))
}

#[cfg(feature = "rodiojack-backend")]
pub fn mk_rodiojack(device: Option<String>, format: AudioFormat) -> Box<dyn Sink> {
    Box::new(open(
        cpal::host_from_id(cpal::HostId::Jack).unwrap(),
        device,
        format,
    ))
}

#[derive(Debug, Error)]
pub enum RodioError {
    #[error("Rodio: no device available")]
    NoDeviceAvailable,
    #[error("Rodio: device \"{0}\" is not available")]
    DeviceNotAvailable(String),
    #[error("Rodio play error: {0}")]
    PlayError(#[from] rodio::PlayError),
    #[error("Rodio stream error: {0}")]
    StreamError(#[from] rodio::StreamError),
    #[error("Cannot get audio devices: {0}")]
    DevicesError(#[from] cpal::DevicesError),
}

pub struct RodioSink {
    rodio_sink: rodio::Sink,
    format: AudioFormat,
    _stream: rodio::OutputStream,
}

fn list_formats(device: &rodio::Device) {
    match device.default_output_config() {
        Ok(cfg) => {
            debug!("  Default config:");
            debug!("    {:?}", cfg);
        }
        Err(e) => {
            // Use loglevel debug, since even the output is only debug
            debug!("Error getting default rodio::Sink config: {}", e);
        }
    };

    match device.supported_output_configs() {
        Ok(mut cfgs) => {
            if let Some(first) = cfgs.next() {
                debug!("  Available configs:");
                debug!("    {:?}", first);
            } else {
                return;
            }

            for cfg in cfgs {
                debug!("    {:?}", cfg);
            }
        }
        Err(e) => {
            debug!("Error getting supported rodio::Sink configs: {}", e);
        }
    }
}

fn list_outputs(host: &cpal::Host) -> Result<(), cpal::DevicesError> {
    let mut default_device_name = None;

    if let Some(default_device) = host.default_output_device() {
        default_device_name = default_device.name().ok();
        println!(
            "Default Audio Device:\n  {}",
            default_device_name.as_deref().unwrap_or("[unknown name]")
        );

        list_formats(&default_device);

        println!("Other Available Audio Devices:");
    } else {
        warn!("No default device was found");
    }

    for device in host.output_devices()? {
        match device.name() {
            Ok(name) if Some(&name) == default_device_name.as_ref() => (),
            Ok(name) => {
                println!("  {}", name);
                list_formats(&device);
            }
            Err(e) => {
                warn!("Cannot get device name: {}", e);
                println!("   [unknown name]");
                list_formats(&device);
            }
        }
    }

    Ok(())
}

fn create_sink(
    host: &cpal::Host,
    device: Option<String>,
) -> Result<(rodio::Sink, rodio::OutputStream), RodioError> {
    let rodio_device = match device {
        Some(ask) if &ask == "?" => {
            let exit_code = match list_outputs(host) {
                Ok(()) => 0,
                Err(e) => {
                    error!("{}", e);
                    1
                }
            };
            exit(exit_code)
        }
        Some(device_name) => {
            host.output_devices()?
                .find(|d| d.name().ok().map_or(false, |name| name == device_name)) // Ignore devices for which getting name fails
                .ok_or(RodioError::DeviceNotAvailable(device_name))?
        }
        None => host
            .default_output_device()
            .ok_or(RodioError::NoDeviceAvailable)?,
    };

    let name = rodio_device.name().ok();
    info!(
        "Using audio device: {}",
        name.as_deref().unwrap_or("[unknown name]")
    );

    let (stream, handle) = rodio::OutputStream::try_from_device(&rodio_device)?;
    let sink = rodio::Sink::try_new(&handle)?;
    Ok((sink, stream))
}

pub fn open(host: cpal::Host, device: Option<String>, format: AudioFormat) -> RodioSink {
    debug!(
        "Using rodio sink with format {:?} and cpal host: {}",
        format,
        host.id().name()
    );

    match format {
        AudioFormat::F32 => {
            #[cfg(target_os = "linux")]
            warn!("Rodio output to Alsa is known to cause garbled sound, consider using `--backend alsa`")
        }
        AudioFormat::S16 => (),
        _ => unimplemented!("Rodio currently only supports F32 and S16 formats"),
    }

    let (sink, stream) = create_sink(&host, device).unwrap();

    debug!("Rodio sink was created");
    RodioSink {
        rodio_sink: sink,
        format,
        _stream: stream,
    }
}

impl Sink for RodioSink {
    start_stop_noop!();

    fn write(&mut self, packet: &AudioPacket) -> io::Result<()> {
        let samples = packet.samples();
        match self.format {
            AudioFormat::F32 => {
                let source =
                    rodio::buffer::SamplesBuffer::new(NUM_CHANNELS as u16, SAMPLE_RATE, samples);
                self.rodio_sink.append(source);
            }
            AudioFormat::S16 => {
                let samples_s16: &[i16] = &SamplesConverter::to_s16(samples);
                let source = rodio::buffer::SamplesBuffer::new(
                    NUM_CHANNELS as u16,
                    SAMPLE_RATE,
                    samples_s16,
                );
                self.rodio_sink.append(source);
            }
            _ => unreachable!(),
        };
        let source = rodio::buffer::SamplesBuffer::new(2, 44100, packet.samples());
        self.rodio_sink.append(source);

        // Chunk sizes seem to be about 256 to 3000 ish items long.
        // Assuming they're on average 1628 then a half second buffer is:
        // 44100 elements --> about 27 chunks
        while self.rodio_sink.len() > 26 {
            // sleep and wait for rodio to drain a bit
            thread::sleep(time::Duration::from_millis(10));
        }
        Ok(())
    }
}
<commit_msg>Fix bug in rodio backend<commit_after>use std::process::exit;
use std::{io, thread, time};

use cpal::traits::{DeviceTrait, HostTrait};
use thiserror::Error;

use super::Sink;
use crate::audio::{AudioPacket, SamplesConverter};
use crate::config::AudioFormat;
use crate::player::{NUM_CHANNELS, SAMPLE_RATE};

#[cfg(all(
    feature = "rodiojack-backend",
    not(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd"))
))]
compile_error!("Rodio JACK backend is currently only supported on linux.");

#[cfg(feature = "rodio-backend")]
pub fn mk_rodio(device: Option<String>, format: AudioFormat) -> Box<dyn Sink> {
    Box::new(open(cpal::default_host(), device, format))
}

#[cfg(feature = "rodiojack-backend")]
pub fn mk_rodiojack(device: Option<String>, format: AudioFormat) -> Box<dyn Sink> {
    Box::new(open(
        cpal::host_from_id(cpal::HostId::Jack).unwrap(),
        device,
        format,
    ))
}

#[derive(Debug, Error)]
pub enum RodioError {
    #[error("Rodio: no device available")]
    NoDeviceAvailable,
    #[error("Rodio: device \"{0}\" is not available")]
    DeviceNotAvailable(String),
    #[error("Rodio play error: {0}")]
    PlayError(#[from] rodio::PlayError),
    #[error("Rodio stream error: {0}")]
    StreamError(#[from] rodio::StreamError),
    #[error("Cannot get audio devices: {0}")]
    DevicesError(#[from] cpal::DevicesError),
}

pub struct RodioSink {
    rodio_sink: rodio::Sink,
    format: AudioFormat,
    _stream: rodio::OutputStream,
}

fn list_formats(device: &rodio::Device) {
    match device.default_output_config() {
        Ok(cfg) => {
            debug!("  Default config:");
            debug!("    {:?}", cfg);
        }
        Err(e) => {
            // Use loglevel debug, since even the output is only debug
            debug!("Error getting default rodio::Sink config: {}", e);
        }
    };

    match device.supported_output_configs() {
        Ok(mut cfgs) => {
            if let Some(first) = cfgs.next() {
                debug!("  Available configs:");
                debug!("    {:?}", first);
            } else {
                return;
            }

            for cfg in cfgs {
                debug!("    {:?}", cfg);
            }
        }
        Err(e) => {
            debug!("Error getting supported rodio::Sink configs: {}", e);
        }
    }
}

fn list_outputs(host: &cpal::Host) -> Result<(), cpal::DevicesError> {
    let mut default_device_name = None;

    if let Some(default_device) = host.default_output_device() {
        default_device_name = default_device.name().ok();
        println!(
            "Default Audio Device:\n  {}",
            default_device_name.as_deref().unwrap_or("[unknown name]")
        );

        list_formats(&default_device);

        println!("Other Available Audio Devices:");
    } else {
        warn!("No default device was found");
    }

    for device in host.output_devices()? {
        match device.name() {
            Ok(name) if Some(&name) == default_device_name.as_ref() => (),
            Ok(name) => {
                println!("  {}", name);
                list_formats(&device);
            }
            Err(e) => {
                warn!("Cannot get device name: {}", e);
                println!("   [unknown name]");
                list_formats(&device);
            }
        }
    }

    Ok(())
}

fn create_sink(
    host: &cpal::Host,
    device: Option<String>,
) -> Result<(rodio::Sink, rodio::OutputStream), RodioError> {
    let rodio_device = match device {
        Some(ask) if &ask == "?" => {
            let exit_code = match list_outputs(host) {
                Ok(()) => 0,
                Err(e) => {
                    error!("{}", e);
                    1
                }
            };
            exit(exit_code)
        }
        Some(device_name) => {
            host.output_devices()?
                .find(|d| d.name().ok().map_or(false, |name| name == device_name)) // Ignore devices for which getting name fails
                .ok_or(RodioError::DeviceNotAvailable(device_name))?
        }
        None => host
            .default_output_device()
            .ok_or(RodioError::NoDeviceAvailable)?,
    };

    let name = rodio_device.name().ok();
    info!(
        "Using audio device: {}",
        name.as_deref().unwrap_or("[unknown name]")
    );

    let (stream, handle) = rodio::OutputStream::try_from_device(&rodio_device)?;
    let sink = rodio::Sink::try_new(&handle)?;
    Ok((sink, stream))
}

pub fn open(host: cpal::Host, device: Option<String>, format: AudioFormat) -> RodioSink {
    debug!(
        "Using rodio sink with format {:?} and cpal host: {}",
        format,
        host.id().name()
    );

    match format {
        AudioFormat::F32 => {
            #[cfg(target_os = "linux")]
            warn!("Rodio output to Alsa is known to cause garbled sound, consider using `--backend alsa`")
        }
        AudioFormat::S16 => (),
        _ => unimplemented!("Rodio currently only supports F32 and S16 formats"),
    }

    let (sink, stream) = create_sink(&host, device).unwrap();

    debug!("Rodio sink was created");
    RodioSink {
        rodio_sink: sink,
        format,
        _stream: stream,
    }
}

impl Sink for RodioSink {
    start_stop_noop!();

    fn write(&mut self, packet: &AudioPacket) -> io::Result<()> {
        let samples = packet.samples();
        match self.format {
            AudioFormat::F32 => {
                let source =
                    rodio::buffer::SamplesBuffer::new(NUM_CHANNELS as u16, SAMPLE_RATE, samples);
                self.rodio_sink.append(source);
            }
            AudioFormat::S16 => {
                let samples_s16: &[i16] = &SamplesConverter::to_s16(samples);
                let source = rodio::buffer::SamplesBuffer::new(
                    NUM_CHANNELS as u16,
                    SAMPLE_RATE,
                    samples_s16,
                );
                self.rodio_sink.append(source);
            }
            _ => unreachable!(),
        };

        // Chunk sizes seem to be about 256 to 3000 ish items long.
        // Assuming they're on average 1628 then a half second buffer is:
        // 44100 elements --> about 27 chunks
        while self.rodio_sink.len() > 26 {
            // sleep and wait for rodio to drain a bit
            thread::sleep(time::Duration::from_millis(10));
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before>use packets;
use std::iter;
use std::cmp;
use std::convert::From;
use std::borrow;

//This has to be small enough to leave room for the header.
const CHUNK_SIZE: usize = 500;

#[derive(Debug)]
pub struct FrameEncoder<'A, T: 'A> {
    channel: i16,
    sn: u64,
    last_reliable_frame: u64,
    reliable: bool,
    first: bool,
    iter: &'A mut T,
    workspace: Vec<u8>,
}

impl<'A, T, Q> FrameEncoder<'A, T>
where T: iter::ExactSizeIterator+iter::Iterator<Item=Q>,
Q: borrow::Borrow<u8>,
Vec<u8>: iter::Extend<Q> {
    pub fn new(iter: &'A mut T, channel: i16, starting_sequence_number: u64, last_reliable_frame: u64, reliable: bool)->FrameEncoder<'A, T> {
        FrameEncoder {
            channel: channel,
            sn: starting_sequence_number,
            last_reliable_frame: last_reliable_frame,
            reliable: reliable,
            first: true,
            iter: iter,
            workspace: Vec::with_capacity(CHUNK_SIZE),
        }
    }
}

impl<'A, T, Q> iter::Iterator for FrameEncoder<'A, T>
where T: iter::ExactSizeIterator+iter::Iterator<Item=Q>,
Q: borrow::Borrow<u8>,
Vec<u8>: iter::Extend<Q> {
    type Item = packets::Packet;

    fn next(&mut self)->Option<packets::Packet> {
        let mut header = None;
        if self.first {
            let length = packets::FRAME_HEADER_SIZE+self.iter.len();
            header = Some(packets::FrameHeader{last_reliable_frame: self.last_reliable_frame, length: length as u32});
        }
        //Get some bytes.
        self.workspace.clear();
        self.workspace.extend(self.iter.take(CHUNK_SIZE));
        if self.first == false && self.workspace.len() == 0 {return None;}
        let dp = packets::DataPacketBuilder::with_payload_and_header(self.sn, self.workspace.clone(), header)
        .set_reliable(self.reliable)
        .set_frame_start(self.first)
        .set_frame_end(self.iter.len() == 0)
        .build();
        self.first = false;
        self.sn += 1;
        return Some(packets::Packet::Data{chan: self.channel, packet: dp});
    }
}

#[test]
fn test_frames() {
    let test_data: Vec<u8> = vec![1u8; 1000];
    //channel 100, start at sn 3, last reliable is 1.
    let mut got_packets = FrameEncoder::new(&mut test_data.iter(), 100, 3, 1, false).collect::<Vec<_>>();
    let mut expected_packets = vec![
        packets::Packet::Data{chan: 100,
            packet: packets::DataPacketBuilder::with_payload_and_header(3, vec![1u8; 500], Some(packets::FrameHeader{length: 1012, last_reliable_frame: 1})).build()
        },
        packets::Packet::Data{chan: 100,
            packet: packets::DataPacketBuilder::with_payload(4, vec![1u8; 500]).set_frame_end(true).build()
        }
    ];
    assert_eq!(got_packets, expected_packets);
    got_packets = FrameEncoder::new(&mut test_data.iter(), 100, 3, 1, true).collect();
    expected_packets = vec![
        packets::Packet::Data{chan: 100,
            packet: packets::DataPacketBuilder::with_payload_and_header(3, vec![1u8; 500], Some(packets::FrameHeader{length: 1012, last_reliable_frame: 1})).set_reliable(true).build()
        },
        packets::Packet::Data{chan: 100,
            packet: packets::DataPacketBuilder::with_payload(4, vec![1u8; 500]).set_reliable(true).set_frame_end(true).build(),
        }
    ];
    assert_eq!(got_packets, expected_packets);
}
<commit_msg>FrameEncoder implements ExactSizeIterator. Includes tests.<commit_after>use packets;
use std::iter;
use std::cmp;
use std::convert::From;
use std::borrow;

//This has to be small enough to leave room for the header.
const CHUNK_SIZE: usize = 500;

#[derive(Debug)]
pub struct FrameEncoder<'A, T: 'A> {
    channel: i16,
    sn: u64,
    last_reliable_frame: u64,
    reliable: bool,
    first: bool,
    iter: &'A mut T,
    workspace: Vec<u8>,
}

impl<'A, T, Q> FrameEncoder<'A, T>
where T: iter::ExactSizeIterator+iter::Iterator<Item=Q>,
Q: borrow::Borrow<u8>,
Vec<u8>: iter::Extend<Q> {
    pub fn new(iter: &'A mut T, channel: i16, starting_sequence_number: u64, last_reliable_frame: u64, reliable: bool)->FrameEncoder<'A, T> {
        FrameEncoder {
            channel: channel,
            sn: starting_sequence_number,
            last_reliable_frame: last_reliable_frame,
            reliable: reliable,
            first: true,
            iter: iter,
            workspace: Vec::with_capacity(CHUNK_SIZE),
        }
    }
}

impl<'A, T, Q> iter::Iterator for FrameEncoder<'A, T>
where T: iter::ExactSizeIterator+iter::Iterator<Item=Q>,
Q: borrow::Borrow<u8>,
Vec<u8>: iter::Extend<Q> {
    type Item = packets::Packet;

    fn next(&mut self)->Option<packets::Packet> {
        let mut header = None;
        if self.first {
            let length = packets::FRAME_HEADER_SIZE+self.iter.len();
            header = Some(packets::FrameHeader{last_reliable_frame: self.last_reliable_frame, length: length as u32});
        }
        //Get some bytes.
        self.workspace.clear();
        self.workspace.extend(self.iter.take(CHUNK_SIZE));
        if self.first == false && self.workspace.len() == 0 {return None;}
        let dp = packets::DataPacketBuilder::with_payload_and_header(self.sn, self.workspace.clone(), header)
        .set_reliable(self.reliable)
        .set_frame_start(self.first)
        .set_frame_end(self.iter.len() == 0)
        .build();
        self.first = false;
        self.sn += 1;
        return Some(packets::Packet::Data{chan: self.channel, packet: dp});
    }
}

impl<'A, T, Q> iter::ExactSizeIterator for FrameEncoder<'A, T>
where T: iter::ExactSizeIterator+iter::Iterator<Item=Q>,
Q: borrow::Borrow<u8>,
Vec<u8>: iter::Extend<Q> {
    fn len(&self)->usize {
        let mut res = self.iter.len()/CHUNK_SIZE;
        if res*CHUNK_SIZE < self.iter.len() {res += 1}
        if res ==0 && self.first == true {res = 1}
        return res;
    }
}

#[test]
fn test_frame_encoding() {
    let test_data: Vec<u8> = vec![1u8; 1000];
    //channel 100, start at sn 3, last reliable is 1.
    let mut got_packets = FrameEncoder::new(&mut test_data.iter(), 100, 3, 1, false).collect::<Vec<_>>();
    let mut expected_packets = vec![
        packets::Packet::Data{chan: 100,
            packet: packets::DataPacketBuilder::with_payload_and_header(3, vec![1u8; 500], Some(packets::FrameHeader{length: 1012, last_reliable_frame: 1})).build()
        },
        packets::Packet::Data{chan: 100,
            packet: packets::DataPacketBuilder::with_payload(4, vec![1u8; 500]).set_frame_end(true).build()
        }
    ];
    assert_eq!(got_packets, expected_packets);
    got_packets = FrameEncoder::new(&mut test_data.iter(), 100, 3, 1, true).collect();
    expected_packets = vec![
        packets::Packet::Data{chan: 100,
            packet: packets::DataPacketBuilder::with_payload_and_header(3, vec![1u8; 500], Some(packets::FrameHeader{length: 1012, last_reliable_frame: 1})).set_reliable(true).build()
        },
        packets::Packet::Data{chan: 100,
            packet: packets::DataPacketBuilder::with_payload(4, vec![1u8; 500]).set_reliable(true).set_frame_end(true).build(),
        }
    ];
    assert_eq!(got_packets, expected_packets);
}

macro_rules! lentest {
    ($a: expr, $b: expr) => {
        assert_eq!(FrameEncoder::new(&mut [0u8; $a].iter(), 0, 0, 0, false).len(), $b);
    }
}

#[test]
fn test_frame_exact_size_iterator() {
    lentest!(0, 1);
    lentest!(499, 1);
    lentest!(501, 2);
    lentest!(1000, 2);
    lentest!(2001, 5);
}<|endoftext|>


<commit_before>// Copyright (c) 2018 Fabian Schuiki

//! Physical types.

use std::fmt::{self, Display};
use std::ops::Deref;
use std::iter::{once, repeat};

pub use num::BigInt;

use common::name::Name;
use ty2::prelude::*;
use ty2::ScalarSubtype;

/// A physical type.
///
/// This can either be an `PhysicalBasetype` or a `PhysicalSubtype`.
pub trait PhysicalType: Type {
    /// Convert to a type.
    fn as_type(&self) -> &Type;

    /// The range of values this physical type can assume.
    fn range(&self) -> &Range<BigInt>;

    /// The units of measure of this type.
    fn units(&self) -> &[PhysicalUnit];

    /// The index of the primary unit.
    fn primary_index(&self) -> usize;

    /// The base type of this physical type.
    fn base_type(&self) -> &Type;

    /// The resolution function associated with this type.
    fn resolution_func(&self) -> Option<usize> {
        None
    }

    /// Returns `Some` if self is a `PhysicalBasetype`, `None` otherwise.
    fn as_basetype(&self) -> Option<&PhysicalBasetype> {
        None
    }

    /// Returns `Some` if self is a `PhysicalSubtype`, `None` otherwise.
    fn as_subtype(&self) -> Option<&PhysicalSubtype> {
        None
    }

    /// Returns an `&PhysicalBasetype` or panics if the type is not a basetype.
    fn unwrap_basetype(&self) -> &PhysicalBasetype {
        self.as_basetype().expect("physical type is not a basetype")
    }

    /// Returns an `&PhysicalSubtype` or panics if the type is not a subtype.
    fn unwrap_subtype(&self) -> &PhysicalSubtype {
        self.as_subtype().expect("physical type is not a subtype")
    }

    /// Check if two physical types are equal.
    fn is_equal(&self, other: &PhysicalType) -> bool;
}

impl<'t> PartialEq for PhysicalType + 't {
    fn eq(&self, other: &PhysicalType) -> bool {
        PhysicalType::is_equal(self, other)
    }
}

impl<'t> Eq for PhysicalType + 't {}

macro_rules! common_type_impl {
    () => {
        fn is_scalar(&self) -> bool {
            true
        }

        fn is_discrete(&self) -> bool {
            false
        }

        fn is_numeric(&self) -> bool {
            true
        }

        fn is_composite(&self) -> bool {
            false
        }

        fn as_any(&self) -> AnyType {
            AnyType::Physical(self)
        }
    }
}

/// A physical base type.
///
/// In VHDL a physical type is an integer multiple of some measurement unit.
/// A physical type has exactly one primary unit, and multiple secondary units
/// defined as multiples of that primary unit.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PhysicalBasetype {
    /// The range of integer multiples of the primary unit.
    range: Range<BigInt>,
    /// The units of this type.
    units: Vec<PhysicalUnit>,
    /// The index of the primary unit.
    primary: usize,
}

impl PhysicalBasetype {
    /// Create a new physical type.
    ///
    /// # Example
    ///
    /// ```
    /// use moore_vhdl::ty2::{PhysicalBasetype, PhysicalUnit, Range};
    /// use moore_vhdl::common::name::get_name_table;
    ///
    /// let ty = PhysicalBasetype::new(Range::ascending(0, 1_000_000), vec![
    ///     PhysicalUnit::primary(get_name_table().intern("fs", false), 1),
    ///     PhysicalUnit::secondary(get_name_table().intern("ps", false), 1_000, 1000, 0),
    ///     PhysicalUnit::secondary(get_name_table().intern("ns", false), 1_000_000, 1000, 1),
    /// ], 0);
    ///
    /// assert_eq!(format!("{}", ty), "0 to 1000000 units (fs, ps, ns)");
    /// ```
    pub fn new<I>(range: Range<BigInt>, units: I, primary: usize) -> PhysicalBasetype
    where
        I: IntoIterator<Item = PhysicalUnit>,
    {
        PhysicalBasetype {
            range: range,
            units: units.into_iter().collect(),
            primary: primary,
        }
    }
}

impl Type for PhysicalBasetype {
    common_type_impl!();

    fn into_owned<'a>(self) -> OwnedType<'a>
    where
        Self: 'a,
    {
        OwnedType::PhysicalBasetype(self)
    }

    fn to_owned<'a>(&self) -> OwnedType<'a>
    where
        Self: 'a,
    {
        OwnedType::PhysicalBasetype(self.clone())
    }
}

impl PhysicalType for PhysicalBasetype {
    fn as_type(&self) -> &Type {
        self
    }

    fn range(&self) -> &Range<BigInt> {
        &self.range
    }

    fn units(&self) -> &[PhysicalUnit] {
        &self.units
    }

    fn primary_index(&self) -> usize {
        self.primary
    }

    fn base_type(&self) -> &Type {
        self
    }

    fn as_basetype(&self) -> Option<&PhysicalBasetype> {
        Some(self)
    }

    fn is_equal(&self, other: &PhysicalType) -> bool {
        other.as_basetype().map(|t| self == t).unwrap_or(false)
    }
}

impl Display for PhysicalBasetype {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{} units (", self.range)?;
        for (sep, unit) in once("").chain(repeat(", ")).zip(self.units.iter()) {
            write!(f, "{}{}", sep, unit.name)?;
        }
        write!(f, ")")?;
        Ok(())
    }
}

impl Deref for PhysicalBasetype {
    type Target = Range<BigInt>;
    fn deref(&self) -> &Range<BigInt> {
        &self.range
    }
}

/// A subtype of an integer type.
pub type PhysicalSubtype<'t> = ScalarSubtype<'t, PhysicalType, BigInt>;

impl<'t> PhysicalSubtype<'t> {
    /// Create a new integer subtype.
    ///
    /// Returns `Some(...)` if `range` is a subrange of the integer, or `None`
    /// otherwise.
    ///
    /// # Example
    ///
    /// ```
    /// use moore_vhdl::ty2::{Type, TypeMark, PhysicalBasetype, PhysicalSubtype, Range};
    /// use moore_vhdl::common::name::get_name_table;
    ///
    /// let ty = PhysicalBasetype::new(Range::ascending(-1000isize, 1000isize), vec![
    ///     PhysicalUnit::primary(get_name_table().intern("fs", false), 1),
    ///     PhysicalUnit::secondary(get_name_table().intern("ps", false), 1000, 1000, 0),
    /// ], 0);
    /// let tm = TypeMark::new(
    ///     get_name_table().intern("TIME", false),
    ///     &ty,
    /// );
    /// let a = PhysicalSubtype::new(&tm, Range::ascending(0isize, 100isize)).unwrap();
    /// let b = PhysicalSubtype::new(&tm, Range::descending(100isize, 0isize)).unwrap();
    ///
    /// assert_eq!(format!("{}", a), "TIME range 0 to 100");
    /// assert_eq!(format!("{}", b), "TIME range 100 downto 0");
    /// ```
    pub fn new(mark: &'t TypeMark<'t>, range: Range<BigInt>) -> Option<PhysicalSubtype<'t>> {
        let base = mark.as_any().as_physical()?;
        let base_range = base.range();
        if base_range.has_subrange(&range) {
            Some(PhysicalSubtype {
                resfn: base.resolution_func(),
                mark: mark,
                base: base,
                con: range,
            })
        } else {
            None
        }
    }
}

impl<'t> Type for PhysicalSubtype<'t> {
    common_type_impl!();

    fn into_owned<'a>(self) -> OwnedType<'a>
    where
        Self: 'a,
    {
        OwnedType::PhysicalSubtype(self)
    }

    fn to_owned<'a>(&self) -> OwnedType<'a>
    where
        Self: 'a,
    {
        OwnedType::PhysicalSubtype(self.clone())
    }
}

impl<'t> PhysicalType for PhysicalSubtype<'t> {
    fn as_type(&self) -> &Type {
        self
    }

    fn range(&self) -> &Range<BigInt> {
        &self.con
    }

    fn units(&self) -> &[PhysicalUnit] {
        self.base.units()
    }

    fn primary_index(&self) -> usize {
        self.base.primary_index()
    }

    fn base_type(&self) -> &Type {
        self.base.as_type()
    }

    fn as_subtype(&self) -> Option<&PhysicalSubtype> {
        Some(self)
    }

    fn is_equal(&self, other: &PhysicalType) -> bool {
        other.as_subtype().map(|t| self == t).unwrap_or(false)
    }
}

impl<'t> Display for PhysicalSubtype<'t> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{} range {}", self.mark, self.con)
    }
}

/// A unit of a physical type.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PhysicalUnit {
    /// The name of the unit.
    pub name: Name,
    /// The scale of the unit with respect to the physical type's primary unit.
    pub abs: BigInt,
    /// The scale of the unit with respect to another unit.
    pub rel: Option<(BigInt, usize)>,
}

impl PhysicalUnit {
    /// Create a new unit.
    ///
    /// # Example
    ///
    /// ```
    /// use moore_vhdl::ty2::{PhysicalUnit, BigInt};
    /// use moore_vhdl::common::name::get_name_table;
    ///
    /// let name = get_name_table().intern("fs", false);
    /// let unit = PhysicalUnit::new(name, 1, Some((1000, 0)));
    ///
    /// assert_eq!(unit.name, name);
    /// assert_eq!(unit.abs, BigInt::from(1));
    /// assert_eq!(unit.rel, Some((BigInt::from(1000), 0)));
    /// ```
    pub fn new<A, R>(name: Name, abs: A, rel: Option<(R, usize)>) -> PhysicalUnit
    where
        BigInt: From<A> + From<R>,
    {
        PhysicalUnit {
            name: name,
            abs: abs.into(),
            rel: rel.map(|(rel, rel_to)| (rel.into(), rel_to)),
        }
    }

    /// Create a new primary unit.
    ///
    /// # Example
    ///
    /// ```
    /// use moore_vhdl::ty2::{PhysicalUnit, BigInt};
    /// use moore_vhdl::common::name::get_name_table;
    ///
    /// let name = get_name_table().intern("fs", false);
    /// let unit = PhysicalUnit::primary(name, 1);
    ///
    /// assert_eq!(unit.name, name);
    /// assert_eq!(unit.abs, BigInt::from(1));
    /// assert_eq!(unit.rel, None);
    /// ```
    pub fn primary<A>(name: Name, abs: A) -> PhysicalUnit
    where
        BigInt: From<A>,
    {
        PhysicalUnit {
            name: name,
            abs: abs.into(),
            rel: None,
        }
    }

    /// Create a new secondary unit.
    ///
    /// # Example
    ///
    /// ```
    /// use moore_vhdl::ty2::{PhysicalUnit, BigInt};
    /// use moore_vhdl::common::name::get_name_table;
    ///
    /// let name = get_name_table().intern("fs", false);
    /// let unit = PhysicalUnit::secondary(name, 1, 1000, 0);
    ///
    /// assert_eq!(unit.name, name);
    /// assert_eq!(unit.abs, BigInt::from(1));
    /// assert_eq!(unit.rel, Some((BigInt::from(1000), 0)));
    /// ```
    pub fn secondary<A, R>(name: Name, abs: A, rel: R, rel_to: usize) -> PhysicalUnit
    where
        BigInt: From<A> + From<R>,
    {
        PhysicalUnit {
            name: name,
            abs: abs.into(),
            rel: Some((rel.into(), rel_to)),
        }
    }
}
<commit_msg>vhdl: fix physical type test<commit_after>// Copyright (c) 2018 Fabian Schuiki

//! Physical types.

use std::fmt::{self, Display};
use std::ops::Deref;
use std::iter::{once, repeat};

pub use num::BigInt;

use common::name::Name;
use ty2::prelude::*;
use ty2::ScalarSubtype;

/// A physical type.
///
/// This can either be an `PhysicalBasetype` or a `PhysicalSubtype`.
pub trait PhysicalType: Type {
    /// Convert to a type.
    fn as_type(&self) -> &Type;

    /// The range of values this physical type can assume.
    fn range(&self) -> &Range<BigInt>;

    /// The units of measure of this type.
    fn units(&self) -> &[PhysicalUnit];

    /// The index of the primary unit.
    fn primary_index(&self) -> usize;

    /// The base type of this physical type.
    fn base_type(&self) -> &Type;

    /// The resolution function associated with this type.
    fn resolution_func(&self) -> Option<usize> {
        None
    }

    /// Returns `Some` if self is a `PhysicalBasetype`, `None` otherwise.
    fn as_basetype(&self) -> Option<&PhysicalBasetype> {
        None
    }

    /// Returns `Some` if self is a `PhysicalSubtype`, `None` otherwise.
    fn as_subtype(&self) -> Option<&PhysicalSubtype> {
        None
    }

    /// Returns an `&PhysicalBasetype` or panics if the type is not a basetype.
    fn unwrap_basetype(&self) -> &PhysicalBasetype {
        self.as_basetype().expect("physical type is not a basetype")
    }

    /// Returns an `&PhysicalSubtype` or panics if the type is not a subtype.
    fn unwrap_subtype(&self) -> &PhysicalSubtype {
        self.as_subtype().expect("physical type is not a subtype")
    }

    /// Check if two physical types are equal.
    fn is_equal(&self, other: &PhysicalType) -> bool;
}

impl<'t> PartialEq for PhysicalType + 't {
    fn eq(&self, other: &PhysicalType) -> bool {
        PhysicalType::is_equal(self, other)
    }
}

impl<'t> Eq for PhysicalType + 't {}

macro_rules! common_type_impl {
    () => {
        fn is_scalar(&self) -> bool {
            true
        }

        fn is_discrete(&self) -> bool {
            false
        }

        fn is_numeric(&self) -> bool {
            true
        }

        fn is_composite(&self) -> bool {
            false
        }

        fn as_any(&self) -> AnyType {
            AnyType::Physical(self)
        }
    }
}

/// A physical base type.
///
/// In VHDL a physical type is an integer multiple of some measurement unit.
/// A physical type has exactly one primary unit, and multiple secondary units
/// defined as multiples of that primary unit.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PhysicalBasetype {
    /// The range of integer multiples of the primary unit.
    range: Range<BigInt>,
    /// The units of this type.
    units: Vec<PhysicalUnit>,
    /// The index of the primary unit.
    primary: usize,
}

impl PhysicalBasetype {
    /// Create a new physical type.
    ///
    /// # Example
    ///
    /// ```
    /// use moore_vhdl::ty2::{PhysicalBasetype, PhysicalUnit, Range};
    /// use moore_vhdl::common::name::get_name_table;
    ///
    /// let ty = PhysicalBasetype::new(Range::ascending(0, 1_000_000), vec![
    ///     PhysicalUnit::primary(get_name_table().intern("fs", false), 1),
    ///     PhysicalUnit::secondary(get_name_table().intern("ps", false), 1_000, 1000, 0),
    ///     PhysicalUnit::secondary(get_name_table().intern("ns", false), 1_000_000, 1000, 1),
    /// ], 0);
    ///
    /// assert_eq!(format!("{}", ty), "0 to 1000000 units (fs, ps, ns)");
    /// ```
    pub fn new<I>(range: Range<BigInt>, units: I, primary: usize) -> PhysicalBasetype
    where
        I: IntoIterator<Item = PhysicalUnit>,
    {
        PhysicalBasetype {
            range: range,
            units: units.into_iter().collect(),
            primary: primary,
        }
    }
}

impl Type for PhysicalBasetype {
    common_type_impl!();

    fn into_owned<'a>(self) -> OwnedType<'a>
    where
        Self: 'a,
    {
        OwnedType::PhysicalBasetype(self)
    }

    fn to_owned<'a>(&self) -> OwnedType<'a>
    where
        Self: 'a,
    {
        OwnedType::PhysicalBasetype(self.clone())
    }
}

impl PhysicalType for PhysicalBasetype {
    fn as_type(&self) -> &Type {
        self
    }

    fn range(&self) -> &Range<BigInt> {
        &self.range
    }

    fn units(&self) -> &[PhysicalUnit] {
        &self.units
    }

    fn primary_index(&self) -> usize {
        self.primary
    }

    fn base_type(&self) -> &Type {
        self
    }

    fn as_basetype(&self) -> Option<&PhysicalBasetype> {
        Some(self)
    }

    fn is_equal(&self, other: &PhysicalType) -> bool {
        other.as_basetype().map(|t| self == t).unwrap_or(false)
    }
}

impl Display for PhysicalBasetype {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{} units (", self.range)?;
        for (sep, unit) in once("").chain(repeat(", ")).zip(self.units.iter()) {
            write!(f, "{}{}", sep, unit.name)?;
        }
        write!(f, ")")?;
        Ok(())
    }
}

impl Deref for PhysicalBasetype {
    type Target = Range<BigInt>;
    fn deref(&self) -> &Range<BigInt> {
        &self.range
    }
}

/// A subtype of an integer type.
pub type PhysicalSubtype<'t> = ScalarSubtype<'t, PhysicalType, BigInt>;

impl<'t> PhysicalSubtype<'t> {
    /// Create a new integer subtype.
    ///
    /// Returns `Some(...)` if `range` is a subrange of the integer, or `None`
    /// otherwise.
    ///
    /// # Example
    ///
    /// ```
    /// use moore_vhdl::ty2::{Type, TypeMark, PhysicalUnit, PhysicalBasetype, PhysicalSubtype, Range};
    /// use moore_vhdl::common::name::get_name_table;
    ///
    /// let ty = PhysicalBasetype::new(Range::ascending(-1000isize, 1000isize), vec![
    ///     PhysicalUnit::primary(get_name_table().intern("fs", false), 1),
    ///     PhysicalUnit::secondary(get_name_table().intern("ps", false), 1000, 1000, 0),
    /// ], 0);
    /// let tm = TypeMark::new(
    ///     get_name_table().intern("TIME", false),
    ///     &ty,
    /// );
    /// let a = PhysicalSubtype::new(&tm, Range::ascending(0isize, 100isize)).unwrap();
    /// let b = PhysicalSubtype::new(&tm, Range::descending(100isize, 0isize)).unwrap();
    ///
    /// assert_eq!(format!("{}", a), "TIME range 0 to 100");
    /// assert_eq!(format!("{}", b), "TIME range 100 downto 0");
    /// ```
    pub fn new(mark: &'t TypeMark<'t>, range: Range<BigInt>) -> Option<PhysicalSubtype<'t>> {
        let base = mark.as_any().as_physical()?;
        let base_range = base.range();
        if base_range.has_subrange(&range) {
            Some(PhysicalSubtype {
                resfn: base.resolution_func(),
                mark: mark,
                base: base,
                con: range,
            })
        } else {
            None
        }
    }
}

impl<'t> Type for PhysicalSubtype<'t> {
    common_type_impl!();

    fn into_owned<'a>(self) -> OwnedType<'a>
    where
        Self: 'a,
    {
        OwnedType::PhysicalSubtype(self)
    }

    fn to_owned<'a>(&self) -> OwnedType<'a>
    where
        Self: 'a,
    {
        OwnedType::PhysicalSubtype(self.clone())
    }
}

impl<'t> PhysicalType for PhysicalSubtype<'t> {
    fn as_type(&self) -> &Type {
        self
    }

    fn range(&self) -> &Range<BigInt> {
        &self.con
    }

    fn units(&self) -> &[PhysicalUnit] {
        self.base.units()
    }

    fn primary_index(&self) -> usize {
        self.base.primary_index()
    }

    fn base_type(&self) -> &Type {
        self.base.as_type()
    }

    fn as_subtype(&self) -> Option<&PhysicalSubtype> {
        Some(self)
    }

    fn is_equal(&self, other: &PhysicalType) -> bool {
        other.as_subtype().map(|t| self == t).unwrap_or(false)
    }
}

impl<'t> Display for PhysicalSubtype<'t> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{} range {}", self.mark, self.con)
    }
}

/// A unit of a physical type.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PhysicalUnit {
    /// The name of the unit.
    pub name: Name,
    /// The scale of the unit with respect to the physical type's primary unit.
    pub abs: BigInt,
    /// The scale of the unit with respect to another unit.
    pub rel: Option<(BigInt, usize)>,
}

impl PhysicalUnit {
    /// Create a new unit.
    ///
    /// # Example
    ///
    /// ```
    /// use moore_vhdl::ty2::{PhysicalUnit, BigInt};
    /// use moore_vhdl::common::name::get_name_table;
    ///
    /// let name = get_name_table().intern("fs", false);
    /// let unit = PhysicalUnit::new(name, 1, Some((1000, 0)));
    ///
    /// assert_eq!(unit.name, name);
    /// assert_eq!(unit.abs, BigInt::from(1));
    /// assert_eq!(unit.rel, Some((BigInt::from(1000), 0)));
    /// ```
    pub fn new<A, R>(name: Name, abs: A, rel: Option<(R, usize)>) -> PhysicalUnit
    where
        BigInt: From<A> + From<R>,
    {
        PhysicalUnit {
            name: name,
            abs: abs.into(),
            rel: rel.map(|(rel, rel_to)| (rel.into(), rel_to)),
        }
    }

    /// Create a new primary unit.
    ///
    /// # Example
    ///
    /// ```
    /// use moore_vhdl::ty2::{PhysicalUnit, BigInt};
    /// use moore_vhdl::common::name::get_name_table;
    ///
    /// let name = get_name_table().intern("fs", false);
    /// let unit = PhysicalUnit::primary(name, 1);
    ///
    /// assert_eq!(unit.name, name);
    /// assert_eq!(unit.abs, BigInt::from(1));
    /// assert_eq!(unit.rel, None);
    /// ```
    pub fn primary<A>(name: Name, abs: A) -> PhysicalUnit
    where
        BigInt: From<A>,
    {
        PhysicalUnit {
            name: name,
            abs: abs.into(),
            rel: None,
        }
    }

    /// Create a new secondary unit.
    ///
    /// # Example
    ///
    /// ```
    /// use moore_vhdl::ty2::{PhysicalUnit, BigInt};
    /// use moore_vhdl::common::name::get_name_table;
    ///
    /// let name = get_name_table().intern("fs", false);
    /// let unit = PhysicalUnit::secondary(name, 1, 1000, 0);
    ///
    /// assert_eq!(unit.name, name);
    /// assert_eq!(unit.abs, BigInt::from(1));
    /// assert_eq!(unit.rel, Some((BigInt::from(1000), 0)));
    /// ```
    pub fn secondary<A, R>(name: Name, abs: A, rel: R, rel_to: usize) -> PhysicalUnit
    where
        BigInt: From<A> + From<R>,
    {
        PhysicalUnit {
            name: name,
            abs: abs.into(),
            rel: Some((rel.into(), rel_to)),
        }
    }
}
<|endoftext|>