<commit_before>use anyhow::Context;
use anyhow::Result;
use pueue_lib::state::State;
use rstest::rstest;

use crate::fixtures::*;
use crate::helper::*;

/// Test that the normal status command works as expected.
/// Calling `pueue` without any subcommand is equivalent of using `status`.
#[rstest]
#[case(false)]
#[case(true)]
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn default_status(#[case] use_subcommand: bool) -> Result<()> {
    let daemon = daemon().await?;
    let shared = &daemon.settings.shared;

    // Add a task and wait until it finishes.
    assert_success(add_task(shared, "ls", false).await?);
    wait_for_task_condition(shared, 0, |task| task.is_done()).await?;

    let subcommand = if use_subcommand {
        vec!["status"]
    } else {
        Vec::new()
    };

    let output = run_client_command(shared, &subcommand).await?;

    let context = get_task_context(&daemon.settings).await?;
    assert_stdout_matches("status__default_status", output.stdout, context)?;

    Ok(())
}

/// Test the status output with all columns enabled.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn full_status() -> Result<()> {
    let daemon = daemon().await?;
    let shared = &daemon.settings.shared;

    // Add a paused task so we can use it as a dependency.
    run_client_command(
        shared,
        &["add", "--label", "test", "--delay", "2 hours", "ls"],
    )
    .await?;

    // Add a second command that depends on the first one.
    run_client_command(shared, &["add", "--after=0", "ls"]).await?;

    let output = run_client_command(shared, &["status"]).await?;

    let context = get_task_context(&daemon.settings).await?;
    assert_stdout_matches("status__full_status", output.stdout, context)?;

    Ok(())
}

/// Calling `status` with the `--color=always` flag, colors the output as expected.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn colored_status() -> Result<()> {
    let daemon = daemon().await?;
    let shared = &daemon.settings.shared;

    // Add a task and wait until it finishes.
    assert_success(add_task(shared, "ls", false).await?);
    wait_for_task_condition(shared, 0, |task| task.is_done()).await?;

    let output = run_client_command(shared, &["--color", "always", "status"]).await?;

    let context = get_task_context(&daemon.settings).await?;
    assert_stdout_matches("status__status_with_color", output.stdout, context)?;

    Ok(())
}

/// Calling `pueue status --json` will result in the current state being printed to the cli.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn status_json() -> Result<()> {
    let daemon = daemon().await?;
    let shared = &daemon.settings.shared;

    // Add a task and wait until it finishes.
    assert_success(add_task(shared, "ls", false).await?);
    wait_for_task_condition(shared, 0, |task| task.is_done()).await?;

    let output = run_client_command(shared, &["status", "--json"]).await?;

    let json = String::from_utf8_lossy(&output.stdout);
    let deserialized_state: State =
        serde_json::from_str(&*json).context("Failed to deserialize json state")?;

    let state = get_state(shared).await?;
    assert_eq!(
        deserialized_state, *state,
        "Json state differs from actual daemon state."
    );

    Ok(())
}
<commit_msg>fix: Reduce chance of hitting a test failure to midnight<commit_after>use anyhow::Context;
use anyhow::Result;
use pueue_lib::state::State;
use rstest::rstest;

use crate::fixtures::*;
use crate::helper::*;

/// Test that the normal status command works as expected.
/// Calling `pueue` without any subcommand is equivalent of using `status`.
#[rstest]
#[case(false)]
#[case(true)]
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn default_status(#[case] use_subcommand: bool) -> Result<()> {
    let daemon = daemon().await?;
    let shared = &daemon.settings.shared;

    // Add a task and wait until it finishes.
    assert_success(add_task(shared, "ls", false).await?);
    wait_for_task_condition(shared, 0, |task| task.is_done()).await?;

    let subcommand = if use_subcommand {
        vec!["status"]
    } else {
        Vec::new()
    };

    let output = run_client_command(shared, &subcommand).await?;

    let context = get_task_context(&daemon.settings).await?;
    assert_stdout_matches("status__default_status", output.stdout, context)?;

    Ok(())
}

/// Test the status output with all columns enabled.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn full_status() -> Result<()> {
    let daemon = daemon().await?;
    let shared = &daemon.settings.shared;

    // Add a paused task so we can use it as a dependency.
    run_client_command(
        shared,
        &["add", "--label", "test", "--delay", "1 minute", "ls"],
    )
    .await?;

    // Add a second command that depends on the first one.
    run_client_command(shared, &["add", "--after=0", "ls"]).await?;

    let output = run_client_command(shared, &["status"]).await?;

    let context = get_task_context(&daemon.settings).await?;
    assert_stdout_matches("status__full_status", output.stdout, context)?;

    Ok(())
}

/// Calling `status` with the `--color=always` flag, colors the output as expected.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn colored_status() -> Result<()> {
    let daemon = daemon().await?;
    let shared = &daemon.settings.shared;

    // Add a task and wait until it finishes.
    assert_success(add_task(shared, "ls", false).await?);
    wait_for_task_condition(shared, 0, |task| task.is_done()).await?;

    let output = run_client_command(shared, &["--color", "always", "status"]).await?;

    let context = get_task_context(&daemon.settings).await?;
    assert_stdout_matches("status__status_with_color", output.stdout, context)?;

    Ok(())
}

/// Calling `pueue status --json` will result in the current state being printed to the cli.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn status_json() -> Result<()> {
    let daemon = daemon().await?;
    let shared = &daemon.settings.shared;

    // Add a task and wait until it finishes.
    assert_success(add_task(shared, "ls", false).await?);
    wait_for_task_condition(shared, 0, |task| task.is_done()).await?;

    let output = run_client_command(shared, &["status", "--json"]).await?;

    let json = String::from_utf8_lossy(&output.stdout);
    let deserialized_state: State =
        serde_json::from_str(&*json).context("Failed to deserialize json state")?;

    let state = get_state(shared).await?;
    assert_eq!(
        deserialized_state, *state,
        "Json state differs from actual daemon state."
    );

    Ok(())
}
<|endoftext|>


<commit_before>
// Copyright 2017 The gltf Library Developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::fmt;

/// An immutable JSON source path.
#[derive(Clone, Debug, PartialEq)]
pub struct Path(pub String);

impl Path {
    /// Creates an empty JSON source path.
    ///
    /// # Examples
    ///
    /// Basic usage:
    ///
    /// ```rust,ignore
    /// # use gltf::json::Path;
    /// let path = Path::new();
    /// assert_eq!("", path.as_str());
    /// ```
    pub fn new() -> Self {
        Path(String::new())
    }

    /// Returns a new path ending with the given field.
    ///
    /// # Examples
    ///
    /// Basic usage:
    ///
    /// ```rust,ignore
    /// # use gltf::json::Path;
    /// let path = Path::new().field("foo");
    /// assert_eq!("foo", path.as_str());
    /// assert_eq!("foo.bar", path.field("bar").as_str());
    /// ```
    pub fn field(&self, name: &str) -> Self {
        if self.0.is_empty() {
            Path(name.to_string())
        } else {
            Path(format!("{}.{}", self.0, name))
        }
    }

    /// Returns a new path ending with the given array index.
    ///
    /// # Examples
    ///
    /// Basic usage:
    ///
    /// ```rust,ignore
    /// # use gltf::json::Path;
    /// let path = Path::new().field("foo");
    /// assert_eq!("foo[123]", path.index(123).as_str());
    /// ```
    pub fn index(&self, index: usize) -> Self {
        Path(format!("{}[{}]", self.0, index))
    }

    /// Returns a new path ending with the given object key.
    ///
    /// # Examples
    ///
    /// Basic usage:
    ///
    /// ```rust,ignore
    /// # use gltf::json::Path;
    /// let path = Path::new().field("foo");
    /// assert_eq!("foo[\"bar\"]", path.key("bar").as_str());
    /// ```
    pub fn key(&self, key: &str) -> Self {
        Path(format!("{}[\"{}\"]", self.0, key))
    }

    /// Returns a view into the internal representation.
    pub fn as_str(&self) -> &str {
        &self.0
    }
}

impl fmt::Display for Path {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}
<commit_msg>Fix gltf_json::Path tests<commit_after>
// Copyright 2017 The gltf Library Developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::fmt;

/// An immutable JSON source path.
#[derive(Clone, Debug, PartialEq)]
pub struct Path(pub String);

impl Path {
    /// Creates an empty JSON source path.
    ///
    /// # Examples
    ///
    /// Basic usage
    ///
    /// ```rust
    /// # use gltf_json::Path;
    /// let path = Path::new();
    /// assert_eq!("", path.as_str());
    /// ```
    pub fn new() -> Self {
        Path(String::new())
    }

    /// Returns a new path ending with the given field.
    ///
    /// # Examples
    ///
    /// Basic usage
    ///
    /// ```rust
    /// # use gltf_json::Path;
    /// let path = Path::new().field("foo");
    /// assert_eq!("foo", path.as_str());
    /// assert_eq!("foo.bar", path.field("bar").as_str());
    /// ```
    pub fn field(&self, name: &str) -> Self {
        if self.0.is_empty() {
            Path(name.to_string())
        } else {
            Path(format!("{}.{}", self.0, name))
        }
    }

    /// Returns a new path ending with the given array index.
    ///
    /// # Examples
    ///
    /// Basic usage
    ///
    /// ```rust
    /// # use gltf_json::Path;
    /// let path = Path::new().field("foo");
    /// assert_eq!("foo[123]", path.index(123).as_str());
    /// ```
    pub fn index(&self, index: usize) -> Self {
        Path(format!("{}[{}]", self.0, index))
    }

    /// Returns a new path ending with the given object key.
    ///
    /// # Examples
    ///
    /// Basic usage
    ///
    /// ```rust
    /// # use gltf_json::Path;
    /// let path = Path::new().field("foo");
    /// assert_eq!("foo[\"bar\"]", path.key("bar").as_str());
    /// ```
    pub fn key(&self, key: &str) -> Self {
        Path(format!("{}[\"{}\"]", self.0, key))
    }

    /// Returns a view into the internal representation.
    pub fn as_str(&self) -> &str {
        &self.0
    }
}

impl fmt::Display for Path {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}
<|endoftext|>


<commit_before>//! Reader and writer for the refFlat format.
//!
//! The refFlat format is a transcript-oriented format in which each
//! transcript is denoted in a single line. It is used most prominently
//! by the [picard suite tools](https://broadinstitute.github.io/picard/)
//!
//! A minimum specification of the columns can be found here:
//! https://genome.ucsc.edu/goldenPath/gbdDescriptionsOld.html#RefFlat

use std::io;
use std::fs;
use std::convert::AsRef;
use std::path::Path;
use std::str::FromStr;

use csv;

use error::FeatureError;
use {Strand, Transcript, TBuilder};
use super::error::ParseError;

pub type RefFlatRow = (String, String, String, char, u64, u64, u64, u64, usize, String, String);

pub struct Reader<R: io::Read> {
    inner: csv::Reader<R>,
}

impl<R: io::Read> Reader<R> {

    pub fn from_reader(in_reader: R) -> Reader<R> {
        Reader {
            inner: csv::Reader::from_reader(in_reader)
                .delimiter(b'\t')
                .has_headers(false)
        }
    }

    pub fn transcripts<'a>(&'a mut self) -> RefFlatTranscripts<'a, R> {
        RefFlatTranscripts { inner: self.inner.decode() }
    }
}

impl Reader<fs::File> {
    pub fn from_file<P: AsRef<Path>>(path: P) -> io::Result<Self> {
        fs::File::open(path).map(Reader::from_reader)
    }
}

pub struct RefFlatTranscripts<'a, R: 'a> where R: io::Read {
    inner: csv::DecodedRecords<'a, R, RefFlatRow>,
}

impl<'a, R> Iterator for RefFlatTranscripts<'a, R> where R: io::Read {

    type Item = Result<Transcript, ParseError>;

    fn next(&mut self) -> Option<Result<Transcript, ParseError>> {
        self.inner.next().map(|row_res| {
            let row = row_res.map_err(ParseError::from)?;
            let (gene_id,
                 transcript_name,
                 seq_name,
                 strand_char,
                 trx_start,
                 trx_end,
                 coding_start,
                 coding_end,
                 num_exons,
                 raw_exon_starts,
                 raw_exon_ends): RefFlatRow = row;

            let exon_coords = zip_raw_exon_coords(raw_exon_starts, raw_exon_ends);
            if exon_coords.len() != num_exons {
                return Err(ParseError::FormatSpecific(
                    "number of exon and exon coordinates mismatch"));
            }

            let strand = Strand::from_char(&strand_char)
                .map_err(FeatureError::from)
                .map_err(ParseError::from)?;
            let mut tb = TBuilder::new(seq_name, trx_start, trx_end)
                .id(transcript_name)
                .strand(strand)
                .exon_coords(exon_coords)
                .coding_incl_stop(true)
                .attribute("gene_id", gene_id);

            if coding_start != coding_end {
                tb = tb.coding_coord(coding_start, coding_end)
            }

            tb.build().map_err(ParseError::from)
        })
    }
}

#[inline]
fn zip_raw_exon_coords(starts: String, ends: String) -> Vec<(u64, u64)> {
    let exon_starts = starts
        .split(',').filter_map(|item| u64::from_str(item).ok());
    let exon_ends = ends
        .split(',').filter_map(|item| u64::from_str(item).ok());
    exon_starts.zip(exon_ends).collect()
}
<commit_msg>Add RefFlatRecord struct<commit_after>//! Reader and writer for the refFlat format.
//!
//! The refFlat format is a transcript-oriented format in which each
//! transcript is denoted in a single line. It is used most prominently
//! by the [picard suite tools](https://broadinstitute.github.io/picard/)
//!
//! A minimum specification of the columns can be found here:
//! https://genome.ucsc.edu/goldenPath/gbdDescriptionsOld.html#RefFlat

use std::io;
use std::fs;
use std::convert::AsRef;
use std::path::Path;
use std::str::FromStr;

use csv;

use error::FeatureError;
use {Strand, Transcript, TBuilder};
use super::error::ParseError;

pub type RefFlatRow = (String, String, String, char, u64, u64, u64, u64, usize, String, String);

pub struct RefFlatRecord {
    pub gene_id: String,
    pub transcript_name: String,
    pub seq_name: String,
    pub strand_char: char,
    pub trx_start: u64,
    pub trx_end: u64,
    pub coding_start: u64,
    pub coding_end: u64,
    pub num_exons: usize,
    pub exon_starts: String,
    pub exon_ends: String,
}

impl RefFlatRecord {
    pub fn new(row: RefFlatRow) -> RefFlatRecord {
        RefFlatRecord {
            gene_id: row.0,
            transcript_name: row.1,
            seq_name: row.2,
            strand_char: row.3,
            trx_start: row.4,
            trx_end: row.5,
            coding_start: row.6,
            coding_end: row.7,
            num_exons: row.8,
            exon_starts: row.9,
            exon_ends: row.10,
        }
    }

    pub fn to_transcript(self) -> Result<Transcript, ParseError> {

        let exon_coords = self.zip_raw_exon_coords();
        if exon_coords.len() != self.num_exons {
            return Err(ParseError::FormatSpecific(
                "number of exon and exon coordinates mismatch"));
        }

        let strand = Strand::from_char(&self.strand_char)
            .map_err(FeatureError::from)
            .map_err(ParseError::from)?;
        let mut tb = TBuilder::new(self.seq_name, self.trx_start, self.trx_end)
            .id(self.transcript_name)
            .strand(strand)
            .exon_coords(exon_coords)
            .coding_incl_stop(true)
            .attribute("gene_id", self.gene_id);

        if self.coding_start != self.coding_end {
            tb = tb.coding_coord(self.coding_start, self.coding_end)
        }

        tb.build().map_err(ParseError::from)
    }

    #[inline]
    fn zip_raw_exon_coords(&self) -> Vec<(u64, u64)> {
        let exon_starts = self.exon_starts
            .split(',').filter_map(|item| u64::from_str(item).ok());
        let exon_ends = self.exon_ends
            .split(',').filter_map(|item| u64::from_str(item).ok());
        exon_starts.zip(exon_ends).collect()
    }
}

pub struct Reader<R: io::Read> {
    inner: csv::Reader<R>,
}

impl<R: io::Read> Reader<R> {

    pub fn from_reader(in_reader: R) -> Reader<R> {
        Reader {
            inner: csv::Reader::from_reader(in_reader)
                .delimiter(b'\t')
                .has_headers(false)
        }
    }

    pub fn transcripts<'a>(&'a mut self) -> RefFlatTranscripts<'a, R> {
        RefFlatTranscripts { inner: self.inner.decode() }
    }
}

impl Reader<fs::File> {
    pub fn from_file<P: AsRef<Path>>(path: P) -> io::Result<Self> {
        fs::File::open(path).map(Reader::from_reader)
    }
}

pub struct RefFlatTranscripts<'a, R: 'a> where R: io::Read {
    inner: csv::DecodedRecords<'a, R, RefFlatRow>,
}

impl<'a, R> Iterator for RefFlatTranscripts<'a, R> where R: io::Read {

    type Item = Result<Transcript, ParseError>;

    fn next(&mut self) -> Option<Result<Transcript, ParseError>> {
        self.inner.next().map(|row| {
            row.or_else(|err| Err(ParseError::from(err)))
                .map(RefFlatRecord::new)
                .and_then(|record| record.to_transcript())
        })
    }
}
<|endoftext|>


<commit_before>// this module adds some functionality based on the required implementations
// here like: `LinkedList::pop_back` or `Clone for LinkedList<T>`
// You are free to use anything in it, but it's mainly for the test framework.
mod pre_implemented;

pub struct LinkedList<T>(std::marker::PhantomData<T>);

pub struct Cursor<'a, T>(std::marker::PhantomData<&'a mut T>);

pub struct Iter<'a, T>(std::marker::PhantomData<&'a T>);

impl<T> LinkedList<T> {
    pub fn new() -> Self {
        unimplemented!()
    }

    pub fn len(&self) -> usize {
        unimplemented!()
    }

    /// Return a cursor positioned on the front element
    pub fn cursor_front(&mut self) -> Cursor<'_, T> {
        unimplemented!()
    }

    /// Return a cursor positioned on the back element
    pub fn cursor_back(&mut self) -> Cursor<'_, T> {
        unimplemented!()
    }

    /// Return an iterator that moves from front to back
    pub fn iter(&self) -> Iter<'_, T> {
        unimplemented!()
    }
}

impl<T> Drop for LinkedList<T> {
    fn drop(&mut self) {
        unimplemented!()
    }
}

// the cursor is expected to act as if it is at the position of an element
// and it also has to work with and be able to insert into an empty list.
impl<T> Cursor<'_, T> {
    /// Take a mutable reference to the current element
    pub fn peek_mut(&mut self) -> Option<&mut T> {
        unimplemented!()
    }

    /// Move one position forward (towards the back) and
    /// return a reference to the new position
    pub fn next(&mut self) -> Option<&mut T> {
        unimplemented!()
    }

    /// Move one position backward (towards the front) and
    /// return a reference to the new position
    pub fn prev(&mut self) -> Option<&mut T> {
        unimplemented!()
    }

    /// Remove and return the element at the current position and move the cursor
    /// to the neighboring element that's closest to the back. This can be
    /// either the next or previous position.
    pub fn take(&mut self) -> Option<T> {
        unimplemented!()
    }

    pub fn insert_after(&mut self, _element: T) {
        unimplemented!()
    }

    pub fn insert_before(&mut self, _element: T) {
        unimplemented!()
    }
}

impl<'a, T> Iterator for Iter<'a, T> {
    type Item = &'a T;

    fn next(&mut self) -> Option<&'a T> {
        unimplemented!()
    }
}
<commit_msg>remove Drop impl<commit_after>// this module adds some functionality based on the required implementations
// here like: `LinkedList::pop_back` or `Clone for LinkedList<T>`
// You are free to use anything in it, but it's mainly for the test framework.
mod pre_implemented;

pub struct LinkedList<T>(std::marker::PhantomData<T>);

pub struct Cursor<'a, T>(std::marker::PhantomData<&'a mut T>);

pub struct Iter<'a, T>(std::marker::PhantomData<&'a T>);

impl<T> LinkedList<T> {
    pub fn new() -> Self {
        unimplemented!()
    }

    pub fn len(&self) -> usize {
        unimplemented!()
    }

    /// Return a cursor positioned on the front element
    pub fn cursor_front(&mut self) -> Cursor<'_, T> {
        unimplemented!()
    }

    /// Return a cursor positioned on the back element
    pub fn cursor_back(&mut self) -> Cursor<'_, T> {
        unimplemented!()
    }

    /// Return an iterator that moves from front to back
    pub fn iter(&self) -> Iter<'_, T> {
        unimplemented!()
    }
}

// the cursor is expected to act as if it is at the position of an element
// and it also has to work with and be able to insert into an empty list.
impl<T> Cursor<'_, T> {
    /// Take a mutable reference to the current element
    pub fn peek_mut(&mut self) -> Option<&mut T> {
        unimplemented!()
    }

    /// Move one position forward (towards the back) and
    /// return a reference to the new position
    pub fn next(&mut self) -> Option<&mut T> {
        unimplemented!()
    }

    /// Move one position backward (towards the front) and
    /// return a reference to the new position
    pub fn prev(&mut self) -> Option<&mut T> {
        unimplemented!()
    }

    /// Remove and return the element at the current position and move the cursor
    /// to the neighboring element that's closest to the back. This can be
    /// either the next or previous position.
    pub fn take(&mut self) -> Option<T> {
        unimplemented!()
    }

    pub fn insert_after(&mut self, _element: T) {
        unimplemented!()
    }

    pub fn insert_before(&mut self, _element: T) {
        unimplemented!()
    }
}

impl<'a, T> Iterator for Iter<'a, T> {
    type Item = &'a T;

    fn next(&mut self) -> Option<&'a T> {
        unimplemented!()
    }
}
<|endoftext|>


<commit_before>
use piston_window::*;
use piston_window::character::CharacterCache;

use game::block::Block;
use game::config::Config;
use game::render_options::RenderOptions;
use game::tetromino::Tetromino;

pub struct Grid {
    pub boxes: [[Option<Block>; 10]; 22],
    overlay: [[Option<Block>; 10]; 22],
    border: rectangle::Rectangle,
}

impl Grid {
    pub fn new() -> Grid {
        Grid {
            boxes: [[None; 10]; 22],
            overlay: [[None; 10]; 22],
            border: rectangle::Rectangle::new([1.0; 4]),
        }
    }

    pub fn apply_tetromino(&mut self, tetromino: &Tetromino, config: &Config) {
        // Clear the overlay
        self.overlay = [[None; 10]; 22];

        for y in 0..4 {
            for x in 0..4 {
                if let Some(ref block) = tetromino.blocks[y][x] {
                    let x = (tetromino.x + x as i32) as usize;
                    let y = (tetromino.y + y as i32) as usize;

                    let (shadow_x, shadow_y) = tetromino.find_landing_xy(&self.boxes);
                    let shadow_x = (shadow_x + x as i32) as usize;
                    let shadow_y = (shadow_y + y as i32) as usize;

                    if x >= 10 || y >= 22 {
                        continue;
                    }

                    self.overlay[y][x] = Some(*block);

                    // If the shadow is enabled, also store that in the overlay
                    if config.shadow_enabled {
                        if let Some(ref shadow) = tetromino.shadow[y][x] {
                            self.overlay[shadow_y][shadow_x] = Some(*shadow);
                        }
                    }
                }
            }
        }
    }

    pub fn store_tetromino(&mut self, tetromino: &Tetromino) {
        for y in 0..4 {
            for x in 0..4 {
                if let Some(ref block) = tetromino.blocks[y][x] {
                    let x = (tetromino.x + x as i32) as usize;
                    let y = (tetromino.y + y as i32) as usize;

                    if x >= 10 || y >= 22 {
                        continue;
                    }

                    self.boxes[y][x] = Some(*block);
                }
            }
        }
    }

    /// Removes complete lines from the grid
    pub fn remove_complete_lines(&mut self, config: &Config) -> u32 {
        // Pointer to where we're currently writing lines
        let mut y_mut = (config.grid_size.1 - 1) as usize;
        let mut cleared_lines = 0;

        for y in (0..config.grid_size.1).rev() {
            // If every column in this row has a block,
            // we consider it "complete"
            let complete = self.boxes[y as usize].iter().all(|block| block.is_some());

            // If this is not a complete line, copy it
            // into the currently pointed line
            if !complete {
                self.boxes[y_mut] = self.boxes[y as usize];
                y_mut -= 1;
                if y_mut == 0 {
                    break;
                }
            } else {
                cleared_lines += 1;
            }
        }

        cleared_lines
    }

    pub fn render<'a, G, C>(&self, options: &mut RenderOptions<'a, G, C>, e: &Event)
        where C: CharacterCache,
              G: Graphics<Texture = <C as CharacterCache>::Texture>
    {
        let grid_offset = options.config.grid_offset;
        let tile_size = options.config.tile_size;

        // Draw the "border" first
        self.border.draw([(grid_offset - 2.0) as f64,
                          (grid_offset - 2.0) as f64,
                          (tile_size * 10.0 + 4.0) as f64,
                          (tile_size * 20.0 + 4.0) as f64],
                         &Default::default(),
                         options.context.transform,
                         options.graphics);

        for y in 2..22 {
            for x in 0..10 {

                let adjusted_y = y - 2;
                if let Some(ref block) = self.overlay[y][x] {
                    block.render(x, y, options.config, options.context, options.graphics, e);
                    continue;
                }

                match self.boxes[y][x] {
                    Some(ref block) => {
                        block.render(x, y, options.config, options.context, options.graphics, e);
                    }
                    None => {
                        rectangle([0.0, 0.0, 0.0, 1.0],
                                  [x as f64 * tile_size + grid_offset,
                                   adjusted_y as f64 * tile_size + grid_offset,
                                   tile_size as f64,
                                   tile_size as f64],
                                  options.context.transform,
                                  options.graphics);
                    }
                };
            }
        }
    }
}
<commit_msg>Removed the Rectangle allocation when drawing the grid<commit_after>
use piston_window::*;
use piston_window::character::CharacterCache;

use game::block::Block;
use game::config::Config;
use game::render_options::RenderOptions;
use game::tetromino::Tetromino;

pub struct Grid {
    pub boxes: [[Option<Block>; 10]; 22],
    overlay: [[Option<Block>; 10]; 22],
    border: rectangle::Rectangle,
    block: rectangle::Rectangle,
}

impl Grid {
    pub fn new() -> Grid {
        Grid {
            boxes: [[None; 10]; 22],
            overlay: [[None; 10]; 22],
            border: rectangle::Rectangle::new([1.0; 4]),
            block: rectangle::Rectangle::new([0.0; 4]),
        }
    }

    pub fn apply_tetromino(&mut self, tetromino: &Tetromino, config: &Config) {
        // Clear the overlay
        self.overlay = [[None; 10]; 22];

        for y in 0..4 {
            for x in 0..4 {
                if let Some(ref block) = tetromino.blocks[y][x] {
                    let x = (tetromino.x + x as i32) as usize;
                    let y = (tetromino.y + y as i32) as usize;

                    let (shadow_x, shadow_y) = tetromino.find_landing_xy(&self.boxes);
                    let shadow_x = (shadow_x + x as i32) as usize;
                    let shadow_y = (shadow_y + y as i32) as usize;

                    if x >= 10 || y >= 22 {
                        continue;
                    }

                    self.overlay[y][x] = Some(*block);

                    // If the shadow is enabled, also store that in the overlay
                    if config.shadow_enabled {
                        if let Some(ref shadow) = tetromino.shadow[y][x] {
                            self.overlay[shadow_y][shadow_x] = Some(*shadow);
                        }
                    }
                }
            }
        }
    }

    pub fn store_tetromino(&mut self, tetromino: &Tetromino) {
        for y in 0..4 {
            for x in 0..4 {
                if let Some(ref block) = tetromino.blocks[y][x] {
                    let x = (tetromino.x + x as i32) as usize;
                    let y = (tetromino.y + y as i32) as usize;

                    if x >= 10 || y >= 22 {
                        continue;
                    }

                    self.boxes[y][x] = Some(*block);
                }
            }
        }
    }

    /// Removes complete lines from the grid
    pub fn remove_complete_lines(&mut self, config: &Config) -> u32 {
        // Pointer to where we're currently writing lines
        let mut y_mut = (config.grid_size.1 - 1) as usize;
        let mut cleared_lines = 0;

        for y in (0..config.grid_size.1).rev() {
            // If every column in this row has a block,
            // we consider it "complete"
            let complete = self.boxes[y as usize].iter().all(|block| block.is_some());

            // If this is not a complete line, copy it
            // into the currently pointed line
            if !complete {
                self.boxes[y_mut] = self.boxes[y as usize];
                y_mut -= 1;
                if y_mut == 0 {
                    break;
                }
            } else {
                cleared_lines += 1;
            }
        }

        cleared_lines
    }

    pub fn render<'a, G, C>(&self, options: &mut RenderOptions<'a, G, C>, e: &Event)
        where C: CharacterCache,
              G: Graphics<Texture = <C as CharacterCache>::Texture>
    {
        let grid_offset = options.config.grid_offset;
        let tile_size = options.config.tile_size;

        // Draw the "border" first
        self.border.draw([(grid_offset - 2.0) as f64,
                          (grid_offset - 2.0) as f64,
                          (tile_size * 10.0 + 4.0) as f64,
                          (tile_size * 20.0 + 4.0) as f64],
                         &Default::default(),
                         options.context.transform,
                         options.graphics);

        for y in 2..22 {
            for x in 0..10 {

                let adjusted_y = y - 2;
                if let Some(ref block) = self.overlay[y][x] {
                    block.render(x, y, options.config, options.context, options.graphics, e);
                    continue;
                }

                match self.boxes[y][x] {
                    Some(ref block) => {
                        block.render(x, y, options.config, options.context, options.graphics, e);
                    }
                    None => {
                        self.block.draw([x as f64 * tile_size + grid_offset,
                                         adjusted_y as f64 * tile_size + grid_offset,
                                         tile_size as f64,
                                         tile_size as f64],
                                        &Default::default(),
                                        options.context.transform,
                                        options.graphics);
                    }
                };
            }
        }
    }
}
<|endoftext|>


<commit_before>use tikv::raftserver::store::*;

use super::util::*;
use super::store::new_store_cluster;

#[test]
fn test_multi_store() {
    // init_env_log();

    // test a cluster with five nodes [1, 5], only one region (region 1).
    // every node has a store and a peer with same id as node's.
    let count = 5;
    let mut cluster = new_store_cluster(0, count);
    cluster.bootstrap_single_region().expect("");
    cluster.run_all_nodes();

    let (key, value) = (b"a1", b"v1");

    cluster.put(key, value);
    assert_eq!(cluster.get(key), Some(value.to_vec()));

    let check_res = cluster.check_quorum(|engine| {
        match engine.get_value(&keys::data_key(key)).unwrap() {
            None => false,
            Some(v) => &*v == value,
        }
    });
    assert!(check_res);

    cluster.delete(key);
    assert_eq!(cluster.get(key), None);

    let check_res = cluster.check_quorum(|engine| {
        engine.get_value(&keys::data_key(key)).unwrap().is_none()
    });
    assert!(check_res);
}

#[test]
fn test_multi_store_leader_crash() {
    let count = 5;
    let mut cluster = new_store_cluster(0, count);
    cluster.bootstrap_single_region().expect("");
    cluster.run_all_nodes();

    let (key1, value1) = (b"a1", b"v1");

    cluster.put(key1, value1);

    let last_leader = cluster.leader_of_region(1).unwrap();
    cluster.stop_node(last_leader.get_node_id());

    sleep_ms(500);
    cluster.reset_leader_of_region(1);
    let new_leader = cluster.leader_of_region(1).expect("leader should be elected.");
    assert!(new_leader != last_leader);

    assert_eq!(cluster.get(key1), Some(value1.to_vec()));

    let (key2, value2) = (b"a2", b"v2");

    cluster.put(key2, value2);
    cluster.delete(key1);
    assert!(cluster.engines[&last_leader.get_node_id()]
                .get_value(&keys::data_key(key2))
                .unwrap()
                .is_none());
    assert!(cluster.engines[&last_leader.get_node_id()]
                .get_value(&keys::data_key(key1))
                .unwrap()
                .is_some());

    // week up
    cluster.run_node(last_leader.get_node_id());

    sleep_ms(400);
    let v = cluster.engines[&last_leader.get_node_id()]
                .get_value(&keys::data_key(key2))
                .unwrap()
                .unwrap();
    assert_eq!(&*v, value2);
    assert!(cluster.engines[&last_leader.get_node_id()]
                .get_value(&keys::data_key(key1))
                .unwrap()
                .is_none());
}

#[test]
fn test_multi_store_cluster_restart() {
    let count = 5;
    let mut cluster = new_store_cluster(0, count);
    cluster.bootstrap_single_region().expect("");
    cluster.run_all_nodes();

    let (key, value) = (b"a1", b"v1");

    assert!(cluster.leader_of_region(1).is_some());
    assert_eq!(cluster.get(key), None);
    cluster.put(key, value);

    assert_eq!(cluster.get(key), Some(value.to_vec()));

    cluster.shutdown();
    cluster.run_all_nodes();

    assert!(cluster.leader_of_region(1).is_some());
    assert_eq!(cluster.get(key), Some(value.to_vec()));
}

#[test]
fn test_multi_store_lost_majority() {
    let mut tests = vec![4, 5];
    for count in tests.drain(..) {
        let mut cluster = new_store_cluster(0, count);
        cluster.bootstrap_single_region().expect("");
        cluster.run_all_nodes();

        let half = (count as u64 + 1) / 2;
        for i in 1..half + 1 {
            cluster.stop_node(i);
        }
        if let Some(leader) = cluster.leader_of_region(1) {
            if leader.get_node_id() >= half + 1 {
                cluster.stop_node(leader.get_node_id());
            }
        }
        cluster.reset_leader_of_region(1);
        sleep_ms(600);

        assert!(cluster.leader_of_region(1).is_none());
    }
}
<commit_msg>tests/raftserver: Fix tests<commit_after>use tikv::raftserver::store::*;

use super::util::*;
use super::store::new_store_cluster;

#[test]
fn test_multi_store() {
    // init_env_log();

    // test a cluster with five nodes [1, 5], only one region (region 1).
    // every node has a store and a peer with same id as node's.
    let count = 5;
    let mut cluster = new_store_cluster(0, count);
    cluster.bootstrap_single_region().expect("");
    cluster.run_all_nodes();

    let (key, value) = (b"a1", b"v1");

    cluster.put(key, value);
    assert_eq!(cluster.get(key), Some(value.to_vec()));

    let check_res = cluster.check_quorum(|engine| {
        match engine.get_value(&keys::data_key(key)).unwrap() {
            None => false,
            Some(v) => &*v == value,
        }
    });
    assert!(check_res);

    cluster.delete(key);
    assert_eq!(cluster.get(key), None);

    let check_res = cluster.check_quorum(|engine| {
        engine.get_value(&keys::data_key(key)).unwrap().is_none()
    });
    assert!(check_res);
}

#[test]
fn test_multi_store_leader_crash() {
    let count = 5;
    let mut cluster = new_store_cluster(0, count);
    cluster.bootstrap_single_region().expect("");
    cluster.run_all_nodes();

    let (key1, value1) = (b"a1", b"v1");

    cluster.put(key1, value1);

    let last_leader = cluster.leader_of_region(1).unwrap();
    cluster.stop_node(last_leader.get_node_id());

    sleep_ms(800);
    cluster.reset_leader_of_region(1);
    let new_leader = cluster.leader_of_region(1).expect("leader should be elected.");
    assert!(new_leader != last_leader);

    assert_eq!(cluster.get(key1), Some(value1.to_vec()));

    let (key2, value2) = (b"a2", b"v2");

    cluster.put(key2, value2);
    cluster.delete(key1);
    assert!(cluster.engines[&last_leader.get_node_id()]
                .get_value(&keys::data_key(key2))
                .unwrap()
                .is_none());
    assert!(cluster.engines[&last_leader.get_node_id()]
                .get_value(&keys::data_key(key1))
                .unwrap()
                .is_some());

    // week up
    cluster.run_node(last_leader.get_node_id());

    sleep_ms(400);
    let v = cluster.engines[&last_leader.get_node_id()]
                .get_value(&keys::data_key(key2))
                .unwrap()
                .unwrap();
    assert_eq!(&*v, value2);
    assert!(cluster.engines[&last_leader.get_node_id()]
                .get_value(&keys::data_key(key1))
                .unwrap()
                .is_none());
}

#[test]
fn test_multi_store_cluster_restart() {
    let count = 5;
    let mut cluster = new_store_cluster(0, count);
    cluster.bootstrap_single_region().expect("");
    cluster.run_all_nodes();

    let (key, value) = (b"a1", b"v1");

    assert!(cluster.leader_of_region(1).is_some());
    assert_eq!(cluster.get(key), None);
    cluster.put(key, value);

    assert_eq!(cluster.get(key), Some(value.to_vec()));

    cluster.shutdown();
    cluster.run_all_nodes();

    assert!(cluster.leader_of_region(1).is_some());
    assert_eq!(cluster.get(key), Some(value.to_vec()));
}

#[test]
fn test_multi_store_lost_majority() {
    let mut tests = vec![4, 5];
    for count in tests.drain(..) {
        let mut cluster = new_store_cluster(0, count);
        cluster.bootstrap_single_region().expect("");
        cluster.run_all_nodes();

        let half = (count as u64 + 1) / 2;
        for i in 1..half + 1 {
            cluster.stop_node(i);
        }
        if let Some(leader) = cluster.leader_of_region(1) {
            if leader.get_node_id() >= half + 1 {
                cluster.stop_node(leader.get_node_id());
            }
        }
        cluster.reset_leader_of_region(1);
        sleep_ms(600);

        assert!(cluster.leader_of_region(1).is_none());
    }
}
<|endoftext|>


<commit_before>use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6, IpAddr};
use std::fmt;

pub enum ProxyProtocolHeader {
  V1(HeaderV1),
  V2(HeaderV2),
}

impl ProxyProtocolHeader {
  // Use this method to writte the header in the backend socket
  pub fn into_bytes(&self) -> Vec<u8> {
    match *self {
      ProxyProtocolHeader::V1(ref header) => header.into_bytes(),
      ProxyProtocolHeader::V2(ref header) => header.into_bytes(),
    }
  }
}

/// Indicate the proxied INET protocol and family
#[derive(Debug, PartialEq, Eq)]
pub enum ProtocolSupportedV1 {
  TCP4,    // TCP over IPv4
  TCP6,    // TCP over IPv6
  UNKNOWN, // unsupported,or unknown protocols
}

impl fmt::Display for ProtocolSupportedV1 {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    match *self {
      ProtocolSupportedV1::TCP4 => write!(f, "TCP4"),
      ProtocolSupportedV1::TCP6 => write!(f, "TCP6"),
      ProtocolSupportedV1::UNKNOWN => write!(f, "UNKNOWN"),
    }
  }
}

/// Proxy Protocol header for version 1 (text version)
/// Example:
/// - TCP/IPv4: `PROXY TCP4 255.255.255.255 255.255.255.255 65535 65535\r\n`
/// - TCP/IPv6: `PROXY TCP6 ffff:f...f:ffff ffff:f...f:ffff 65535 65535\r\n`
/// - Unknown: `PROXY UNKNOWN\r\n`
#[derive(Debug)]
pub struct HeaderV1 {
  protocol: ProtocolSupportedV1,
  addr_src: SocketAddr,
  addr_dst: SocketAddr,
}

const PROXY_PROTO_IDENTIFIER: &'static str = "PROXY";

impl HeaderV1 {

  pub fn new (addr_src: SocketAddr, addr_dst: SocketAddr) -> Self {
    let protocol = if addr_dst.is_ipv6() {
      ProtocolSupportedV1::TCP6
    } else if addr_dst.is_ipv4() {
      ProtocolSupportedV1::TCP4
    } else {
      ProtocolSupportedV1::UNKNOWN
    };

    HeaderV1 {
      protocol,
      addr_src,
      addr_dst,
    }
  }

  pub fn into_bytes(&self) -> Vec<u8> {
    if self.protocol.eq(&ProtocolSupportedV1::UNKNOWN) {
      format!("{} {}\r\n",
          PROXY_PROTO_IDENTIFIER,
          self.protocol,
      ).into_bytes()
    } else {
      format!("{} {} {} {} {} {}\r\n",
        PROXY_PROTO_IDENTIFIER,
        self.protocol,
        self.addr_src.ip(),
        self.addr_dst.ip(),
        self.addr_src.port(),
        self.addr_dst.port(),
      ).into_bytes()
    }
  }
}

#[cfg(test)]
mod test {

  use super::*;
  use std::net::{IpAddr, Ipv4Addr, SocketAddr, Ipv6Addr};

  #[test]
  fn it_should_return_a_correct_header_with_ipv4() {
    let header = HeaderV1::new(
      SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 80),
      SocketAddr::new(IpAddr::V4(Ipv4Addr::new(172, 17, 40, 59)), 80)
    );

    let header_to_cmp = "PROXY TCP4 127.0.0.1 172.17.40.59 80 80\r\n".as_bytes();

    assert_eq!(header_to_cmp, &header.into_bytes()[..]);
  }

  #[test]
  fn it_should_return_a_correct_header_with_ipv6() {
    let header = HeaderV1::new(
      SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0xffff)), 80),
      SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0x9c, 0x76)), 80)
    );

    let header_to_cmp = "PROXY TCP6 ::0.0.255.255 ::0.156.0.118 80 80\r\n".as_bytes();
    assert_eq!(header_to_cmp, &header.into_bytes()[..]);
  }

  // #[test]
  // fn it_should_return_the_an_unknown_header() {
  //   let header = HeaderV1::new(ProtocolSupportedV1::UNKNOWN, None, None);

  //   let header_to_cmp = "PROXY UNKNOWN\r\n".as_bytes();

  //   assert_eq!(header_to_cmp, &header.into_bytes()[..]);
  // }
}

pub struct HeaderV2 {
  signature: [u8; 12], // hex 0D 0A 0D 0A 00 0D 0A 51 55 49 54 0A
  ver_and_cmd: u8,     // protocol version and command
  family: u8,          // protocol family and address
  len: u16,            // number of following bytes part of the header
  addr: ProxyAddr,
}

impl HeaderV2 {
  pub fn new(addr_src: SocketAddr, addr_dst: SocketAddr) -> Self {
    let addr = ProxyAddr::from(addr_src, addr_dst);

    HeaderV2 {
      signature: [0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A],
      ver_and_cmd: 0x20,
      family: get_family(&addr),
      len: addr.len(),
      addr,
    }
  }

  pub fn into_bytes(&self) -> Vec<u8> {
    let mut header = Vec::new();
    header.extend_from_slice(&self.signature);
    header.push(self.ver_and_cmd);
    header.push(self.family);
    header.extend_from_slice(&u16_to_array_of_u8(self.len));
    header.extend_from_slice(&self.addr.into_bytes());
    header
  }
}

pub enum ProxyAddr {
  Ipv4Addr {
    src_addr: SocketAddrV4,
    dst_addr: SocketAddrV4,
  },
  Ipv6Addr {
    src_addr: SocketAddrV6,
    dst_addr: SocketAddrV6,
  },
  UnixAddr {
    src_addr: [u8; 108],
    dst_addr: [u8; 108],
  },
  AfUnspec,
}

impl ProxyAddr {

  pub fn from(addr_src: SocketAddr, addr_dst: SocketAddr) -> Self {
    match (addr_src, addr_dst) {
      (SocketAddr::V4(addr_ipv4_src), SocketAddr::V4(addr_ipv4_dst)) => {
        ProxyAddr::Ipv4Addr{
          src_addr: addr_ipv4_src,
          dst_addr: addr_ipv4_dst,
        }
      },
      (SocketAddr::V6(addr_ipv6_src), SocketAddr::V6(addr_ipv6_dst)) => {
        ProxyAddr::Ipv6Addr{
          src_addr: addr_ipv6_src,
          dst_addr: addr_ipv6_dst,
        }
      },
      _ => ProxyAddr::AfUnspec,
    }
  }

  fn len(&self) -> u16 {
    match *self {
      ProxyAddr::Ipv4Addr{ src_addr: _, dst_addr: _ } => 12,
      ProxyAddr::Ipv6Addr{ src_addr: _, dst_addr: _ } => 36,
      ProxyAddr::UnixAddr{ src_addr: _, dst_addr: _ } => 216,
      ProxyAddr::AfUnspec => unimplemented!(),
    }
  }

  fn into_bytes(&self) -> Vec<u8> {
    let mut res = Vec::new();

    match *self {
      ProxyAddr::Ipv4Addr{ src_addr, dst_addr } => {
        res.extend_from_slice(&src_addr.ip().octets());
        res.extend_from_slice(&dst_addr.ip().octets());
        res.extend_from_slice(&u16_to_array_of_u8(src_addr.port()));
        res.extend_from_slice(&u16_to_array_of_u8(dst_addr.port()));
      },
      ProxyAddr::Ipv6Addr{ src_addr, dst_addr } => {
        res.extend_from_slice(&src_addr.ip().octets());
        res.extend_from_slice(&dst_addr.ip().octets());
        res.extend_from_slice(&u16_to_array_of_u8(src_addr.port()));
        res.extend_from_slice(&u16_to_array_of_u8(dst_addr.port()));
      },
      ProxyAddr::UnixAddr{ src_addr, dst_addr } =>{
        res.extend_from_slice(&src_addr);
        res.extend_from_slice(&dst_addr);
      },
      ProxyAddr::AfUnspec => unimplemented!(),
    };

    res
  }
}

fn get_family(addr: &ProxyAddr) -> u8 {
  match addr {
    &ProxyAddr::Ipv4Addr{ src_addr: _, dst_addr: _ } => 0x10 | 0x01, // AF_INET  = 1 + STREAM = 1
    &ProxyAddr::Ipv6Addr{ src_addr: _, dst_addr: _ } => 0x20 | 0x01, // AF_INET6 = 2 + STREAM = 1
    &ProxyAddr::UnixAddr{ src_addr: _, dst_addr: _ } => 0x30 | 0x01, // AF_UNIX  = 3 + STREAM = 1
    &ProxyAddr::AfUnspec => unimplemented!(),
  }
}

fn u16_to_array_of_u8(x: u16) -> [u8; 2] {
    let b1 : u8 = ((x >> 8) & 0xff) as u8;
    let b2 : u8 = (x & 0xff) as u8;
    return [b1, b2]
}

#[cfg(test)]
mod testV2 {

  use super::*;
  use std::net::{Ipv4Addr, Ipv6Addr};

  #[test]
  fn test_u16_to_array_of_u8() {
    let val_u16: u16 = 65534;
    let expected = [0xff, 0xfe];
    assert_eq!(expected, u16_to_array_of_u8(val_u16));
  }

  #[test]
  fn test_deserialize_tcp_ipv4_proxy_protocol_header() {
    let src_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(125, 25, 10, 1)), 8080);
    let dst_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(10, 4, 5, 8)), 4200);

    let header = HeaderV2::new(src_addr, dst_addr);
    let expected = &[
      0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A, // MAGIC header
      0x20,                                                                   // Version 2 and command LOCAL
      0x11,                                                                   // family AF_UNIX with IPv4
      0x00, 0x0C,                                                             // address sizes = 12
      0x7D, 0x19, 0x0A, 0x01,                                                 // source address
      0x0A, 0x04, 0x05, 0x08,                                                 // destination address
      0x1F, 0x90,                                                             // source port
      0x10, 0x68,                                                             // destination port
    ];

    assert_eq!(expected, &header.into_bytes()[..]);
  }

  #[test]
  fn test_deserialize_tcp_ipv6_proxy_protocol_header() {
    let src_addr = SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), 8080);
    let dst_addr = SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), 4200);

    let header = HeaderV2::new(src_addr, dst_addr);
    let expected = vec![
      0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A,                         // MAGIC header
      0x20,                                                                                // Version 2 and command LOCAL
      0x21,                                                                                // family AF_UNIX with IPv6
      0x00, 0x24,                                                                                // address sizes = 36
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // source address
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // destination address
      0x1F, 0x90,                                                                                // source port
      0x10, 0x68,                                                                                // destination port
    ];

    assert_eq!(&expected[..], &header.into_bytes()[..]);
  }
}<commit_msg>Support the AF_UNSPEC case for the proxy protocol v2<commit_after>use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6, IpAddr};
use std::fmt;

pub enum ProxyProtocolHeader {
  V1(HeaderV1),
  V2(HeaderV2),
}

impl ProxyProtocolHeader {
  // Use this method to writte the header in the backend socket
  pub fn into_bytes(&self) -> Vec<u8> {
    match *self {
      ProxyProtocolHeader::V1(ref header) => header.into_bytes(),
      ProxyProtocolHeader::V2(ref header) => header.into_bytes(),
    }
  }
}

/// Indicate the proxied INET protocol and family
#[derive(Debug, PartialEq, Eq)]
pub enum ProtocolSupportedV1 {
  TCP4,    // TCP over IPv4
  TCP6,    // TCP over IPv6
  UNKNOWN, // unsupported,or unknown protocols
}

impl fmt::Display for ProtocolSupportedV1 {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    match *self {
      ProtocolSupportedV1::TCP4 => write!(f, "TCP4"),
      ProtocolSupportedV1::TCP6 => write!(f, "TCP6"),
      ProtocolSupportedV1::UNKNOWN => write!(f, "UNKNOWN"),
    }
  }
}

/// Proxy Protocol header for version 1 (text version)
/// Example:
/// - TCP/IPv4: `PROXY TCP4 255.255.255.255 255.255.255.255 65535 65535\r\n`
/// - TCP/IPv6: `PROXY TCP6 ffff:f...f:ffff ffff:f...f:ffff 65535 65535\r\n`
/// - Unknown: `PROXY UNKNOWN\r\n`
#[derive(Debug)]
pub struct HeaderV1 {
  protocol: ProtocolSupportedV1,
  addr_src: SocketAddr,
  addr_dst: SocketAddr,
}

const PROXY_PROTO_IDENTIFIER: &'static str = "PROXY";

impl HeaderV1 {

  pub fn new (addr_src: SocketAddr, addr_dst: SocketAddr) -> Self {
    let protocol = if addr_dst.is_ipv6() {
      ProtocolSupportedV1::TCP6
    } else if addr_dst.is_ipv4() {
      ProtocolSupportedV1::TCP4
    } else {
      ProtocolSupportedV1::UNKNOWN
    };

    HeaderV1 {
      protocol,
      addr_src,
      addr_dst,
    }
  }

  pub fn into_bytes(&self) -> Vec<u8> {
    if self.protocol.eq(&ProtocolSupportedV1::UNKNOWN) {
      format!("{} {}\r\n",
          PROXY_PROTO_IDENTIFIER,
          self.protocol,
      ).into_bytes()
    } else {
      format!("{} {} {} {} {} {}\r\n",
        PROXY_PROTO_IDENTIFIER,
        self.protocol,
        self.addr_src.ip(),
        self.addr_dst.ip(),
        self.addr_src.port(),
        self.addr_dst.port(),
      ).into_bytes()
    }
  }
}

#[cfg(test)]
mod test {

  use super::*;
  use std::net::{IpAddr, Ipv4Addr, SocketAddr, Ipv6Addr};

  #[test]
  fn it_should_return_a_correct_header_with_ipv4() {
    let header = HeaderV1::new(
      SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 80),
      SocketAddr::new(IpAddr::V4(Ipv4Addr::new(172, 17, 40, 59)), 80)
    );

    let header_to_cmp = "PROXY TCP4 127.0.0.1 172.17.40.59 80 80\r\n".as_bytes();

    assert_eq!(header_to_cmp, &header.into_bytes()[..]);
  }

  #[test]
  fn it_should_return_a_correct_header_with_ipv6() {
    let header = HeaderV1::new(
      SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0xffff)), 80),
      SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0x9c, 0x76)), 80)
    );

    let header_to_cmp = "PROXY TCP6 ::0.0.255.255 ::0.156.0.118 80 80\r\n".as_bytes();
    assert_eq!(header_to_cmp, &header.into_bytes()[..]);
  }

  // #[test]
  // fn it_should_return_the_an_unknown_header() {
  //   let header = HeaderV1::new(ProtocolSupportedV1::UNKNOWN, None, None);

  //   let header_to_cmp = "PROXY UNKNOWN\r\n".as_bytes();

  //   assert_eq!(header_to_cmp, &header.into_bytes()[..]);
  // }
}

pub struct HeaderV2 {
  signature: [u8; 12], // hex 0D 0A 0D 0A 00 0D 0A 51 55 49 54 0A
  ver_and_cmd: u8,     // protocol version and command
  family: u8,          // protocol family and address
  len: u16,            // number of following bytes part of the header
  addr: ProxyAddr,
}

impl HeaderV2 {
  pub fn new(addr_src: SocketAddr, addr_dst: SocketAddr) -> Self {
    let addr = ProxyAddr::from(addr_src, addr_dst);

    HeaderV2 {
      signature: [0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A],
      ver_and_cmd: 0x20,
      family: get_family(&addr),
      len: addr.len(),
      addr,
    }
  }

  pub fn into_bytes(&self) -> Vec<u8> {
    let mut header = Vec::new();
    header.extend_from_slice(&self.signature);
    header.push(self.ver_and_cmd);
    header.push(self.family);
    header.extend_from_slice(&u16_to_array_of_u8(self.len));
    header.extend_from_slice(&self.addr.into_bytes());
    header
  }
}

pub enum ProxyAddr {
  Ipv4Addr {
    src_addr: SocketAddrV4,
    dst_addr: SocketAddrV4,
  },
  Ipv6Addr {
    src_addr: SocketAddrV6,
    dst_addr: SocketAddrV6,
  },
  UnixAddr {
    src_addr: [u8; 108],
    dst_addr: [u8; 108],
  },
  AfUnspec,
}

impl ProxyAddr {

  pub fn from(addr_src: SocketAddr, addr_dst: SocketAddr) -> Self {
    match (addr_src, addr_dst) {
      (SocketAddr::V4(addr_ipv4_src), SocketAddr::V4(addr_ipv4_dst)) => {
        ProxyAddr::Ipv4Addr{
          src_addr: addr_ipv4_src,
          dst_addr: addr_ipv4_dst,
        }
      },
      (SocketAddr::V6(addr_ipv6_src), SocketAddr::V6(addr_ipv6_dst)) => {
        ProxyAddr::Ipv6Addr{
          src_addr: addr_ipv6_src,
          dst_addr: addr_ipv6_dst,
        }
      },
      _ => ProxyAddr::AfUnspec,
    }
  }

  fn len(&self) -> u16 {
    match *self {
      ProxyAddr::Ipv4Addr{ src_addr: _, dst_addr: _ } => 12,
      ProxyAddr::Ipv6Addr{ src_addr: _, dst_addr: _ } => 36,
      ProxyAddr::UnixAddr{ src_addr: _, dst_addr: _ } => 216,
      ProxyAddr::AfUnspec => 0,
    }
  }

  fn into_bytes(&self) -> Vec<u8> {
    let mut res = Vec::new();

    match *self {
      ProxyAddr::Ipv4Addr{ src_addr, dst_addr } => {
        res.extend_from_slice(&src_addr.ip().octets());
        res.extend_from_slice(&dst_addr.ip().octets());
        res.extend_from_slice(&u16_to_array_of_u8(src_addr.port()));
        res.extend_from_slice(&u16_to_array_of_u8(dst_addr.port()));
      },
      ProxyAddr::Ipv6Addr{ src_addr, dst_addr } => {
        res.extend_from_slice(&src_addr.ip().octets());
        res.extend_from_slice(&dst_addr.ip().octets());
        res.extend_from_slice(&u16_to_array_of_u8(src_addr.port()));
        res.extend_from_slice(&u16_to_array_of_u8(dst_addr.port()));
      },
      ProxyAddr::UnixAddr{ src_addr, dst_addr } =>{
        res.extend_from_slice(&src_addr);
        res.extend_from_slice(&dst_addr);
      },
      ProxyAddr::AfUnspec => {},
    };

    res
  }
}

fn get_family(addr: &ProxyAddr) -> u8 {
  match addr {
    &ProxyAddr::Ipv4Addr{ src_addr: _, dst_addr: _ } => 0x10 | 0x01, // AF_INET  = 1 + STREAM = 1
    &ProxyAddr::Ipv6Addr{ src_addr: _, dst_addr: _ } => 0x20 | 0x01, // AF_INET6 = 2 + STREAM = 1
    &ProxyAddr::UnixAddr{ src_addr: _, dst_addr: _ } => 0x30 | 0x01, // AF_UNIX  = 3 + STREAM = 1
    &ProxyAddr::AfUnspec => 0x00, // AF_UNSPEC + UNSPEC
  }
}

fn u16_to_array_of_u8(x: u16) -> [u8; 2] {
    let b1 : u8 = ((x >> 8) & 0xff) as u8;
    let b2 : u8 = (x & 0xff) as u8;
    return [b1, b2]
}

#[cfg(test)]
mod testV2 {

  use super::*;
  use std::net::{Ipv4Addr, Ipv6Addr};

  #[test]
  fn test_u16_to_array_of_u8() {
    let val_u16: u16 = 65534;
    let expected = [0xff, 0xfe];
    assert_eq!(expected, u16_to_array_of_u8(val_u16));
  }

  #[test]
  fn test_deserialize_tcp_ipv4_proxy_protocol_header() {
    let src_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(125, 25, 10, 1)), 8080);
    let dst_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(10, 4, 5, 8)), 4200);

    let header = HeaderV2::new(src_addr, dst_addr);
    let expected = &[
      0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A, // MAGIC header
      0x20,                                                                   // Version 2 and command LOCAL
      0x11,                                                                   // family AF_UNIX with IPv4
      0x00, 0x0C,                                                             // address sizes = 12
      0x7D, 0x19, 0x0A, 0x01,                                                 // source address
      0x0A, 0x04, 0x05, 0x08,                                                 // destination address
      0x1F, 0x90,                                                             // source port
      0x10, 0x68,                                                             // destination port
    ];

    assert_eq!(expected, &header.into_bytes()[..]);
  }

  #[test]
  fn test_deserialize_tcp_ipv6_proxy_protocol_header() {
    let src_addr = SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), 8080);
    let dst_addr = SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), 4200);

    let header = HeaderV2::new(src_addr, dst_addr);
    let expected = vec![
      0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A,                         // MAGIC header
      0x20,                                                                                // Version 2 and command LOCAL
      0x21,                                                                                // family AF_UNIX with IPv6
      0x00, 0x24,                                                                                // address sizes = 36
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // source address
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // destination address
      0x1F, 0x90,                                                                                // source port
      0x10, 0x68,                                                                                // destination port
    ];

    assert_eq!(&expected[..], &header.into_bytes()[..]);
  }
}<|endoftext|>


<commit_before>// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.

// This software is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This software is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Parity.  If not, see <http://www.gnu.org/licenses/>.

//! Database utilities and definitions.

use cita_db::{DBTransaction, KeyValueDB};
use rlp::{decode, encode, Decodable, Encodable};
use std::collections::HashMap;
use std::hash::Hash;
use std::ops::Deref;
use util::RwLock;

// database columns
/// Column for State
pub const COL_STATE: Option<u32> = Some(0);
/// Column for Block headers
pub const COL_HEADERS: Option<u32> = Some(1);
/// Column for Block bodies
pub const COL_BODIES: Option<u32> = Some(2);
/// Column for Extras
pub const COL_EXTRA: Option<u32> = Some(3);
/// Column for Traces
pub const COL_TRACE: Option<u32> = Some(4);
/// Column for the empty accounts bloom filter.
pub const COL_ACCOUNT_BLOOM: Option<u32> = Some(5);
/// Number of columns in DB
pub const NUM_COLUMNS: Option<u32> = Some(6);

/// Modes for updating caches.
#[derive(Clone, Copy)]
pub enum CacheUpdatePolicy {
    /// Overwrite entries.
    Overwrite,
    /// Remove entries.
    Remove,
}

/// A cache for arbitrary key-value pairs.
pub trait Cache<K, V> {
    /// Insert an entry into the cache and get the old value.
    fn insert(&mut self, k: K, v: V) -> Option<V>;

    /// Remove an entry from the cache, getting the old value if it existed.
    fn remove(&mut self, k: &K) -> Option<V>;

    /// Query the cache for a key's associated value.
    fn get(&self, k: &K) -> Option<&V>;
}

#[allow(unknown_lints, clippy::implicit_hasher)] // TODO clippy
impl<K, V> Cache<K, V> for HashMap<K, V>
where
    K: Hash + Eq,
{
    fn insert(&mut self, k: K, v: V) -> Option<V> {
        HashMap::insert(self, k, v)
    }

    fn remove(&mut self, k: &K) -> Option<V> {
        HashMap::remove(self, k)
    }

    fn get(&self, k: &K) -> Option<&V> {
        HashMap::get(self, k)
    }
}

/// Should be used to get database key associated with given value.
pub trait Key<T> {
    /// The db key associated with this value.
    type Target: Deref<Target = [u8]>;

    /// Returns db key.
    fn key(&self) -> Self::Target;
}

/// Should be used to write value into database.
pub trait Writable {
    /// Writes the value into the database.
    fn write<T, R>(&mut self, col: Option<u32>, key: &Key<T, Target = R>, value: &T)
    where
        T: Encodable,
        R: Deref<Target = [u8]>;

    /// Deletes key from the databse.
    fn delete<T, R>(&mut self, col: Option<u32>, key: &Key<T, Target = R>)
    where
        T: Encodable,
        R: Deref<Target = [u8]>;

    /// Writes the value into the database and updates the cache.
    fn write_with_cache<K, T, R>(
        &mut self,
        col: Option<u32>,
        cache: &mut Cache<K, T>,
        key: K,
        value: T,
        policy: CacheUpdatePolicy,
    ) where
        K: Key<T, Target = R> + Hash + Eq,
        T: Encodable,
        R: Deref<Target = [u8]>,
    {
        self.write(col, &key, &value);
        match policy {
            CacheUpdatePolicy::Overwrite => {
                cache.insert(key, value);
            }
            CacheUpdatePolicy::Remove => {
                cache.remove(&key);
            }
        }
    }

    /// Writes the values into the database and updates the cache.
    fn extend_with_cache<K, T, R>(
        &mut self,
        col: Option<u32>,
        cache: &mut Cache<K, T>,
        values: HashMap<K, T>,
        policy: CacheUpdatePolicy,
    ) where
        K: Key<T, Target = R> + Hash + Eq,
        T: Encodable,
        R: Deref<Target = [u8]>,
    {
        match policy {
            CacheUpdatePolicy::Overwrite => {
                for (key, value) in values {
                    self.write(col, &key, &value);
                    cache.insert(key, value);
                }
            }
            CacheUpdatePolicy::Remove => {
                for (key, value) in &values {
                    self.write(col, key, value);
                    cache.remove(key);
                }
            }
        }
    }

    /// Writes and removes the values into the database and updates the cache.
    fn extend_with_option_cache<K, T, R>(
        &mut self,
        col: Option<u32>,
        cache: &mut Cache<K, Option<T>>,
        values: HashMap<K, Option<T>>,
        policy: CacheUpdatePolicy,
    ) where
        K: Key<T, Target = R> + Hash + Eq,
        T: Encodable,
        R: Deref<Target = [u8]>,
    {
        match policy {
            CacheUpdatePolicy::Overwrite => {
                for (key, value) in values {
                    match value {
                        Some(ref v) => self.write(col, &key, v),
                        None => self.delete(col, &key),
                    }
                    cache.insert(key, value);
                }
            }
            CacheUpdatePolicy::Remove => {
                for (key, value) in values {
                    match value {
                        Some(v) => self.write(col, &key, &v),
                        None => self.delete(col, &key),
                    }
                    cache.remove(&key);
                }
            }
        }
    }
}

/// Should be used to read values from database.
pub trait Readable {
    /// Returns value for given key.
    fn read<T, R>(&self, col: Option<u32>, key: &Key<T, Target = R>) -> Option<T>
    where
        T: Decodable,
        R: Deref<Target = [u8]>;

    /// Returns value for given key either in cache or in database.
    fn read_with_cache<K, T, C>(&self, col: Option<u32>, cache: &RwLock<C>, key: &K) -> Option<T>
    where
        K: Key<T> + Eq + Hash + Clone,
        T: Clone + Decodable,
        C: Cache<K, T>,
    {
        {
            let read = cache.read();
            if let Some(v) = read.get(key) {
                return Some(v.clone());
            }
        }

        self.read(col, key).map(|value: T| {
            let mut write = cache.write();
            write.insert(key.clone(), value.clone());
            value
        })
    }

    /// Returns true if given value exists.
    fn exists<T, R>(&self, col: Option<u32>, key: &Key<T, Target = R>) -> bool
    where
        R: Deref<Target = [u8]>;

    /// Returns true if given value exists either in cache or in database.
    fn exists_with_cache<K, T, R, C>(&self, col: Option<u32>, cache: &RwLock<C>, key: &K) -> bool
    where
        K: Eq + Hash + Key<T, Target = R>,
        R: Deref<Target = [u8]>,
        C: Cache<K, T>,
    {
        {
            let read = cache.read();
            if read.get(key).is_some() {
                return true;
            }
        }

        self.exists::<T, R>(col, key)
    }
}

impl Writable for DBTransaction {
    fn write<T, R>(&mut self, col: Option<u32>, key: &Key<T, Target = R>, value: &T)
    where
        T: Encodable,
        R: Deref<Target = [u8]>,
    {
        self.put(col, &key.key(), &encode(value));
    }

    fn delete<T, R>(&mut self, col: Option<u32>, key: &Key<T, Target = R>)
    where
        T: Encodable,
        R: Deref<Target = [u8]>,
    {
        self.delete(col, &key.key());
    }
}

impl<KVDB: KeyValueDB + ?Sized> Readable for KVDB {
    fn read<T, R>(&self, col: Option<u32>, key: &Key<T, Target = R>) -> Option<T>
    where
        T: Decodable,
        R: Deref<Target = [u8]>,
    {
        let result = self.get(col, &key.key());

        match result {
            Ok(option) => option.map(|v| decode(&v)),
            Err(err) => {
                panic!(
                    "db get failed, key: {:?}, err: {:?}",
                    &key.key() as &[u8],
                    err
                );
            }
        }
    }

    fn exists<T, R>(&self, col: Option<u32>, key: &Key<T, Target = R>) -> bool
    where
        R: Deref<Target = [u8]>,
    {
        let result = self.get(col, &key.key());

        match result {
            Ok(v) => v.is_some(),
            Err(err) => {
                panic!(
                    "db get failed, key: {:?}, err: {:?}",
                    &key.key() as &[u8],
                    err
                );
            }
        }
    }
}
<commit_msg>Revert "remove unused db column"<commit_after>// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.

// This software is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This software is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Parity.  If not, see <http://www.gnu.org/licenses/>.

//! Database utilities and definitions.

use cita_db::{DBTransaction, KeyValueDB};
use rlp::{decode, encode, Decodable, Encodable};
use std::collections::HashMap;
use std::hash::Hash;
use std::ops::Deref;
use util::RwLock;

// database columns
/// Column for State
pub const COL_STATE: Option<u32> = Some(0);
/// Column for Block headers
pub const COL_HEADERS: Option<u32> = Some(1);
/// Column for Block bodies
pub const COL_BODIES: Option<u32> = Some(2);
/// Column for Extras
pub const COL_EXTRA: Option<u32> = Some(3);
/// Column for Traces
pub const COL_TRACE: Option<u32> = Some(4);
/// Column for the empty accounts bloom filter.
pub const COL_ACCOUNT_BLOOM: Option<u32> = Some(5);
/// Column for general information from the local node which can persist.
pub const COL_NODE_INFO: Option<u32> = Some(6);
/// Number of columns in DB
pub const NUM_COLUMNS: Option<u32> = Some(7);

/// Modes for updating caches.
#[derive(Clone, Copy)]
pub enum CacheUpdatePolicy {
    /// Overwrite entries.
    Overwrite,
    /// Remove entries.
    Remove,
}

/// A cache for arbitrary key-value pairs.
pub trait Cache<K, V> {
    /// Insert an entry into the cache and get the old value.
    fn insert(&mut self, k: K, v: V) -> Option<V>;

    /// Remove an entry from the cache, getting the old value if it existed.
    fn remove(&mut self, k: &K) -> Option<V>;

    /// Query the cache for a key's associated value.
    fn get(&self, k: &K) -> Option<&V>;
}

#[allow(unknown_lints, clippy::implicit_hasher)] // TODO clippy
impl<K, V> Cache<K, V> for HashMap<K, V>
where
    K: Hash + Eq,
{
    fn insert(&mut self, k: K, v: V) -> Option<V> {
        HashMap::insert(self, k, v)
    }

    fn remove(&mut self, k: &K) -> Option<V> {
        HashMap::remove(self, k)
    }

    fn get(&self, k: &K) -> Option<&V> {
        HashMap::get(self, k)
    }
}

/// Should be used to get database key associated with given value.
pub trait Key<T> {
    /// The db key associated with this value.
    type Target: Deref<Target = [u8]>;

    /// Returns db key.
    fn key(&self) -> Self::Target;
}

/// Should be used to write value into database.
pub trait Writable {
    /// Writes the value into the database.
    fn write<T, R>(&mut self, col: Option<u32>, key: &Key<T, Target = R>, value: &T)
    where
        T: Encodable,
        R: Deref<Target = [u8]>;

    /// Deletes key from the databse.
    fn delete<T, R>(&mut self, col: Option<u32>, key: &Key<T, Target = R>)
    where
        T: Encodable,
        R: Deref<Target = [u8]>;

    /// Writes the value into the database and updates the cache.
    fn write_with_cache<K, T, R>(
        &mut self,
        col: Option<u32>,
        cache: &mut Cache<K, T>,
        key: K,
        value: T,
        policy: CacheUpdatePolicy,
    ) where
        K: Key<T, Target = R> + Hash + Eq,
        T: Encodable,
        R: Deref<Target = [u8]>,
    {
        self.write(col, &key, &value);
        match policy {
            CacheUpdatePolicy::Overwrite => {
                cache.insert(key, value);
            }
            CacheUpdatePolicy::Remove => {
                cache.remove(&key);
            }
        }
    }

    /// Writes the values into the database and updates the cache.
    fn extend_with_cache<K, T, R>(
        &mut self,
        col: Option<u32>,
        cache: &mut Cache<K, T>,
        values: HashMap<K, T>,
        policy: CacheUpdatePolicy,
    ) where
        K: Key<T, Target = R> + Hash + Eq,
        T: Encodable,
        R: Deref<Target = [u8]>,
    {
        match policy {
            CacheUpdatePolicy::Overwrite => {
                for (key, value) in values {
                    self.write(col, &key, &value);
                    cache.insert(key, value);
                }
            }
            CacheUpdatePolicy::Remove => {
                for (key, value) in &values {
                    self.write(col, key, value);
                    cache.remove(key);
                }
            }
        }
    }

    /// Writes and removes the values into the database and updates the cache.
    fn extend_with_option_cache<K, T, R>(
        &mut self,
        col: Option<u32>,
        cache: &mut Cache<K, Option<T>>,
        values: HashMap<K, Option<T>>,
        policy: CacheUpdatePolicy,
    ) where
        K: Key<T, Target = R> + Hash + Eq,
        T: Encodable,
        R: Deref<Target = [u8]>,
    {
        match policy {
            CacheUpdatePolicy::Overwrite => {
                for (key, value) in values {
                    match value {
                        Some(ref v) => self.write(col, &key, v),
                        None => self.delete(col, &key),
                    }
                    cache.insert(key, value);
                }
            }
            CacheUpdatePolicy::Remove => {
                for (key, value) in values {
                    match value {
                        Some(v) => self.write(col, &key, &v),
                        None => self.delete(col, &key),
                    }
                    cache.remove(&key);
                }
            }
        }
    }
}

/// Should be used to read values from database.
pub trait Readable {
    /// Returns value for given key.
    fn read<T, R>(&self, col: Option<u32>, key: &Key<T, Target = R>) -> Option<T>
    where
        T: Decodable,
        R: Deref<Target = [u8]>;

    /// Returns value for given key either in cache or in database.
    fn read_with_cache<K, T, C>(&self, col: Option<u32>, cache: &RwLock<C>, key: &K) -> Option<T>
    where
        K: Key<T> + Eq + Hash + Clone,
        T: Clone + Decodable,
        C: Cache<K, T>,
    {
        {
            let read = cache.read();
            if let Some(v) = read.get(key) {
                return Some(v.clone());
            }
        }

        self.read(col, key).map(|value: T| {
            let mut write = cache.write();
            write.insert(key.clone(), value.clone());
            value
        })
    }

    /// Returns true if given value exists.
    fn exists<T, R>(&self, col: Option<u32>, key: &Key<T, Target = R>) -> bool
    where
        R: Deref<Target = [u8]>;

    /// Returns true if given value exists either in cache or in database.
    fn exists_with_cache<K, T, R, C>(&self, col: Option<u32>, cache: &RwLock<C>, key: &K) -> bool
    where
        K: Eq + Hash + Key<T, Target = R>,
        R: Deref<Target = [u8]>,
        C: Cache<K, T>,
    {
        {
            let read = cache.read();
            if read.get(key).is_some() {
                return true;
            }
        }

        self.exists::<T, R>(col, key)
    }
}

impl Writable for DBTransaction {
    fn write<T, R>(&mut self, col: Option<u32>, key: &Key<T, Target = R>, value: &T)
    where
        T: Encodable,
        R: Deref<Target = [u8]>,
    {
        self.put(col, &key.key(), &encode(value));
    }

    fn delete<T, R>(&mut self, col: Option<u32>, key: &Key<T, Target = R>)
    where
        T: Encodable,
        R: Deref<Target = [u8]>,
    {
        self.delete(col, &key.key());
    }
}

impl<KVDB: KeyValueDB + ?Sized> Readable for KVDB {
    fn read<T, R>(&self, col: Option<u32>, key: &Key<T, Target = R>) -> Option<T>
    where
        T: Decodable,
        R: Deref<Target = [u8]>,
    {
        let result = self.get(col, &key.key());

        match result {
            Ok(option) => option.map(|v| decode(&v)),
            Err(err) => {
                panic!(
                    "db get failed, key: {:?}, err: {:?}",
                    &key.key() as &[u8],
                    err
                );
            }
        }
    }

    fn exists<T, R>(&self, col: Option<u32>, key: &Key<T, Target = R>) -> bool
    where
        R: Deref<Target = [u8]>,
    {
        let result = self.get(col, &key.key());

        match result {
            Ok(v) => v.is_some(),
            Err(err) => {
                panic!(
                    "db get failed, key: {:?}, err: {:?}",
                    &key.key() as &[u8],
                    err
                );
            }
        }
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

<%namespace name="helpers" file="/helpers.mako.rs" />

<% data.new_style_struct("Pointing", inherited=True, gecko_name="UserInterface") %>

<%helpers:longhand name="cursor" animatable="False" spec="https://drafts.csswg.org/css-ui/#cursor">
    pub use self::computed_value::T as SpecifiedValue;
    use values::NoViewportPercentage;
    use values::computed::ComputedValueAsSpecified;
    use values::specified::url::SpecifiedUrl;

    impl ComputedValueAsSpecified for SpecifiedValue {}
    impl NoViewportPercentage for SpecifiedValue {}

    pub mod computed_value {
        use std::fmt;
        use style_traits::cursor::Cursor;
        use style_traits::ToCss;
        use values::specified::url::SpecifiedUrl;

        #[derive(Clone, PartialEq, Copy, Debug)]
        #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
        pub enum Keyword {
            AutoCursor,
            SpecifiedCursor(Cursor),
        }

        #[cfg(not(feature = "gecko"))]
        pub type T = Keyword;

        #[cfg(feature = "gecko")]
        #[derive(Clone, PartialEq, Debug)]
        pub struct Image {
            pub url: SpecifiedUrl,
            pub hotspot: Option<(f32, f32)>,
        }

        #[cfg(feature = "gecko")]
        #[derive(Clone, PartialEq, Debug)]
        pub struct T {
            pub images: Vec<Image>,
            pub keyword: Keyword,
        }

        impl ToCss for Keyword {
            fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
                match *self {
                    Keyword::AutoCursor => dest.write_str("auto"),
                    Keyword::SpecifiedCursor(c) => c.to_css(dest),
                }
            }
        }

        #[cfg(feature = "gecko")]
        impl ToCss for Image {
            fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
                try!(self.url.to_css(dest));
                if let Some((x, y)) = self.hotspot {
                    try!(dest.write_str(" "));
                    try!(x.to_css(dest));
                    try!(dest.write_str(" "));
                    try!(y.to_css(dest));
                }
                Ok(())
            }
        }

        #[cfg(feature = "gecko")]
        impl ToCss for T {
            fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
                for url in &self.images {
                    try!(url.to_css(dest));
                    try!(dest.write_str(", "));
                }
                self.keyword.to_css(dest)
            }
        }
    }

    #[cfg(not(feature = "gecko"))]
    #[inline]
    pub fn get_initial_value() -> computed_value::T {
        computed_value::Keyword::AutoCursor
    }

    #[cfg(feature = "gecko")]
    #[inline]
    pub fn get_initial_value() -> computed_value::T {
        computed_value::T {
            images: vec![],
            keyword: computed_value::Keyword::AutoCursor
        }
    }

    impl Parse for computed_value::Keyword {
        fn parse(_context: &ParserContext, input: &mut Parser) -> Result<computed_value::Keyword, ()> {
            use std::ascii::AsciiExt;
            use style_traits::cursor::Cursor;
            let ident = try!(input.expect_ident());
            if ident.eq_ignore_ascii_case("auto") {
                Ok(computed_value::Keyword::AutoCursor)
            } else {
                Cursor::from_css_keyword(&ident).map(computed_value::Keyword::SpecifiedCursor)
            }
        }
    }

    #[cfg(feature = "gecko")]
    fn parse_image(context: &ParserContext, input: &mut Parser) -> Result<computed_value::Image, ()> {
        Ok(computed_value::Image {
            url: try!(SpecifiedUrl::parse(context, input)),
            hotspot: match input.try(|input| input.expect_number()) {
                Ok(number) => Some((number, try!(input.expect_number()))),
                Err(()) => None,
            },
        })
    }

    #[cfg(not(feature = "gecko"))]
    pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
        computed_value::Keyword::parse(context, input)
    }

    /// cursor: [<url> [<number> <number>]?]# [auto | default | ...]
    #[cfg(feature = "gecko")]
    pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
        let mut images = vec![];
        loop {
            match input.try(|input| parse_image(context, input)) {
                Ok(image) => images.push(image),
                Err(()) => break,
            }
            try!(input.expect_comma());
        }

        Ok(computed_value::T {
            images: images,
            keyword: try!(computed_value::Keyword::parse(context, input)),
        })
    }
</%helpers:longhand>

// NB: `pointer-events: auto` (and use of `pointer-events` in anything that isn't SVG, in fact)
// is nonstandard, slated for CSS4-UI.
// TODO(pcwalton): SVG-only values.
${helpers.single_keyword("pointer-events", "auto none", animatable=False,
                         spec="https://www.w3.org/TR/SVG11/interact.html#PointerEventsProperty")}

${helpers.single_keyword("-moz-user-input", "none enabled disabled",
                         products="gecko", gecko_ffi_name="mUserInput",
                         gecko_enum_prefix="StyleUserInput",
                         gecko_inexhaustive=True,
                         animatable=False,
                         spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-user-input)")}

${helpers.single_keyword("-moz-user-modify", "read-only read-write write-only",
                         products="gecko", gecko_ffi_name="mUserModify",
                         gecko_enum_prefix="StyleUserModify",
                         gecko_inexhaustive=True,
                         animatable=False,
                         spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-user-modify)")}

${helpers.single_keyword("-moz-user-focus",
                         "ignore normal select-after select-before select-menu select-same select-all none",
                         products="gecko", gecko_ffi_name="mUserFocus",
                         gecko_enum_prefix="StyleUserFocus",
                         gecko_inexhaustive=True,
                         animatable=False,
                         spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-user-focus)")}
<commit_msg>Auto merge of #15090 - upsuper:moz-user-input, r=Wafflespeanut<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

<%namespace name="helpers" file="/helpers.mako.rs" />

<% data.new_style_struct("Pointing", inherited=True, gecko_name="UserInterface") %>

<%helpers:longhand name="cursor" animatable="False" spec="https://drafts.csswg.org/css-ui/#cursor">
    pub use self::computed_value::T as SpecifiedValue;
    use values::NoViewportPercentage;
    use values::computed::ComputedValueAsSpecified;
    use values::specified::url::SpecifiedUrl;

    impl ComputedValueAsSpecified for SpecifiedValue {}
    impl NoViewportPercentage for SpecifiedValue {}

    pub mod computed_value {
        use std::fmt;
        use style_traits::cursor::Cursor;
        use style_traits::ToCss;
        use values::specified::url::SpecifiedUrl;

        #[derive(Clone, PartialEq, Copy, Debug)]
        #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
        pub enum Keyword {
            AutoCursor,
            SpecifiedCursor(Cursor),
        }

        #[cfg(not(feature = "gecko"))]
        pub type T = Keyword;

        #[cfg(feature = "gecko")]
        #[derive(Clone, PartialEq, Debug)]
        pub struct Image {
            pub url: SpecifiedUrl,
            pub hotspot: Option<(f32, f32)>,
        }

        #[cfg(feature = "gecko")]
        #[derive(Clone, PartialEq, Debug)]
        pub struct T {
            pub images: Vec<Image>,
            pub keyword: Keyword,
        }

        impl ToCss for Keyword {
            fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
                match *self {
                    Keyword::AutoCursor => dest.write_str("auto"),
                    Keyword::SpecifiedCursor(c) => c.to_css(dest),
                }
            }
        }

        #[cfg(feature = "gecko")]
        impl ToCss for Image {
            fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
                try!(self.url.to_css(dest));
                if let Some((x, y)) = self.hotspot {
                    try!(dest.write_str(" "));
                    try!(x.to_css(dest));
                    try!(dest.write_str(" "));
                    try!(y.to_css(dest));
                }
                Ok(())
            }
        }

        #[cfg(feature = "gecko")]
        impl ToCss for T {
            fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
                for url in &self.images {
                    try!(url.to_css(dest));
                    try!(dest.write_str(", "));
                }
                self.keyword.to_css(dest)
            }
        }
    }

    #[cfg(not(feature = "gecko"))]
    #[inline]
    pub fn get_initial_value() -> computed_value::T {
        computed_value::Keyword::AutoCursor
    }

    #[cfg(feature = "gecko")]
    #[inline]
    pub fn get_initial_value() -> computed_value::T {
        computed_value::T {
            images: vec![],
            keyword: computed_value::Keyword::AutoCursor
        }
    }

    impl Parse for computed_value::Keyword {
        fn parse(_context: &ParserContext, input: &mut Parser) -> Result<computed_value::Keyword, ()> {
            use std::ascii::AsciiExt;
            use style_traits::cursor::Cursor;
            let ident = try!(input.expect_ident());
            if ident.eq_ignore_ascii_case("auto") {
                Ok(computed_value::Keyword::AutoCursor)
            } else {
                Cursor::from_css_keyword(&ident).map(computed_value::Keyword::SpecifiedCursor)
            }
        }
    }

    #[cfg(feature = "gecko")]
    fn parse_image(context: &ParserContext, input: &mut Parser) -> Result<computed_value::Image, ()> {
        Ok(computed_value::Image {
            url: try!(SpecifiedUrl::parse(context, input)),
            hotspot: match input.try(|input| input.expect_number()) {
                Ok(number) => Some((number, try!(input.expect_number()))),
                Err(()) => None,
            },
        })
    }

    #[cfg(not(feature = "gecko"))]
    pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
        computed_value::Keyword::parse(context, input)
    }

    /// cursor: [<url> [<number> <number>]?]# [auto | default | ...]
    #[cfg(feature = "gecko")]
    pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
        let mut images = vec![];
        loop {
            match input.try(|input| parse_image(context, input)) {
                Ok(image) => images.push(image),
                Err(()) => break,
            }
            try!(input.expect_comma());
        }

        Ok(computed_value::T {
            images: images,
            keyword: try!(computed_value::Keyword::parse(context, input)),
        })
    }
</%helpers:longhand>

// NB: `pointer-events: auto` (and use of `pointer-events` in anything that isn't SVG, in fact)
// is nonstandard, slated for CSS4-UI.
// TODO(pcwalton): SVG-only values.
${helpers.single_keyword("pointer-events", "auto none", animatable=False,
                         spec="https://www.w3.org/TR/SVG11/interact.html#PointerEventsProperty")}

${helpers.single_keyword("-moz-user-input", "auto none enabled disabled",
                         products="gecko", gecko_ffi_name="mUserInput",
                         gecko_enum_prefix="StyleUserInput",
                         gecko_inexhaustive=True,
                         animatable=False,
                         spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-user-input)")}

${helpers.single_keyword("-moz-user-modify", "read-only read-write write-only",
                         products="gecko", gecko_ffi_name="mUserModify",
                         gecko_enum_prefix="StyleUserModify",
                         gecko_inexhaustive=True,
                         animatable=False,
                         spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-user-modify)")}

${helpers.single_keyword("-moz-user-focus",
                         "none ignore normal select-after select-before select-menu select-same select-all",
                         products="gecko", gecko_ffi_name="mUserFocus",
                         gecko_enum_prefix="StyleUserFocus",
                         gecko_inexhaustive=True,
                         animatable=False,
                         spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-user-focus)")}
<|endoftext|>


<commit_before>//! System tests for compiling Rust code with cargo.
//!
//! Any copyright is dedicated to the Public Domain.
//! http://creativecommons.org/publicdomain/zero/1.0/

#![deny(rust_2018_idioms)]

#[cfg(all(not(target_os = "windows"), not(target_os = "macos")))]
#[macro_use]
extern crate log;

/// Test that building a simple Rust crate with cargo using sccache results in a cache hit
/// when built a second time.
#[test]
#[cfg(all(not(target_os = "windows"), not(target_os = "macos")))]
fn test_rust_cargo() {
    test_rust_cargo_cmd("check");
    test_rust_cargo_cmd("build");
}

#[cfg(all(not(target_os = "windows"), not(target_os = "macos")))]
fn test_rust_cargo_cmd(cmd: &str) {
    use assert_cmd::prelude::*;
    use chrono::Local;
    use predicates::prelude::*;
    use std::env;
    use std::fs;
    use std::io::Write;
    use std::path::Path;
    use std::process::{Command, Stdio};

    fn sccache_command() -> Command {
        Command::new(assert_cmd::cargo::cargo_bin("sccache"))
    }

    fn stop() {
        trace!("sccache --stop-server");
        drop(
            sccache_command()
                .arg("--stop-server")
                .stdout(Stdio::null())
                .stderr(Stdio::null())
                .status(),
        );
    }

    drop(
        env_logger::Builder::new()
            .format(|f, record| {
                write!(
                    f,
                    "{} [{}] - {}",
                    Local::now().format("%Y-%m-%dT%H:%M:%S%.3f"),
                    record.level(),
                    record.args()
                )
            })
            .parse_env("RUST_LOG")
            .try_init(),
    );
    let cargo = env!("CARGO");
    debug!("cargo: {}", cargo);
    let sccache = assert_cmd::cargo::cargo_bin("sccache");
    debug!("sccache: {:?}", sccache);
    let crate_dir = Path::new(file!()).parent().unwrap().join("test-crate");
    // Ensure there's no existing sccache server running.
    stop();
    // Create a temp directory to use for the disk cache.
    let tempdir = tempfile::Builder::new()
        .prefix("sccache_test_rust_cargo")
        .tempdir()
        .unwrap();
    let cache_dir = tempdir.path().join("cache");
    fs::create_dir(&cache_dir).unwrap();
    let cargo_dir = tempdir.path().join("cargo");
    fs::create_dir(&cargo_dir).unwrap();
    // Start a new sccache server.
    trace!("sccache --start-server");
    sccache_command()
        .arg("--start-server")
        .env("SCCACHE_DIR", &cache_dir)
        .assert()
        .success();
    // `cargo clean` first, just to be sure there's no leftover build objects.
    let envs = vec![
        ("RUSTC_WRAPPER", &sccache),
        ("CARGO_TARGET_DIR", &cargo_dir),
    ];
    Command::new(&cargo)
        .args(&["clean"])
        .envs(envs.iter().copied())
        .current_dir(&crate_dir)
        .assert()
        .success();
    // Now build the crate with cargo.
    Command::new(&cargo)
        .args(&[cmd, "--color=never"])
        .envs(envs.iter().copied())
        .current_dir(&crate_dir)
        .assert()
        .stderr(predicates::str::contains("\x1b[").from_utf8().not())
        .success();
    // Clean it so we can build it again.
    Command::new(&cargo)
        .args(&["clean"])
        .envs(envs.iter().copied())
        .current_dir(&crate_dir)
        .assert()
        .success();
    Command::new(&cargo)
        .args(&[cmd, "--color=always"])
        .envs(envs.iter().copied())
        .current_dir(&crate_dir)
        .assert()
        .stderr(predicates::str::contains("\x1b[").from_utf8())
        .success();
    // Now get the stats and ensure that we had a cache hit for the second build.
    // Ideally we'd check the stats more usefully here--the test crate has one dependency (itoa)
    // so there are two separate compilations, but cargo will build the test crate with
    // incremental compilation enabled, so sccache will not cache it.
    trace!("sccache --show-stats");
    sccache_command()
        .args(&["--show-stats", "--stats-format=json"])
        .assert()
        .stdout(predicates::str::contains(r#""cache_hits":{"counts":{"Rust":1}}"#).from_utf8())
        .success();
    stop();
}
<commit_msg>Make sccache_cargo integration test more robust<commit_after>//! System tests for compiling Rust code with cargo.
//!
//! Any copyright is dedicated to the Public Domain.
//! http://creativecommons.org/publicdomain/zero/1.0/

#![deny(rust_2018_idioms)]

#[cfg(all(not(target_os = "windows"), not(target_os = "macos")))]
#[macro_use]
extern crate log;

/// Test that building a simple Rust crate with cargo using sccache results in a cache hit
/// when built a second time.
#[test]
#[cfg(all(not(target_os = "windows"), not(target_os = "macos")))]
fn test_rust_cargo() {
    test_rust_cargo_cmd("check");
    test_rust_cargo_cmd("build");
}

#[cfg(all(not(target_os = "windows"), not(target_os = "macos")))]
fn test_rust_cargo_cmd(cmd: &str) {
    use assert_cmd::prelude::*;
    use chrono::Local;
    use predicates::prelude::*;
    use std::env;
    use std::ffi::OsStr;
    use std::fs;
    use std::io::Write;
    use std::path::Path;
    use std::process::{Command, Stdio};

    fn sccache_command() -> Command {
        Command::new(assert_cmd::cargo::cargo_bin("sccache"))
    }

    fn stop() {
        trace!("sccache --stop-server");
        drop(
            sccache_command()
                .arg("--stop-server")
                .stdout(Stdio::null())
                .stderr(Stdio::null())
                .status(),
        );
    }

    drop(
        env_logger::Builder::new()
            .format(|f, record| {
                write!(
                    f,
                    "{} [{}] - {}",
                    Local::now().format("%Y-%m-%dT%H:%M:%S%.3f"),
                    record.level(),
                    record.args()
                )
            })
            .parse_env("RUST_LOG")
            .try_init(),
    );
    let cargo = env!("CARGO");
    debug!("cargo: {}", cargo);
    let sccache = assert_cmd::cargo::cargo_bin("sccache");
    debug!("sccache: {:?}", sccache);
    let crate_dir = Path::new(file!()).parent().unwrap().join("test-crate");
    // Ensure there's no existing sccache server running.
    stop();
    // Create a temp directory to use for the disk cache.
    let tempdir = tempfile::Builder::new()
        .prefix("sccache_test_rust_cargo")
        .tempdir()
        .unwrap();
    let cache_dir = tempdir.path().join("cache");
    fs::create_dir(&cache_dir).unwrap();
    let cargo_dir = tempdir.path().join("cargo");
    fs::create_dir(&cargo_dir).unwrap();
    // Start a new sccache server.
    trace!("sccache --start-server");
    sccache_command()
        .arg("--start-server")
        .env("SCCACHE_DIR", &cache_dir)
        .assert()
        .success();
    // `cargo clean` first, just to be sure there's no leftover build objects.
    let envs = vec![
        ("RUSTC_WRAPPER", sccache.as_ref()),
        ("CARGO_TARGET_DIR", cargo_dir.as_ref()),
        // Explicitly disable incremental compilation because sccache is unable
        // to cache it at the time of writing.
        ("CARGO_INCREMENTAL", OsStr::new("0")),
    ];
    Command::new(&cargo)
        .args(&["clean"])
        .envs(envs.iter().copied())
        .current_dir(&crate_dir)
        .assert()
        .success();
    // Now build the crate with cargo.
    Command::new(&cargo)
        .args(&[cmd, "--color=never"])
        .envs(envs.iter().copied())
        .current_dir(&crate_dir)
        .assert()
        .stderr(predicates::str::contains("\x1b[").from_utf8().not())
        .success();
    // Clean it so we can build it again.
    Command::new(&cargo)
        .args(&["clean"])
        .envs(envs.iter().copied())
        .current_dir(&crate_dir)
        .assert()
        .success();
    Command::new(&cargo)
        .args(&[cmd, "--color=always"])
        .envs(envs.iter().copied())
        .current_dir(&crate_dir)
        .assert()
        .stderr(predicates::str::contains("\x1b[").from_utf8())
        .success();
    // Now get the stats and ensure that we had a cache hit for the second build.
    // The test crate has one dependency (itoa) so there are two separate
    // compilations.
    trace!("sccache --show-stats");
    sccache_command()
        .args(&["--show-stats", "--stats-format=json"])
        .assert()
        .stdout(predicates::str::contains(r#""cache_hits":{"counts":{"Rust":2}}"#).from_utf8())
        .success();
    stop();
}
<|endoftext|>