<commit_before>use super::{Syllable,SyllableOptions,Subtitles,SubtitlesOptions,AsSyllableOptions};
use super::pos::{RowPosition,Size};
use std::ops::Deref;

#[derive(Debug,Serialize,Deserialize,Clone)]
pub struct Sentence {
    pub syllables: Vec<Syllable>,
    #[serde(skip_serializing,skip_deserializing)]
    pub position: RowPosition,
    #[serde(skip_serializing_if="Option::is_none")]
    pub sentence_options: Option<SentenceOptions>,
}

pub trait AsSentenceOptions {
    fn as_sentence_options(&self) -> Option<&SentenceOptions>;
    fn or_sentence_options(&self,other:Option<&SentenceOptions>) -> Option<SentenceOptions> {
        match (self.as_sentence_options(),other) {
            (Some(s),Some(other)) => Some(SentenceOptions {
                transitions: {
                    use std::cmp::Ord;
                    let mut t = s.transitions.iter()
                        .chain(other.transitions.iter())
                        .cloned()
                        .collect::<Vec<SentenceTransition>>();
                    t.sort_by(|a,b| Ord::cmp(&a.offset,&b.offset));
                    t
                },
                syllable_options: s.syllable_options.or_syllable_options(other.syllable_options.as_ref()),
                display_logo: s.display_logo.or(other.display_logo),
                transition_time_after: s.transition_time_after.or(other.transition_time_after),
                fade_time_after: s.fade_time_after.or(other.fade_time_after),
                transition_time_before: s.transition_time_before.or(other.transition_time_before),
                fade_time_before: s.fade_time_before.or(other.fade_time_before),
                row_position: s.row_position.or(other.row_position),
                size: s.size.or(other.size),
            }),
            (Some(s),None) => Some(s.clone()),
            (None,Some(other)) => Some(other.clone()),
            (None,None) => None
        }
    }
    fn as_syllable_options(&self, offset: i32) -> Option<SyllableOptions> {
        match self.as_sentence_options() {
            None => None,
            Some(sentence_options) => sentence_options.merge_at(offset).syllable_options
        }
    }
}

impl SentenceOptions {
    fn bare(&self) -> SentenceOptions {
        SentenceOptions {
            transitions: Vec::new(),
            syllable_options: self.syllable_options,
            display_logo: self.display_logo,
            transition_time_after: self.transition_time_after,
            fade_time_after: self.fade_time_after,
            transition_time_before: self.transition_time_before,
            fade_time_before: self.fade_time_before,
            row_position: self.row_position,
            size: self.size
        }
    }

    pub fn merge_at(&self, offset: i32) -> SentenceOptions {
        let mut sentence_options = Some(self.bare());
        let iter = self.transitions.iter()
            .take_while(|t| t.offset <= offset);
        for s in iter {
            sentence_options = s.new_options.or_sentence_options(sentence_options.as_ref());
        };
        sentence_options.unwrap()
    }
}

impl AsSentenceOptions for Sentence {
    fn as_sentence_options(&self) -> Option<&SentenceOptions> {
        self.sentence_options.as_ref()
    }
}

impl AsSentenceOptions for Subtitles {
    fn as_sentence_options(&self) -> Option<&SentenceOptions> {
        self.subtitles_options.sentence_options.as_ref()
    }
}

impl AsSentenceOptions for SentenceOptions {
    fn as_sentence_options(&self) -> Option<&SentenceOptions> {
        Some(self)
    }
}

impl AsSentenceOptions for SubtitlesOptions {
    fn as_sentence_options(&self) -> Option<&SentenceOptions> {
        self.sentence_options.as_ref()
    }
}

impl<T:AsSentenceOptions> AsSentenceOptions for Option<T> {
    fn as_sentence_options(&self) -> Option<&SentenceOptions> {
        match *self {
            Some(ref t) => t.as_sentence_options(),
            None => None
        }
    }
}

impl Sentence {
    /// used mainly for debugging purposes
    #[allow(dead_code)]
    pub fn text(&self) -> String {
        self.syllables.iter().fold(String::new(),|mut string,syllable| {
            string.push_str(&*syllable.text);
            string
        })
    }
}

#[derive(Debug,Clone,Default,Serialize,Deserialize)]
pub struct SentenceOptions {
    /// Global SyllableOptions
    #[serde(skip_serializing_if="Option::is_none")]
    pub syllable_options: Option<SyllableOptions>,
    #[serde(default,skip_serializing_if="Vec::is_empty")]
    pub transitions: Vec<SentenceTransition>,
    #[serde(skip_serializing_if="Option::is_none")]
    pub display_logo: Option<bool>,
    /// Total time where subtitles start appearing, before first syllable start playing
    #[serde(skip_serializing_if="Option::is_none")]
    pub transition_time_before: Option<u16>,
    /// Span where subtitles start appearing
    #[serde(skip_serializing_if="Option::is_none")]
    pub fade_time_before: Option<u16>,
    /// Total time where subtitles are disappearing, before first syllable start playing
    #[serde(skip_serializing_if="Option::is_none")]
    pub transition_time_after: Option<u16>,
    /// Span where subtitles start disappearing
    #[serde(skip_serializing_if="Option::is_none")]
    pub fade_time_after: Option<u16>,
    #[serde(skip_serializing_if="Option::is_none")]
    #[serde(rename = "position")]
    pub row_position: Option<RowPosition>,
    pub size: Option<Size<Option<f32>>>
}

impl Deref for SentenceOptions {
    type Target = Option<SyllableOptions>;
    fn deref(&self) -> &Option<SyllableOptions> {
        &self.syllable_options
    }
}

#[derive(Debug,Clone,Default,Serialize,Deserialize)]
pub struct SentenceTransition {
    pub offset: i32,
    pub new_options: SentenceOptions
}

#[derive(Debug,Clone)]
pub struct SentenceParameters {
    pub display_logo: bool,
    pub transition_time_before: u16,
    pub fade_time_before: u16,
    pub transition_time_after: u16,
    pub fade_time_after: u16,
    pub row_position: Option<RowPosition>,
    pub size:Size<Option<f32>>
}

impl From<(SentenceOptions,i32)> for SentenceParameters {
    fn from((sentence_options, time_offset): (SentenceOptions,i32)) -> Self {
        let sentence_options : SentenceOptions = sentence_options.merge_at(time_offset);
        SentenceParameters {
            display_logo: sentence_options.display_logo.unwrap_or(true),
            transition_time_before: sentence_options.transition_time_before.unwrap_or(800),
            fade_time_before: sentence_options.fade_time_before.unwrap_or(200),
            transition_time_after: sentence_options.transition_time_after.unwrap_or(500),
            fade_time_after: sentence_options.fade_time_after.unwrap_or(200),
            row_position: sentence_options.row_position,
            size: sentence_options.size.unwrap_or(Size {
                width:Some(0.95),
                height:Some(0.09)
            })
        }
    }
}

#[test]
fn test_sentence_options_propagation(){
    use super::*;
    use utils::RGB;
    let default_options = SentenceOptions {
        syllable_options: Some(SyllableOptions {
            alive_color: Some(Color::new(128,0,0)),
            dead_color: Some(Color::new(0,128,0)),
            transition_color: None,
            outline: None
        }),
        ..SentenceOptions::default()
    };
    let syllable_options = SyllableOptions {
        alive_color: Some(Color::new(172,172,172)),
        ..SyllableOptions::default()
    };
    let syllable_parameters = SyllableParameters::from(syllable_options.or_syllable_options(default_options.as_syllable_options()).unwrap());
    assert_eq!(syllable_parameters.alive_color,Color::new(172,172,172));
    assert_eq!(syllable_parameters.dead_color,Color::new(0,128,0));
    assert_eq!(syllable_parameters.transition_color,Color::new(255,0,0));
}
<commit_msg>Only serialize size if needed<commit_after>use super::{Syllable,SyllableOptions,Subtitles,SubtitlesOptions,AsSyllableOptions};
use super::pos::{RowPosition,Size};
use std::ops::Deref;

#[derive(Debug,Serialize,Deserialize,Clone)]
pub struct Sentence {
    pub syllables: Vec<Syllable>,
    #[serde(skip_serializing,skip_deserializing)]
    pub position: RowPosition,
    #[serde(skip_serializing_if="Option::is_none")]
    pub sentence_options: Option<SentenceOptions>,
}

pub trait AsSentenceOptions {
    fn as_sentence_options(&self) -> Option<&SentenceOptions>;
    fn or_sentence_options(&self,other:Option<&SentenceOptions>) -> Option<SentenceOptions> {
        match (self.as_sentence_options(),other) {
            (Some(s),Some(other)) => Some(SentenceOptions {
                transitions: {
                    use std::cmp::Ord;
                    let mut t = s.transitions.iter()
                        .chain(other.transitions.iter())
                        .cloned()
                        .collect::<Vec<SentenceTransition>>();
                    t.sort_by(|a,b| Ord::cmp(&a.offset,&b.offset));
                    t
                },
                syllable_options: s.syllable_options.or_syllable_options(other.syllable_options.as_ref()),
                display_logo: s.display_logo.or(other.display_logo),
                transition_time_after: s.transition_time_after.or(other.transition_time_after),
                fade_time_after: s.fade_time_after.or(other.fade_time_after),
                transition_time_before: s.transition_time_before.or(other.transition_time_before),
                fade_time_before: s.fade_time_before.or(other.fade_time_before),
                row_position: s.row_position.or(other.row_position),
                size: s.size.or(other.size),
            }),
            (Some(s),None) => Some(s.clone()),
            (None,Some(other)) => Some(other.clone()),
            (None,None) => None
        }
    }
    fn as_syllable_options(&self, offset: i32) -> Option<SyllableOptions> {
        match self.as_sentence_options() {
            None => None,
            Some(sentence_options) => sentence_options.merge_at(offset).syllable_options
        }
    }
}

impl SentenceOptions {
    fn bare(&self) -> SentenceOptions {
        SentenceOptions {
            transitions: Vec::new(),
            syllable_options: self.syllable_options,
            display_logo: self.display_logo,
            transition_time_after: self.transition_time_after,
            fade_time_after: self.fade_time_after,
            transition_time_before: self.transition_time_before,
            fade_time_before: self.fade_time_before,
            row_position: self.row_position,
            size: self.size
        }
    }

    pub fn merge_at(&self, offset: i32) -> SentenceOptions {
        let mut sentence_options = Some(self.bare());
        let iter = self.transitions.iter()
            .take_while(|t| t.offset <= offset);
        for s in iter {
            sentence_options = s.new_options.or_sentence_options(sentence_options.as_ref());
        };
        sentence_options.unwrap()
    }
}

impl AsSentenceOptions for Sentence {
    fn as_sentence_options(&self) -> Option<&SentenceOptions> {
        self.sentence_options.as_ref()
    }
}

impl AsSentenceOptions for Subtitles {
    fn as_sentence_options(&self) -> Option<&SentenceOptions> {
        self.subtitles_options.sentence_options.as_ref()
    }
}

impl AsSentenceOptions for SentenceOptions {
    fn as_sentence_options(&self) -> Option<&SentenceOptions> {
        Some(self)
    }
}

impl AsSentenceOptions for SubtitlesOptions {
    fn as_sentence_options(&self) -> Option<&SentenceOptions> {
        self.sentence_options.as_ref()
    }
}

impl<T:AsSentenceOptions> AsSentenceOptions for Option<T> {
    fn as_sentence_options(&self) -> Option<&SentenceOptions> {
        match *self {
            Some(ref t) => t.as_sentence_options(),
            None => None
        }
    }
}

impl Sentence {
    /// used mainly for debugging purposes
    #[allow(dead_code)]
    pub fn text(&self) -> String {
        self.syllables.iter().fold(String::new(),|mut string,syllable| {
            string.push_str(&*syllable.text);
            string
        })
    }
}

#[derive(Debug,Clone,Default,Serialize,Deserialize)]
pub struct SentenceOptions {
    /// Global SyllableOptions
    #[serde(skip_serializing_if="Option::is_none")]
    pub syllable_options: Option<SyllableOptions>,
    #[serde(default,skip_serializing_if="Vec::is_empty")]
    pub transitions: Vec<SentenceTransition>,
    #[serde(skip_serializing_if="Option::is_none")]
    pub display_logo: Option<bool>,
    /// Total time where subtitles start appearing, before first syllable start playing
    #[serde(skip_serializing_if="Option::is_none")]
    pub transition_time_before: Option<u16>,
    /// Span where subtitles start appearing
    #[serde(skip_serializing_if="Option::is_none")]
    pub fade_time_before: Option<u16>,
    /// Total time where subtitles are disappearing, before first syllable start playing
    #[serde(skip_serializing_if="Option::is_none")]
    pub transition_time_after: Option<u16>,
    /// Span where subtitles start disappearing
    #[serde(skip_serializing_if="Option::is_none")]
    pub fade_time_after: Option<u16>,
    #[serde(skip_serializing_if="Option::is_none")]
    #[serde(rename = "position")]
    pub row_position: Option<RowPosition>,
    #[serde(skip_serializing_if="Option::is_none")]
    pub size: Option<Size<Option<f32>>>
}

impl Deref for SentenceOptions {
    type Target = Option<SyllableOptions>;
    fn deref(&self) -> &Option<SyllableOptions> {
        &self.syllable_options
    }
}

#[derive(Debug,Clone,Default,Serialize,Deserialize)]
pub struct SentenceTransition {
    pub offset: i32,
    pub new_options: SentenceOptions
}

#[derive(Debug,Clone)]
pub struct SentenceParameters {
    pub display_logo: bool,
    pub transition_time_before: u16,
    pub fade_time_before: u16,
    pub transition_time_after: u16,
    pub fade_time_after: u16,
    pub row_position: Option<RowPosition>,
    pub size:Size<Option<f32>>
}

impl From<(SentenceOptions,i32)> for SentenceParameters {
    fn from((sentence_options, time_offset): (SentenceOptions,i32)) -> Self {
        let sentence_options : SentenceOptions = sentence_options.merge_at(time_offset);
        SentenceParameters {
            display_logo: sentence_options.display_logo.unwrap_or(true),
            transition_time_before: sentence_options.transition_time_before.unwrap_or(800),
            fade_time_before: sentence_options.fade_time_before.unwrap_or(200),
            transition_time_after: sentence_options.transition_time_after.unwrap_or(500),
            fade_time_after: sentence_options.fade_time_after.unwrap_or(200),
            row_position: sentence_options.row_position,
            size: sentence_options.size.unwrap_or(Size {
                width:Some(0.95),
                height:Some(0.09)
            })
        }
    }
}

#[test]
fn test_sentence_options_propagation(){
    use super::*;
    use utils::RGB;
    let default_options = SentenceOptions {
        syllable_options: Some(SyllableOptions {
            alive_color: Some(Color::new(128,0,0)),
            dead_color: Some(Color::new(0,128,0)),
            transition_color: None,
            outline: None
        }),
        ..SentenceOptions::default()
    };
    let syllable_options = SyllableOptions {
        alive_color: Some(Color::new(172,172,172)),
        ..SyllableOptions::default()
    };
    let syllable_parameters = SyllableParameters::from(syllable_options.or_syllable_options(default_options.as_syllable_options()).unwrap());
    assert_eq!(syllable_parameters.alive_color,Color::new(172,172,172));
    assert_eq!(syllable_parameters.dead_color,Color::new(0,128,0));
    assert_eq!(syllable_parameters.transition_color,Color::new(255,0,0));
}
<|endoftext|>


<commit_before>use super::{Syllable,SyllableOptions,Subtitles,SubtitlesOptions};
use super::pos::RowPosition;
use std::ops::Deref;

#[derive(Debug,Serialize,Deserialize,Clone)]
pub struct Sentence {
    pub syllables: Vec<Syllable>,
    #[serde(skip_serializing,skip_deserializing)]
    pub position: RowPosition,
    #[serde(skip_serializing_if="Option::is_none")]
    pub sentence_options: Option<SentenceOptions>,
}

pub trait AsSentenceOptions {
    fn as_sentence_options(&self) -> Option<&SentenceOptions>;
    fn or_sentence_options(&self,other:Option<&SentenceOptions>) -> Option<SentenceOptions> {
        match (self.as_sentence_options(),other) {
            (Some(s),Some(other)) => Some(SentenceOptions {
                syllable_options: s.syllable_options.or(other.syllable_options),
                display_logo: s.display_logo.or(other.display_logo),
                transition_time_after: s.transition_time_after.or(other.transition_time_after),
                fade_time_after: s.fade_time_after.or(other.fade_time_after),
                transition_time_before: s.transition_time_before.or(other.transition_time_before),
                fade_time_before: s.fade_time_before.or(other.fade_time_before),
                row_position: s.row_position.or(other.row_position),
            }),
            (Some(s),None) => Some(s.clone()),
            (None,Some(other)) => Some(other.clone()),
            (None,None) => None
        }
    }
}

impl AsSentenceOptions for Sentence {
    fn as_sentence_options(&self) -> Option<&SentenceOptions> {
        self.sentence_options.as_ref()
    }
}

impl AsSentenceOptions for Subtitles {
    fn as_sentence_options(&self) -> Option<&SentenceOptions> {
        self.subtitles_options.sentence_options.as_ref()
    }
}

impl AsSentenceOptions for SentenceOptions {
    fn as_sentence_options(&self) -> Option<&SentenceOptions> {
        Some(self)
    }
}

impl AsSentenceOptions for SubtitlesOptions {
    fn as_sentence_options(&self) -> Option<&SentenceOptions> {
        self.sentence_options.as_ref()
    }
}

impl<T:AsSentenceOptions> AsSentenceOptions for Option<T> {
    fn as_sentence_options(&self) -> Option<&SentenceOptions> {
        match *self {
            Some(ref t) => t.as_sentence_options(),
            None => None
        }
    }
}

impl Sentence {
    /// used mainly for debugging purposes
    #[allow(dead_code)]
    pub fn text(&self) -> String {
        self.syllables.iter().fold(String::new(),|mut string,syllable| {
            string.push_str(&*syllable.text);
            string
        })
    }
}

#[derive(Debug,Clone,Copy,Default,Serialize,Deserialize)]
pub struct SentenceOptions {
    /// Global SyllableOptions
    #[serde(skip_serializing_if="Option::is_none")]
    pub syllable_options: Option<SyllableOptions>,
    #[serde(skip_serializing_if="Option::is_none")]
    pub display_logo: Option<bool>,
    /// Total time where subtitles start appearing, before first syllable start playing
    #[serde(skip_serializing_if="Option::is_none")]
    pub transition_time_before: Option<u16>,
    /// Span where subtitles start appearing
    #[serde(skip_serializing_if="Option::is_none")]
    pub fade_time_before: Option<u16>,
    /// Total time where subtitles are disappearing, before first syllable start playing
    #[serde(skip_serializing_if="Option::is_none")]
    pub transition_time_after: Option<u16>,
    /// Span where subtitles start disappearing
    #[serde(skip_serializing_if="Option::is_none")]
    pub fade_time_after: Option<u16>,
    #[serde(skip_serializing_if="Option::is_none")]
    pub row_position: Option<RowPosition>,
}

impl Deref for SentenceOptions {
    type Target = Option<SyllableOptions>;
    fn deref(&self) -> &Option<SyllableOptions> {
        &self.syllable_options
    }
}

#[derive(Debug,Clone)]
pub struct SentenceParameters {
    pub display_logo: bool,
    pub transition_time_before: u16,
    pub fade_time_before: u16,
    pub transition_time_after: u16,
    pub fade_time_after: u16,
    pub row_position: Option<RowPosition>,
}

impl From<SentenceOptions> for SentenceParameters {
    fn from(sentence_options: SentenceOptions) -> Self {
        SentenceParameters {
            display_logo: sentence_options.display_logo.unwrap_or(true),
            transition_time_before: sentence_options.transition_time_before.unwrap_or(800),
            fade_time_before: sentence_options.fade_time_before.unwrap_or(200),
            transition_time_after: sentence_options.transition_time_after.unwrap_or(500),
            fade_time_after: sentence_options.fade_time_after.unwrap_or(200),
            row_position: sentence_options.row_position,
        }
    }
}
<commit_msg>added tests for syllable options<commit_after>use super::{Syllable,SyllableOptions,Subtitles,SubtitlesOptions};
use super::pos::RowPosition;
use std::ops::Deref;

#[derive(Debug,Serialize,Deserialize,Clone)]
pub struct Sentence {
    pub syllables: Vec<Syllable>,
    #[serde(skip_serializing,skip_deserializing)]
    pub position: RowPosition,
    #[serde(skip_serializing_if="Option::is_none")]
    pub sentence_options: Option<SentenceOptions>,
}

pub trait AsSentenceOptions {
    fn as_sentence_options(&self) -> Option<&SentenceOptions>;
    fn or_sentence_options(&self,other:Option<&SentenceOptions>) -> Option<SentenceOptions> {
        match (self.as_sentence_options(),other) {
            (Some(s),Some(other)) => Some(SentenceOptions {
                syllable_options: s.syllable_options.or(other.syllable_options),
                display_logo: s.display_logo.or(other.display_logo),
                transition_time_after: s.transition_time_after.or(other.transition_time_after),
                fade_time_after: s.fade_time_after.or(other.fade_time_after),
                transition_time_before: s.transition_time_before.or(other.transition_time_before),
                fade_time_before: s.fade_time_before.or(other.fade_time_before),
                row_position: s.row_position.or(other.row_position),
            }),
            (Some(s),None) => Some(s.clone()),
            (None,Some(other)) => Some(other.clone()),
            (None,None) => None
        }
    }
}

impl AsSentenceOptions for Sentence {
    fn as_sentence_options(&self) -> Option<&SentenceOptions> {
        self.sentence_options.as_ref()
    }
}

impl AsSentenceOptions for Subtitles {
    fn as_sentence_options(&self) -> Option<&SentenceOptions> {
        self.subtitles_options.sentence_options.as_ref()
    }
}

impl AsSentenceOptions for SentenceOptions {
    fn as_sentence_options(&self) -> Option<&SentenceOptions> {
        Some(self)
    }
}

impl AsSentenceOptions for SubtitlesOptions {
    fn as_sentence_options(&self) -> Option<&SentenceOptions> {
        self.sentence_options.as_ref()
    }
}

impl<T:AsSentenceOptions> AsSentenceOptions for Option<T> {
    fn as_sentence_options(&self) -> Option<&SentenceOptions> {
        match *self {
            Some(ref t) => t.as_sentence_options(),
            None => None
        }
    }
}

impl Sentence {
    /// used mainly for debugging purposes
    #[allow(dead_code)]
    pub fn text(&self) -> String {
        self.syllables.iter().fold(String::new(),|mut string,syllable| {
            string.push_str(&*syllable.text);
            string
        })
    }
}

#[derive(Debug,Clone,Copy,Default,Serialize,Deserialize)]
pub struct SentenceOptions {
    /// Global SyllableOptions
    #[serde(skip_serializing_if="Option::is_none")]
    pub syllable_options: Option<SyllableOptions>,
    #[serde(skip_serializing_if="Option::is_none")]
    pub display_logo: Option<bool>,
    /// Total time where subtitles start appearing, before first syllable start playing
    #[serde(skip_serializing_if="Option::is_none")]
    pub transition_time_before: Option<u16>,
    /// Span where subtitles start appearing
    #[serde(skip_serializing_if="Option::is_none")]
    pub fade_time_before: Option<u16>,
    /// Total time where subtitles are disappearing, before first syllable start playing
    #[serde(skip_serializing_if="Option::is_none")]
    pub transition_time_after: Option<u16>,
    /// Span where subtitles start disappearing
    #[serde(skip_serializing_if="Option::is_none")]
    pub fade_time_after: Option<u16>,
    #[serde(skip_serializing_if="Option::is_none")]
    pub row_position: Option<RowPosition>,
}

impl Deref for SentenceOptions {
    type Target = Option<SyllableOptions>;
    fn deref(&self) -> &Option<SyllableOptions> {
        &self.syllable_options
    }
}

#[derive(Debug,Clone)]
pub struct SentenceParameters {
    pub display_logo: bool,
    pub transition_time_before: u16,
    pub fade_time_before: u16,
    pub transition_time_after: u16,
    pub fade_time_after: u16,
    pub row_position: Option<RowPosition>,
}

impl From<SentenceOptions> for SentenceParameters {
    fn from(sentence_options: SentenceOptions) -> Self {
        SentenceParameters {
            display_logo: sentence_options.display_logo.unwrap_or(true),
            transition_time_before: sentence_options.transition_time_before.unwrap_or(800),
            fade_time_before: sentence_options.fade_time_before.unwrap_or(200),
            transition_time_after: sentence_options.transition_time_after.unwrap_or(500),
            fade_time_after: sentence_options.fade_time_after.unwrap_or(200),
            row_position: sentence_options.row_position,
        }
    }
}

#[test]
fn test_sentence_options_propagation(){
    use super::*;
    use utils::RGB;
    let default_options = SentenceOptions {
        syllable_options: Some(SyllableOptions {
            alive_color: Some(Color::new(128,0,0)),
            dead_color: Some(Color::new(0,128,0)),
            transition_color: None,
            outline: None
        }),
        ..SentenceOptions::default()
    };
    let syllable_options = SyllableOptions {
        alive_color: Some(Color::new(172,172,172)),
        ..SyllableOptions::default()
    };
    let syllable_parameters = SyllableParameters::from(syllable_options.or_syllable_options(default_options.as_syllable_options()).unwrap());
    assert_eq!(syllable_parameters.alive_color,Color::new(172,172,172));
    assert_eq!(syllable_parameters.dead_color,Color::new(0,128,0));
    assert_eq!(syllable_parameters.transition_color,Color::new(255,0,0));
}
<|endoftext|>


<commit_before>//! Wherein are contained functions for checking the terminal's properties: its size, whether cetrain keys are pressed, mouse position, etc.
//!
//! Logical property groups are represented as modules.
//!
//! # Examples
//!
//! ```ignore
//! # // Ignore here, because on CI all `open()` calls will fail, as there's no graphical env. there
//! use bear_lib_terminal::terminal;
//! use bear_lib_terminal::geometry::Size;
//!
//! terminal::open("terminal::state example", 60, 25);
//! assert!(terminal::state::size() == Size::new(60, 25));
//! terminal::close();
//! ```

mod terminal;

pub mod mouse;

use terminal::KeyCode;
use bear_lib_terminal_sys as ffi;

pub use self::terminal::*;

/// Check, whether a [`KeyCode`](../enum.KeyCode.html)-specified key is currently pressed.
pub fn key_pressed(key: KeyCode) -> bool {
	ffi::check(match key {
		KeyCode::A            => ffi::TK_A,
		KeyCode::B            => ffi::TK_B,
		KeyCode::C            => ffi::TK_C,
		KeyCode::D            => ffi::TK_D,
		KeyCode::E            => ffi::TK_E,
		KeyCode::F            => ffi::TK_F,
		KeyCode::G            => ffi::TK_G,
		KeyCode::H            => ffi::TK_H,
		KeyCode::I            => ffi::TK_I,
		KeyCode::J            => ffi::TK_J,
		KeyCode::K            => ffi::TK_K,
		KeyCode::L            => ffi::TK_L,
		KeyCode::M            => ffi::TK_M,
		KeyCode::N            => ffi::TK_N,
		KeyCode::O            => ffi::TK_O,
		KeyCode::P            => ffi::TK_P,
		KeyCode::Q            => ffi::TK_Q,
		KeyCode::R            => ffi::TK_R,
		KeyCode::S            => ffi::TK_S,
		KeyCode::T            => ffi::TK_T,
		KeyCode::U            => ffi::TK_U,
		KeyCode::V            => ffi::TK_V,
		KeyCode::W            => ffi::TK_W,
		KeyCode::X            => ffi::TK_X,
		KeyCode::Y            => ffi::TK_Y,
		KeyCode::Z            => ffi::TK_Z,
		KeyCode::Row1         => ffi::TK_1,
		KeyCode::Row2         => ffi::TK_2,
		KeyCode::Row3         => ffi::TK_3,
		KeyCode::Row4         => ffi::TK_4,
		KeyCode::Row5         => ffi::TK_5,
		KeyCode::Row6         => ffi::TK_6,
		KeyCode::Row7         => ffi::TK_7,
		KeyCode::Row8         => ffi::TK_8,
		KeyCode::Row9         => ffi::TK_9,
		KeyCode::Row0         => ffi::TK_0,
		KeyCode::Enter        => ffi::TK_ENTER,
		KeyCode::Escape       => ffi::TK_ESCAPE,
		KeyCode::Backspace    => ffi::TK_BACKSPACE,
		KeyCode::Tab          => ffi::TK_TAB,
		KeyCode::Space        => ffi::TK_SPACE,
		KeyCode::Minus        => ffi::TK_MINUS,
		KeyCode::Equals       => ffi::TK_EQUALS,
		KeyCode::LeftBracket  => ffi::TK_LBRACKET,
		KeyCode::RightBracket => ffi::TK_RBRACKET,
		KeyCode::Backslash    => ffi::TK_BACKSLASH,
		KeyCode::Semicolon    => ffi::TK_SEMICOLON,
		KeyCode::Apostrophe   => ffi::TK_APOSTROPHE,
		KeyCode::Grave        => ffi::TK_GRAVE,
		KeyCode::Comma        => ffi::TK_COMMA,
		KeyCode::Period       => ffi::TK_PERIOD,
		KeyCode::Slash        => ffi::TK_SLASH,
		KeyCode::F1           => ffi::TK_F1,
		KeyCode::F2           => ffi::TK_F2,
		KeyCode::F3           => ffi::TK_F3,
		KeyCode::F4           => ffi::TK_F4,
		KeyCode::F5           => ffi::TK_F5,
		KeyCode::F6           => ffi::TK_F6,
		KeyCode::F7           => ffi::TK_F7,
		KeyCode::F8           => ffi::TK_F8,
		KeyCode::F9           => ffi::TK_F9,
		KeyCode::F10          => ffi::TK_F10,
		KeyCode::F11          => ffi::TK_F11,
		KeyCode::F12          => ffi::TK_F12,
		KeyCode::Pause        => ffi::TK_PAUSE,
		KeyCode::Insert       => ffi::TK_INSERT,
		KeyCode::Home         => ffi::TK_HOME,
		KeyCode::PageUp       => ffi::TK_PAGEUP,
		KeyCode::Delete       => ffi::TK_DELETE,
		KeyCode::End          => ffi::TK_END,
		KeyCode::PageDown     => ffi::TK_PAGEDOWN,
		KeyCode::Right        => ffi::TK_RIGHT,
		KeyCode::Left         => ffi::TK_LEFT,
		KeyCode::Down         => ffi::TK_DOWN,
		KeyCode::Up           => ffi::TK_UP,
		KeyCode::NumDivide    => ffi::TK_KP_DIVIDE,
		KeyCode::NumMultiply  => ffi::TK_KP_MULTIPLY,
		KeyCode::NumMinus     => ffi::TK_KP_MINUS,
		KeyCode::NumPlus      => ffi::TK_KP_PLUS,
		KeyCode::NumEnter     => ffi::TK_KP_ENTER,
		KeyCode::Num1         => ffi::TK_KP_1,
		KeyCode::Num2         => ffi::TK_KP_2,
		KeyCode::Num3         => ffi::TK_KP_3,
		KeyCode::Num4         => ffi::TK_KP_4,
		KeyCode::Num5         => ffi::TK_KP_5,
		KeyCode::Num6         => ffi::TK_KP_6,
		KeyCode::Num7         => ffi::TK_KP_7,
		KeyCode::Num8         => ffi::TK_KP_8,
		KeyCode::Num9         => ffi::TK_KP_9,
		KeyCode::Num0         => ffi::TK_KP_0,
		KeyCode::NumPeriod    => ffi::TK_KP_PERIOD,
		KeyCode::MouseLeft    => ffi::TK_MOUSE_LEFT,
		KeyCode::MouseRight   => ffi::TK_MOUSE_RIGHT,
		KeyCode::MouseMiddle  => ffi::TK_MOUSE_MIDDLE,
		KeyCode::MouseFourth  => ffi::TK_MOUSE_X1,
		KeyCode::MouseFifth   => ffi::TK_MOUSE_X2,
	})
}
<commit_msg>Remove logical groups notice<commit_after>//! Wherein are contained functions for checking the terminal's properties: its size, whether cetrain keys are pressed, mouse position, etc.
//!
//! # Examples
//!
//! ```ignore
//! # // Ignore here, because on CI all `open()` calls will fail, as there's no graphical env. there
//! use bear_lib_terminal::terminal;
//! use bear_lib_terminal::geometry::Size;
//!
//! terminal::open("terminal::state example", 60, 25);
//! assert!(terminal::state::size() == Size::new(60, 25));
//! terminal::close();
//! ```

mod terminal;

pub mod mouse;

use terminal::KeyCode;
use bear_lib_terminal_sys as ffi;

pub use self::terminal::*;

/// Check, whether a [`KeyCode`](../enum.KeyCode.html)-specified key is currently pressed.
pub fn key_pressed(key: KeyCode) -> bool {
	ffi::check(match key {
		KeyCode::A            => ffi::TK_A,
		KeyCode::B            => ffi::TK_B,
		KeyCode::C            => ffi::TK_C,
		KeyCode::D            => ffi::TK_D,
		KeyCode::E            => ffi::TK_E,
		KeyCode::F            => ffi::TK_F,
		KeyCode::G            => ffi::TK_G,
		KeyCode::H            => ffi::TK_H,
		KeyCode::I            => ffi::TK_I,
		KeyCode::J            => ffi::TK_J,
		KeyCode::K            => ffi::TK_K,
		KeyCode::L            => ffi::TK_L,
		KeyCode::M            => ffi::TK_M,
		KeyCode::N            => ffi::TK_N,
		KeyCode::O            => ffi::TK_O,
		KeyCode::P            => ffi::TK_P,
		KeyCode::Q            => ffi::TK_Q,
		KeyCode::R            => ffi::TK_R,
		KeyCode::S            => ffi::TK_S,
		KeyCode::T            => ffi::TK_T,
		KeyCode::U            => ffi::TK_U,
		KeyCode::V            => ffi::TK_V,
		KeyCode::W            => ffi::TK_W,
		KeyCode::X            => ffi::TK_X,
		KeyCode::Y            => ffi::TK_Y,
		KeyCode::Z            => ffi::TK_Z,
		KeyCode::Row1         => ffi::TK_1,
		KeyCode::Row2         => ffi::TK_2,
		KeyCode::Row3         => ffi::TK_3,
		KeyCode::Row4         => ffi::TK_4,
		KeyCode::Row5         => ffi::TK_5,
		KeyCode::Row6         => ffi::TK_6,
		KeyCode::Row7         => ffi::TK_7,
		KeyCode::Row8         => ffi::TK_8,
		KeyCode::Row9         => ffi::TK_9,
		KeyCode::Row0         => ffi::TK_0,
		KeyCode::Enter        => ffi::TK_ENTER,
		KeyCode::Escape       => ffi::TK_ESCAPE,
		KeyCode::Backspace    => ffi::TK_BACKSPACE,
		KeyCode::Tab          => ffi::TK_TAB,
		KeyCode::Space        => ffi::TK_SPACE,
		KeyCode::Minus        => ffi::TK_MINUS,
		KeyCode::Equals       => ffi::TK_EQUALS,
		KeyCode::LeftBracket  => ffi::TK_LBRACKET,
		KeyCode::RightBracket => ffi::TK_RBRACKET,
		KeyCode::Backslash    => ffi::TK_BACKSLASH,
		KeyCode::Semicolon    => ffi::TK_SEMICOLON,
		KeyCode::Apostrophe   => ffi::TK_APOSTROPHE,
		KeyCode::Grave        => ffi::TK_GRAVE,
		KeyCode::Comma        => ffi::TK_COMMA,
		KeyCode::Period       => ffi::TK_PERIOD,
		KeyCode::Slash        => ffi::TK_SLASH,
		KeyCode::F1           => ffi::TK_F1,
		KeyCode::F2           => ffi::TK_F2,
		KeyCode::F3           => ffi::TK_F3,
		KeyCode::F4           => ffi::TK_F4,
		KeyCode::F5           => ffi::TK_F5,
		KeyCode::F6           => ffi::TK_F6,
		KeyCode::F7           => ffi::TK_F7,
		KeyCode::F8           => ffi::TK_F8,
		KeyCode::F9           => ffi::TK_F9,
		KeyCode::F10          => ffi::TK_F10,
		KeyCode::F11          => ffi::TK_F11,
		KeyCode::F12          => ffi::TK_F12,
		KeyCode::Pause        => ffi::TK_PAUSE,
		KeyCode::Insert       => ffi::TK_INSERT,
		KeyCode::Home         => ffi::TK_HOME,
		KeyCode::PageUp       => ffi::TK_PAGEUP,
		KeyCode::Delete       => ffi::TK_DELETE,
		KeyCode::End          => ffi::TK_END,
		KeyCode::PageDown     => ffi::TK_PAGEDOWN,
		KeyCode::Right        => ffi::TK_RIGHT,
		KeyCode::Left         => ffi::TK_LEFT,
		KeyCode::Down         => ffi::TK_DOWN,
		KeyCode::Up           => ffi::TK_UP,
		KeyCode::NumDivide    => ffi::TK_KP_DIVIDE,
		KeyCode::NumMultiply  => ffi::TK_KP_MULTIPLY,
		KeyCode::NumMinus     => ffi::TK_KP_MINUS,
		KeyCode::NumPlus      => ffi::TK_KP_PLUS,
		KeyCode::NumEnter     => ffi::TK_KP_ENTER,
		KeyCode::Num1         => ffi::TK_KP_1,
		KeyCode::Num2         => ffi::TK_KP_2,
		KeyCode::Num3         => ffi::TK_KP_3,
		KeyCode::Num4         => ffi::TK_KP_4,
		KeyCode::Num5         => ffi::TK_KP_5,
		KeyCode::Num6         => ffi::TK_KP_6,
		KeyCode::Num7         => ffi::TK_KP_7,
		KeyCode::Num8         => ffi::TK_KP_8,
		KeyCode::Num9         => ffi::TK_KP_9,
		KeyCode::Num0         => ffi::TK_KP_0,
		KeyCode::NumPeriod    => ffi::TK_KP_PERIOD,
		KeyCode::MouseLeft    => ffi::TK_MOUSE_LEFT,
		KeyCode::MouseRight   => ffi::TK_MOUSE_RIGHT,
		KeyCode::MouseMiddle  => ffi::TK_MOUSE_MIDDLE,
		KeyCode::MouseFourth  => ffi::TK_MOUSE_X1,
		KeyCode::MouseFifth   => ffi::TK_MOUSE_X2,
	})
}
<|endoftext|>


<commit_before>#![feature(test)]

#[macro_use]
extern crate lazy_static;
extern crate test;

use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::{Path, PathBuf};
use test::Bencher;

extern crate hyphenation;
use hyphenation::{load, Corpus, Hyphenation, Language};
use hyphenation::exception::{Exceptions};
use hyphenation::pattern::{Patterns};

fn fiat_io(lang: Language) -> Corpus {
    let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
    path.push("patterns");

    hyphenation::set_pattern_folder(path.as_path());
    load::language(lang).unwrap()
}

lazy_static! {
    static ref DATAPATH: PathBuf = {
        let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
        path.push("patterns");

        path
    };

    static ref EN_US: Corpus = fiat_io(Language::English_US);

    static ref WORDS: Vec<String> = {
        let file = File::open(Path::new("/usr/share/dict/words")).unwrap();
        let words: Vec<_> = BufReader::new(file).lines().map(|l| l.unwrap()).collect();

        words
    };
}

#[bench]
fn parse_patterns_en_us(b: &mut Bencher) {
    hyphenation::set_pattern_folder(DATAPATH.as_path());
    let by_line = load::patterns(Language::English_US).unwrap();
    let v: Vec<_> = by_line.collect();

    let mut ps = Patterns::empty();
    b.iter(|| {
        for p in &v {
            for val in p { ps.insert(&*val) };
        }
    });
}

#[bench]
fn parse_exceptions_en_us(b: &mut Bencher) {
    hyphenation::set_pattern_folder(DATAPATH.as_path());
    let by_line = load::exceptions(Language::English_US).unwrap();
    let v: Vec<_> = by_line.collect();

    let mut exs = Exceptions::empty();
    b.iter(|| {
        for ex in &v {
            for val in ex { exs.insert(&*val) };
        }
    });
}

#[bench]
fn opportunities_words(b: &mut Bencher) {
    hyphenation::set_pattern_folder(DATAPATH.as_path());
    let mut ws = WORDS.iter();

    b.iter(|| {
        for w in ws.by_ref() {
            w.opportunities(&EN_US);
        }
    })
}
<commit_msg>Benchmark hyphenation<commit_after>#![feature(test)]

#[macro_use]
extern crate lazy_static;
extern crate test;

use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::{Path, PathBuf};
use test::Bencher;

extern crate hyphenation;
use hyphenation::{load, Corpus, Hyphenation, Language};
use hyphenation::exception::{Exceptions};
use hyphenation::pattern::{Patterns};

fn fiat_io(lang: Language) -> Corpus {
    let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
    path.push("patterns");

    hyphenation::set_pattern_folder(path.as_path());
    load::language(lang).unwrap()
}

lazy_static! {
    static ref DATAPATH: PathBuf = {
        let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
        path.push("patterns");

        path
    };

    static ref EN_US: Corpus = fiat_io(Language::English_US);

    static ref WORDS: Vec<String> = {
        let file = File::open(Path::new("/usr/share/dict/words")).unwrap();
        let words: Vec<_> = BufReader::new(file).lines().map(|l| l.unwrap()).collect();

        words
    };
}

#[bench]
fn parse_patterns_en_us(b: &mut Bencher) {
    hyphenation::set_pattern_folder(DATAPATH.as_path());
    let by_line = load::patterns(Language::English_US).unwrap();
    let v: Vec<_> = by_line.collect();

    let mut ps = Patterns::empty();
    b.iter(|| {
        for p in &v {
            for val in p { ps.insert(&*val) };
        }
    });
}

#[bench]
fn parse_exceptions_en_us(b: &mut Bencher) {
    hyphenation::set_pattern_folder(DATAPATH.as_path());
    let by_line = load::exceptions(Language::English_US).unwrap();
    let v: Vec<_> = by_line.collect();

    let mut exs = Exceptions::empty();
    b.iter(|| {
        for ex in &v {
            for val in ex { exs.insert(&*val) };
        }
    });
}

#[bench]
fn opportunities_words(b: &mut Bencher) {
    hyphenation::set_pattern_folder(DATAPATH.as_path());
    let mut ws = WORDS.iter();

    b.iter(|| {
        for w in ws.by_ref() {
            w.opportunities(&EN_US);
        }
    })
}

#[bench]
fn hyphenate_words(b: &mut Bencher) {
    hyphenation::set_pattern_folder(DATAPATH.as_path());
    let mut ws = WORDS.iter();

    b.iter(|| {
        for w in ws.by_ref() {
            w.hyphenate(&EN_US).count();
        }
    })
}
<|endoftext|>


<commit_before>use ahash::AHashMap;
use jwalk::WalkDir;
use serde::Deserialize;
use std::io::ErrorKind;
use std::os::unix::fs::MetadataExt;
use std::os::unix::fs::PermissionsExt;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use std::time::Instant;
use structopt::StructOpt;
use watchman_client::prelude::*;

#[derive(StructOpt, Debug)]
#[structopt(about = "Audit Watchman's in-memory database with the filesystem")]
pub(crate) struct AuditCmd {
    #[structopt(name = "path", parse(from_os_str))]
    path: PathBuf,
}

query_result_type! {
    struct AuditQueryResult {
        name: NameField,
        mode: ModeAndPermissionsField,
        size: SizeField,
        mtime: MTimeField,
        oclock: ObservedClockField,
        ino: InodeNumberField,
    }
}

// Skip cookies when diffing.
fn is_cookie<T: AsRef<Path>>(name: T) -> bool {
    return name
        .as_ref()
        .file_name()
        .and_then(|s| s.to_str())
        .map_or(false, |s| s.starts_with(".watchman-cookie-"));
}

impl AuditCmd {
    pub(crate) async fn run(&self) -> crate::Result<()> {
        let client = Connector::new().connect().await?;
        let resolved = Arc::new(
            client
                .resolve_root(CanonicalPath::canonicalize(&self.path)?)
                .await?,
        );

        if resolved.watcher() == "eden" {
            return Err(watchman_client::Error::Generic(format!(
                "{} is an EdenFS mount - no need to audit",
                resolved.project_root().display()
            )));
        }

        let config = client.get_config(&resolved).await?;
        let mut ignore_dirs = config.ignore_dirs.unwrap_or_default().clone();

        // TODO: This list is duplicated in the Watchman query below.
        ignore_dirs.push(".hg".into());
        ignore_dirs.push(".git".into());
        ignore_dirs.push(".svn".into());

        let filesystem_state_handle = {
            let resolved = resolved.clone();
            tokio::spawn(async move {
                let mut filesystem_state: AHashMap<PathBuf, std::fs::Metadata> = AHashMap::new();

                let start_crawl = Instant::now();

                // Allocate outside the loop to save time.
                let resolved_path = Arc::new(resolved.path());

                let resolved_path_copy = resolved_path.clone();
                let walk_dir = WalkDir::new(&*resolved_path)
                    .skip_hidden(false)
                    .process_read_dir(move |_depth, path, _read_dir_state, children| {
                        let resolved_path: &Path = resolved_path_copy.as_ref();
                        let from_root = match path.strip_prefix(resolved_path) {
                            Ok(from_root) => from_root,
                            Err(_) => {
                                return;
                            }
                        };

                        if from_root == Path::new("") {
                            children.retain(|child| match child {
                                Ok(child) => {
                                    ignore_dirs.iter().all(|i| i.as_os_str() != child.file_name)
                                }
                                Err(_) => true,
                            });
                        }
                    });

                for entry in walk_dir {
                    let entry = match entry {
                        Ok(entry) => entry,
                        Err(err) => {
                            eprintln!("error while traversing directory: {}", err);
                            continue;
                        }
                    };

                    let entry_path = entry.path();
                    let relpath = match entry_path.strip_prefix(&*resolved_path) {
                        Ok(relpath) => relpath,
                        Err(err) => {
                            eprintln!(
                                "unable to form relative path from {} to {}: {}",
                                resolved_path.display(),
                                entry_path.display(),
                                err
                            );
                            continue;
                        }
                    };

                    let metadata = match entry.metadata() {
                        Ok(metadata) => metadata,
                        Err(err) => {
                            if err.io_error().map(|e| e.kind() == ErrorKind::NotFound) != Some(true)
                            {
                                eprintln!(
                                    "error fetching metadata for {}: {}",
                                    entry_path.display(),
                                    err
                                );
                            }
                            continue;
                        }
                    };
                    filesystem_state.insert(relpath.to_path_buf(), metadata);
                }
                // Watchman doesn't return information about the root, so remove it here.
                filesystem_state.remove(&PathBuf::new());
                eprintln!("Crawled filesystem in {:?}", start_crawl.elapsed());

                filesystem_state
            })
        };

        let start_query = Instant::now();

        // Do not ignore fresh instance results: the goal is to validate the
        // correctness of query results against the filesystem state, no
        // matter what happened in the crawler.

        use Expr::*;
        let result = client
            .query::<AuditQueryResult>(
                &resolved,
                QueryRequestCommon {
                    expression: Some(All(vec![
                        Exists,
                        Not(Box::new(Any(vec![
                            DirName(DirNameTerm {
                                path: ".git".into(),
                                depth: None,
                            }),
                            DirName(DirNameTerm {
                                path: ".hg".into(),
                                depth: None,
                            }),
                            DirName(DirNameTerm {
                                path: ".svn".into(),
                                depth: None,
                            }),
                            Name(NameTerm {
                                paths: vec![".git".into()],
                                wholename: true,
                            }),
                            Name(NameTerm {
                                paths: vec![".svn".into()],
                                wholename: true,
                            }),
                            Name(NameTerm {
                                paths: vec![".hg".into()],
                                wholename: true,
                            }),
                        ]))),
                    ])),
                    ..Default::default()
                },
            )
            .await?;

        eprintln!(
            "Queried Watchman in {:?} (is_fresh_instance = {}, clock = {:?})",
            start_query.elapsed(),
            result.is_fresh_instance,
            result.clock
        );

        let filesystem_state = filesystem_state_handle.await.unwrap();

        let diff_start = Instant::now();

        let watchman_files = match result.files {
            Some(files) => files,
            None => {
                return Err(watchman_client::Error::Generic(
                    "No files set in result {}".into(),
                ));
            }
        };

        let mut any_differences = false;
        let mut phantoms = vec![];
        let mut missing = vec![];

        let mut watchman_state: AHashMap<&Path, &AuditQueryResult> =
            AHashMap::with_capacity(watchman_files.len());
        for watchman_file in &watchman_files {
            let filename = &*watchman_file.name;
            // Skip cookies when diffing.
            if is_cookie(filename) {
                continue;
            }
            watchman_state.insert(filename, watchman_file);

            let metadata = match filesystem_state.get(filename) {
                Some(metadata) => metadata,
                None => {
                    phantoms.push(watchman_file);
                    continue;
                }
            };

            let mut diffs = Vec::new();

            if *watchman_file.mode != u64::from(metadata.permissions().mode()) {
                diffs.push(format!(
                    "watchman mode is {} vs. fs {}",
                    *watchman_file.mode,
                    metadata.permissions().mode()
                ));
            }

            if metadata.is_file() && *watchman_file.size != metadata.size() {
                diffs.push(format!(
                    "watchman size is {} vs. fs {}",
                    *watchman_file.size,
                    metadata.len()
                ));
            }

            if metadata.is_file() && *watchman_file.mtime != metadata.mtime() {
                diffs.push(format!(
                    "watchman mtime is {} vs. fs {}",
                    *watchman_file.mtime,
                    metadata.mtime()
                ));
            }

            if *watchman_file.ino != metadata.ino() {
                diffs.push(format!(
                    "watchman ino is {} vs. fs {}",
                    *watchman_file.ino,
                    metadata.ino()
                ));
            }

            if !diffs.is_empty() {
                println!(
                    "Conflicting information for {}:",
                    watchman_file.name.display()
                );
                for diff in diffs {
                    println!("  {}", diff);
                }
                println!("  oclock is {:?}", *watchman_file.oclock);
                any_differences = true;
            }
        }

        for (path, val) in &filesystem_state {
            if is_cookie(path) {
                continue;
            }
            if !watchman_state.contains_key(&path.as_path()) {
                missing.push((path, val));
            }
        }

        phantoms.sort_by(|x, y| x.name.cmp(&y.name));
        missing.sort_by(|x, y| x.0.cmp(&y.0));

        if !phantoms.is_empty() {
            println!(
                "There are {} items reported by watchman not on the filesystem:",
                phantoms.len()
            );
            for phantom in &phantoms {
                println!("  {}", phantom.name.display());
            }
            any_differences = true;
        }

        if !missing.is_empty() {
            println!(
                "There are {} items on the filesystem not reported by watchman:",
                missing.len()
            );
            for (path, _) in &missing {
                println!("  {}", path.display());
            }
            any_differences = true;
        }

        if any_differences {
            // This is dumb, but Rust doesn't have a standard way to return
            // nonzero exit codes yet.
            std::process::exit(1);
        }

        eprintln!("Diffed in {:#?}", diff_start.elapsed());

        Ok(())
    }
}
<commit_msg>increase audit query timeout to two minutes<commit_after>use ahash::AHashMap;
use jwalk::WalkDir;
use serde::Deserialize;
use std::io::ErrorKind;
use std::os::unix::fs::MetadataExt;
use std::os::unix::fs::PermissionsExt;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use std::time::{Duration, Instant};
use structopt::StructOpt;
use watchman_client::prelude::*;

#[derive(StructOpt, Debug)]
#[structopt(about = "Audit Watchman's in-memory database with the filesystem")]
pub(crate) struct AuditCmd {
    #[structopt(name = "path", parse(from_os_str))]
    path: PathBuf,

    #[structopt(
        long = "timeout",
        about = "seconds to wait for Watchman query result",
        default_value = "120"
    )]
    timeout_secs: u64,
}

query_result_type! {
    struct AuditQueryResult {
        name: NameField,
        mode: ModeAndPermissionsField,
        size: SizeField,
        mtime: MTimeField,
        oclock: ObservedClockField,
        ino: InodeNumberField,
    }
}

// Skip cookies when diffing.
fn is_cookie<T: AsRef<Path>>(name: T) -> bool {
    return name
        .as_ref()
        .file_name()
        .and_then(|s| s.to_str())
        .map_or(false, |s| s.starts_with(".watchman-cookie-"));
}

impl AuditCmd {
    pub(crate) async fn run(&self) -> crate::Result<()> {
        let client = Connector::new().connect().await?;
        let resolved = Arc::new(
            client
                .resolve_root(CanonicalPath::canonicalize(&self.path)?)
                .await?,
        );

        if resolved.watcher() == "eden" {
            return Err(watchman_client::Error::Generic(format!(
                "{} is an EdenFS mount - no need to audit",
                resolved.project_root().display()
            )));
        }

        let config = client.get_config(&resolved).await?;
        let mut ignore_dirs = config.ignore_dirs.unwrap_or_default().clone();

        // TODO: This list is duplicated in the Watchman query below.
        ignore_dirs.push(".hg".into());
        ignore_dirs.push(".git".into());
        ignore_dirs.push(".svn".into());

        let filesystem_state_handle = {
            let resolved = resolved.clone();
            tokio::spawn(async move {
                let mut filesystem_state: AHashMap<PathBuf, std::fs::Metadata> = AHashMap::new();

                let start_crawl = Instant::now();

                // Allocate outside the loop to save time.
                let resolved_path = Arc::new(resolved.path());

                let resolved_path_copy = resolved_path.clone();
                let walk_dir = WalkDir::new(&*resolved_path)
                    .skip_hidden(false)
                    .process_read_dir(move |_depth, path, _read_dir_state, children| {
                        let resolved_path: &Path = resolved_path_copy.as_ref();
                        let from_root = match path.strip_prefix(resolved_path) {
                            Ok(from_root) => from_root,
                            Err(_) => {
                                return;
                            }
                        };

                        if from_root == Path::new("") {
                            children.retain(|child| match child {
                                Ok(child) => {
                                    ignore_dirs.iter().all(|i| i.as_os_str() != child.file_name)
                                }
                                Err(_) => true,
                            });
                        }
                    });

                for entry in walk_dir {
                    let entry = match entry {
                        Ok(entry) => entry,
                        Err(err) => {
                            eprintln!("error while traversing directory: {}", err);
                            continue;
                        }
                    };

                    let entry_path = entry.path();
                    let relpath = match entry_path.strip_prefix(&*resolved_path) {
                        Ok(relpath) => relpath,
                        Err(err) => {
                            eprintln!(
                                "unable to form relative path from {} to {}: {}",
                                resolved_path.display(),
                                entry_path.display(),
                                err
                            );
                            continue;
                        }
                    };

                    let metadata = match entry.metadata() {
                        Ok(metadata) => metadata,
                        Err(err) => {
                            if err.io_error().map(|e| e.kind() == ErrorKind::NotFound) != Some(true)
                            {
                                eprintln!(
                                    "error fetching metadata for {}: {}",
                                    entry_path.display(),
                                    err
                                );
                            }
                            continue;
                        }
                    };
                    filesystem_state.insert(relpath.to_path_buf(), metadata);
                }
                // Watchman doesn't return information about the root, so remove it here.
                filesystem_state.remove(&PathBuf::new());
                eprintln!("Crawled filesystem in {:?}", start_crawl.elapsed());

                filesystem_state
            })
        };

        let start_query = Instant::now();

        // Do not ignore fresh instance results: the goal is to validate the
        // correctness of query results against the filesystem state, no
        // matter what happened in the crawler.

        use Expr::*;
        let result = client
            .query::<AuditQueryResult>(
                &resolved,
                QueryRequestCommon {
                    expression: Some(All(vec![
                        Exists,
                        Not(Box::new(Any(vec![
                            DirName(DirNameTerm {
                                path: ".git".into(),
                                depth: None,
                            }),
                            DirName(DirNameTerm {
                                path: ".hg".into(),
                                depth: None,
                            }),
                            DirName(DirNameTerm {
                                path: ".svn".into(),
                                depth: None,
                            }),
                            Name(NameTerm {
                                paths: vec![".git".into()],
                                wholename: true,
                            }),
                            Name(NameTerm {
                                paths: vec![".svn".into()],
                                wholename: true,
                            }),
                            Name(NameTerm {
                                paths: vec![".hg".into()],
                                wholename: true,
                            }),
                        ]))),
                    ])),
                    sync_timeout: SyncTimeout::Duration(Duration::new(self.timeout_secs, 0)),
                    ..Default::default()
                },
            )
            .await;

        let result = match result {
            Ok(result) => result,
            Err(err) => {
                eprintln!("Error during Watchman query: {}", err);
                // Use a different error code for Watchman query errors, including timeouts, so they can be differentiated by audit logging.
                std::process::exit(2);
            }
        };

        eprintln!(
            "Queried Watchman in {:?} (is_fresh_instance = {}, clock = {:?})",
            start_query.elapsed(),
            result.is_fresh_instance,
            result.clock
        );

        let filesystem_state = filesystem_state_handle.await.unwrap();

        let diff_start = Instant::now();

        let watchman_files = match result.files {
            Some(files) => files,
            None => {
                return Err(watchman_client::Error::Generic(
                    "No files set in result {}".into(),
                ));
            }
        };

        let mut any_differences = false;
        let mut phantoms = vec![];
        let mut missing = vec![];

        let mut watchman_state: AHashMap<&Path, &AuditQueryResult> =
            AHashMap::with_capacity(watchman_files.len());
        for watchman_file in &watchman_files {
            let filename = &*watchman_file.name;
            // Skip cookies when diffing.
            if is_cookie(filename) {
                continue;
            }
            watchman_state.insert(filename, watchman_file);

            let metadata = match filesystem_state.get(filename) {
                Some(metadata) => metadata,
                None => {
                    phantoms.push(watchman_file);
                    continue;
                }
            };

            let mut diffs = Vec::new();

            if *watchman_file.mode != u64::from(metadata.permissions().mode()) {
                diffs.push(format!(
                    "watchman mode is {} vs. fs {}",
                    *watchman_file.mode,
                    metadata.permissions().mode()
                ));
            }

            if metadata.is_file() && *watchman_file.size != metadata.size() {
                diffs.push(format!(
                    "watchman size is {} vs. fs {}",
                    *watchman_file.size,
                    metadata.len()
                ));
            }

            if metadata.is_file() && *watchman_file.mtime != metadata.mtime() {
                diffs.push(format!(
                    "watchman mtime is {} vs. fs {}",
                    *watchman_file.mtime,
                    metadata.mtime()
                ));
            }

            if *watchman_file.ino != metadata.ino() {
                diffs.push(format!(
                    "watchman ino is {} vs. fs {}",
                    *watchman_file.ino,
                    metadata.ino()
                ));
            }

            if !diffs.is_empty() {
                println!(
                    "Conflicting information for {}:",
                    watchman_file.name.display()
                );
                for diff in diffs {
                    println!("  {}", diff);
                }
                println!("  oclock is {:?}", *watchman_file.oclock);
                any_differences = true;
            }
        }

        for (path, val) in &filesystem_state {
            if is_cookie(path) {
                continue;
            }
            if !watchman_state.contains_key(&path.as_path()) {
                missing.push((path, val));
            }
        }

        phantoms.sort_by(|x, y| x.name.cmp(&y.name));
        missing.sort_by(|x, y| x.0.cmp(&y.0));

        if !phantoms.is_empty() {
            println!(
                "There are {} items reported by watchman not on the filesystem:",
                phantoms.len()
            );
            for phantom in &phantoms {
                println!("  {}", phantom.name.display());
            }
            any_differences = true;
        }

        if !missing.is_empty() {
            println!(
                "There are {} items on the filesystem not reported by watchman:",
                missing.len()
            );
            for (path, _) in &missing {
                println!("  {}", path.display());
            }
            any_differences = true;
        }

        if any_differences {
            // This is dumb, but Rust doesn't have a standard way to return
            // nonzero exit codes yet.
            std::process::exit(1);
        }

        eprintln!("Diffed in {:#?}", diff_start.elapsed());

        Ok(())
    }
}
<|endoftext|>


<commit_before>extern crate lifeguard;

#[cfg(test)]
mod tests {
  use lifeguard::{Pool, Recycled};

  #[test]
  fn test_deref() {
      let mut str_pool : Pool<String> = Pool::with_size(1);
      let rstring = str_pool.new_from("cat");
      assert_eq!("cat", *rstring);
  }

  #[test]
  fn test_deref_mut() {
      let mut str_pool : Pool<String> = Pool::with_size(1);
      let mut rstring = str_pool.new_from("cat");
      (*rstring).push_str("s love eating mice");
      println!("{}", *rstring);
      assert_eq!("cats love eating mice", *rstring);
  }

  #[test]
  fn test_as_mut() {
      let mut str_pool : Pool<String> = Pool::with_size(1);
      let mut rstring = str_pool.new_from("cat");
      rstring.as_mut().push_str("s love eating mice");
      assert_eq!("cats love eating mice", *rstring);
  }

  #[test]
  fn test_as_ref() {
      let mut str_pool : Pool<String> = Pool::with_size(1);
      let rstring = str_pool.new_from("cat");
      assert_eq!("cat", rstring.as_ref());
  }

  #[test]
  fn test_recycle() {
      let mut str_pool : Pool<String> = Pool::with_size(1);
      {
        assert_eq!(1, str_pool.size());
        let _rstring = str_pool.new_from("cat");
        assert_eq!(0, str_pool.size());
      }
      assert_eq!(1, str_pool.size());
  }

  #[test]
  fn test_detached() {
      let mut str_pool : Pool<String> = Pool::with_size(1);
      {
        assert_eq!(1, str_pool.size());
        let _string : String = str_pool.new().detach();
        assert_eq!(0, str_pool.size());
      }
      assert_eq!(0, str_pool.size());
  }

  #[test]
  fn test_attach() {
      let mut str_pool : Pool<String> = Pool::with_size(1);
      {
        assert_eq!(1, str_pool.size());
        let string: String = str_pool.new().detach();
        assert_eq!(0, str_pool.size());
        let _rstring: Recycled<String> = str_pool.attach(string);
      }
      assert_eq!(1, str_pool.size());
  }
}
<commit_msg>Manual deref is unnecessary<commit_after>extern crate lifeguard;

#[cfg(test)]
mod tests {
  use lifeguard::{Pool, Recycled};

  #[test]
  fn test_deref() {
      let mut str_pool : Pool<String> = Pool::with_size(1);
      let rstring = str_pool.new_from("cat");
      assert_eq!("cat", *rstring);
  }

  #[test]
  fn test_deref_mut() {
      let mut str_pool : Pool<String> = Pool::with_size(1);
      let mut rstring = str_pool.new_from("cat");
      rstring.push_str("s love eating mice");
      println!("{}", *rstring);
      assert_eq!("cats love eating mice", *rstring);
  }

  #[test]
  fn test_as_mut() {
      let mut str_pool : Pool<String> = Pool::with_size(1);
      let mut rstring = str_pool.new_from("cat");
      rstring.as_mut().push_str("s love eating mice");
      assert_eq!("cats love eating mice", *rstring);
  }

  #[test]
  fn test_as_ref() {
      let mut str_pool : Pool<String> = Pool::with_size(1);
      let rstring = str_pool.new_from("cat");
      assert_eq!("cat", rstring.as_ref());
  }

  #[test]
  fn test_recycle() {
      let mut str_pool : Pool<String> = Pool::with_size(1);
      {
        assert_eq!(1, str_pool.size());
        let _rstring = str_pool.new_from("cat");
        assert_eq!(0, str_pool.size());
      }
      assert_eq!(1, str_pool.size());
  }

  #[test]
  fn test_detached() {
      let mut str_pool : Pool<String> = Pool::with_size(1);
      {
        assert_eq!(1, str_pool.size());
        let _string : String = str_pool.new().detach();
        assert_eq!(0, str_pool.size());
      }
      assert_eq!(0, str_pool.size());
  }

  #[test]
  fn test_attach() {
      let mut str_pool : Pool<String> = Pool::with_size(1);
      {
        assert_eq!(1, str_pool.size());
        let string: String = str_pool.new().detach();
        assert_eq!(0, str_pool.size());
        let _rstring: Recycled<String> = str_pool.attach(string);
      }
      assert_eq!(1, str_pool.size());
  }
}
<|endoftext|>


<commit_before>extern crate presort;

use presort::PermutedVec;
use std::rc::Rc;
use std::cell::RefCell;

struct TreeNode<T> {
	//whether this node's data has changed
	dirty_val: bool,
	//whether some child node's data has changed
	dirty_child: bool,
	//number of children with unchanged decendent counts
	stable_children: usize,
	//parent and index in parent's child vec
	parent: Option<(Tree<T>, usize)>,
	//this node's data
    data: T,
    decendent_count: i32,
    children: Vec<Tree<T>>,
}

type Tree<T> = Rc<RefCell<TreeNode<T>>>;

trait IncTree<T: PartialEq + Clone> {
    fn new_node(data: T) -> Tree<T>;
	fn dirty_decendents(&self);
	fn dirty_parents(&self);
	fn mark_recount(&self, child_index: usize, new_nodes: i32);
	fn get_data(&self) -> T;
	fn set_data(&self, data: T);
	fn get_child(&self, child_num: usize) -> Tree<T>;
	fn set_child(&self, child_num: usize, child: Tree<T>);
	fn push_child(&self, child: Tree<T>);
}

impl<T: PartialEq + Clone> IncTree<T> for Tree<T> {
	fn new_node(data: T) -> Tree<T>{
		Rc::new(RefCell::new(TreeNode {
			dirty_val: true,
			dirty_child: false,
			stable_children: 0,
			parent: None,
			data: data,
			decendent_count: 0,
			children: vec![]
		}))
	}

	//sets dirty flags, but leaves counts
	fn dirty_decendents(&self) {
		let mut tree = self.borrow_mut();
		tree.dirty_val = true;
		tree.dirty_child = true;
		for kid in &tree.children {
			kid.dirty_decendents();
		}
	}

	fn dirty_parents(&self){
		let mut parent = None;
		if let Some((ref p_tree,_)) = self.borrow().parent {
			parent = Some(p_tree.clone()); // Rc clone
		}
		if let Some(parent) = parent {
			parent.borrow_mut().dirty_child = true;
			parent.dirty_parents();
		}
	}

	//when a child changes number of decendents, we mark it in
	//this tree and bubble it up. This updates both
	//`decendent_count` and `stable_children`
	fn mark_recount(&self, child_index: usize, new_nodes: i32) {
		let mut tree = self.borrow_mut();
		tree.decendent_count += new_nodes;
		if tree.stable_children > child_index {
			tree.stable_children = child_index;
		}
		if let Some((ref parent, index)) = tree.parent {
			parent.mark_recount(index, new_nodes)
		}
	}

	fn get_data(&self) -> T {
		self.borrow().data.clone()
	}

	fn set_data(&self, data: T) {
		{
			let mut tree = self.borrow_mut();
			if tree.data == data {return}
			tree.data = data;
			tree.dirty_val = true;
		}
		self.dirty_parents();
	}

	fn get_child(&self, child_num: usize) -> Tree<T> {
		self.borrow().children[child_num].clone() //Rc clone
	}

	fn set_child(&self, child_num: usize, child: Tree<T>) {
		let mut tree = self.borrow_mut();
		//assume all new values are dirty
		child.dirty_decendents();
		child.borrow_mut().parent = Some((self.clone(), child_num)); //Rc clone
		child.dirty_parents();
		let mut nodes: i32 = 0;
		{
			//handle a change in the number of decendents
			let ref old_child = tree.children[child_num].borrow();
			if old_child.decendent_count != child.borrow().decendent_count {
				nodes -= old_child.decendent_count;
				nodes += child.borrow().decendent_count;
				child.borrow_mut().stable_children = 0;
				self.mark_recount(child_num, nodes);
			}
		}
		tree.children[child_num] = child;
	}

	fn push_child(&self, child: Tree<T>) {
		//assume all new values are dirty
		child.dirty_decendents();
		child.borrow_mut().parent = Some((self.clone(), self.borrow().children.len())); //Rc clone
		child.dirty_parents();
		let index;
		let nodes;
		{
			let mut tree = self.borrow_mut();
			nodes = child.borrow().decendent_count + 1;
			index = tree.children.len();
			tree.children.push(child);
		}
		self.mark_recount(index, nodes);
	}
}

fn dump<T: Clone>(tree: &Tree<T>, vec: &mut PermutedVec<T>) {
	let mut tree = tree.borrow_mut();
    vec.push(tree.data.clone());
    tree.dirty_val = false;
    for kid in &tree.children { dump(&kid, vec); }
    tree.dirty_child = false;
    tree.stable_children = tree.children.len();
}

fn update<T: Clone>(tree: &Tree<T>, start_index: usize, vec: &mut PermutedVec<T>) {
	let mut tree = tree.borrow_mut();
	let mut index = start_index;
	if tree.dirty_val {
		if vec.len() <= index {
			vec.push(tree.data.clone())
		} else {
			vec.set(index, tree.data.clone());
		}
		tree.dirty_val = false;
	}
	if !tree.dirty_child {return}
	index += 1;

	//all the children with unchanged decendent counts
	for counted_kid in 0..tree.stable_children{
		update(&tree.children[counted_kid], index, vec);
		index += 1 + tree.children[counted_kid].borrow_mut().decendent_count as usize;
	}
	if tree.stable_children < tree.children.len() {
		{
			//first changed child may have unchanged decendents
			let first_changed = &tree.children[tree.stable_children];
			update(&first_changed, index, vec);
			index += 1 + first_changed.borrow().decendent_count as usize;
		}
		//dump any additional children
		if (tree.stable_children + 1) < tree.children.len() {
			vec.truncate(index);
			for uncounted_kid in (tree.stable_children + 1)..tree.children.len() {
				dump(&tree.children[uncounted_kid], vec);
			}
			tree.stable_children = tree.children.len();
		}
	}
}

#[test]
fn test_tree() {
    let main_tree = Tree::new_node(37);
    let mut vec = PermutedVec::new();
    dump(&main_tree, &mut vec);
    assert_eq!(vec.sorted_iter().collect::<Vec<&usize>>(), vec![&37]);
}

#[test]
fn test_update() {
	println!("start first dump");
	let main_tree = Tree::new_node(37);
	main_tree.push_child(Tree::new_node(42));
	main_tree.push_child(Tree::new_node(20));
	main_tree.push_child(Tree::new_node(63));
	let mut vec = PermutedVec::new();
	dump(&main_tree, &mut vec);
	println!("finished first dump");
    assert_eq!(vec.sorted_iter().collect::<Vec<&usize>>(), vec![&20,&37,&42,&63]);

	println!("start first update");
    main_tree.get_child(1).set_data(25);
    update(&main_tree, 0, &mut vec);
	println!("finished first update");
    assert_eq!(vec.sorted_iter().collect::<Vec<&usize>>(), vec![&25,&37,&42,&63]);

	println!("start add branch");
    let branch = main_tree.get_child(2);
    branch.push_child(Tree::new_node(47));
    branch.push_child(Tree::new_node(53));
    branch.push_child(Tree::new_node(61));
    branch.push_child(Tree::new_node(57));
    update(&main_tree, 0, &mut vec);
	println!("finished add branch");
    assert_eq!(
    	vec.sorted_iter().collect::<Vec<&usize>>(),
    	vec![&25,&37,&42,&47,&53,&57,&61,&63]
    );

	println!("start update branch");
    branch.get_child(2).set_data(77);
    update(&main_tree, 0, &mut vec);
	println!("finished update branch");
    assert_eq!(
    	vec.sorted_iter().collect::<Vec<&usize>>(),
    	vec![&25,&37,&42,&47,&53,&57,&63,&77]
    );

    println!("start early insertion");
    let branch = main_tree.get_child(1);
    branch.push_child(Tree::new_node(1));
    update(&main_tree, 0, &mut vec);
    println!("finished early insertion");
    assert_eq!(
    	vec.sorted_iter().collect::<Vec<&usize>>(),
    	vec![&1,&25,&37,&42,&47,&53,&57,&63,&77]
    );

    println!("start deep add");
    let branch = main_tree.get_child(2).get_child(2);
    let new_branch = Tree::new_node(100);
    new_branch.push_child(Tree::new_node(101));
    branch.push_child(new_branch);
    update(&main_tree, 0, &mut vec);
    println!("finished deep add");
    assert_eq!(
    	vec.sorted_iter().collect::<Vec<&usize>>(),
    	vec![&1,&25,&37,&42,&47,&53,&57,&63,&77,&100,&101]
    );

    println!("start deep edit");
    let branch = branch.get_child(0).get_child(0);
    branch.set_data(105);
    update(&main_tree, 0, &mut vec);
    println!("finished deep edit");
    assert_eq!(
    	vec.sorted_iter().collect::<Vec<&usize>>(),
    	vec![&1,&25,&37,&42,&47,&53,&57,&63,&77,&100,&105]
    );

}
<commit_msg>add pop_child<commit_after>extern crate presort;

use presort::PermutedVec;
use std::rc::Rc;
use std::cell::RefCell;

struct TreeNode<T> {
	//whether this node's data has changed
	dirty_val: bool,
	//whether some child node's data has changed
	dirty_child: bool,
	//number of children with unchanged decendent counts
	stable_children: usize,
	//parent and index in parent's child vec
	parent: Option<(Tree<T>, usize)>,
	//this node's data
    data: T,
    decendent_count: i32,
    children: Vec<Tree<T>>,
}

type Tree<T> = Rc<RefCell<TreeNode<T>>>;

trait IncTree<T: PartialEq + Clone> {
    fn new_node(data: T) -> Tree<T>;
	fn dirty_decendents(&self);
	fn dirty_parents(&self);
	fn mark_recount(&self, child_index: usize, new_nodes: i32);
	fn get_data(&self) -> T;
	fn set_data(&self, data: T);
	fn get_child(&self, child_num: usize) -> Tree<T>;
	fn set_child(&self, child_num: usize, child: Tree<T>);
	fn push_child(&self, child: Tree<T>);
	fn pop_child(&self) -> Option<Tree<T>>;
}

impl<T: PartialEq + Clone> IncTree<T> for Tree<T> {
	fn new_node(data: T) -> Tree<T>{
		Rc::new(RefCell::new(TreeNode {
			dirty_val: true,
			dirty_child: false,
			stable_children: 0,
			parent: None,
			data: data,
			decendent_count: 0,
			children: vec![]
		}))
	}

	//sets dirty flags, but leaves counts
	fn dirty_decendents(&self) {
		let mut tree = self.borrow_mut();
		tree.dirty_val = true;
		tree.dirty_child = true;
		for kid in &tree.children {
			kid.dirty_decendents();
		}
	}

	fn dirty_parents(&self){
		let mut parent = None;
		if let Some((ref p_tree,_)) = self.borrow().parent {
			parent = Some(p_tree.clone()); // Rc clone
		}
		if let Some(parent) = parent {
			parent.borrow_mut().dirty_child = true;
			parent.dirty_parents();
		}
	}

	//when a child changes number of decendents, we mark it in
	//this tree and bubble it up. This updates both
	//`decendent_count` and `stable_children`
	fn mark_recount(&self, child_index: usize, new_nodes: i32) {
		let mut tree = self.borrow_mut();
		tree.decendent_count += new_nodes;
		if tree.stable_children > child_index {
			tree.stable_children = child_index;
		}
		if let Some((ref parent, index)) = tree.parent {
			parent.mark_recount(index, new_nodes)
		}
	}

	fn get_data(&self) -> T {
		self.borrow().data.clone()
	}

	fn set_data(&self, data: T) {
		{
			let mut tree = self.borrow_mut();
			if tree.data == data {return}
			tree.data = data;
			tree.dirty_val = true;
		}
		self.dirty_parents();
	}

	fn get_child(&self, child_num: usize) -> Tree<T> {
		self.borrow().children[child_num].clone() //Rc clone
	}

	fn set_child(&self, child_num: usize, child: Tree<T>) {
		let mut tree = self.borrow_mut();
		//assume all new values are dirty
		child.dirty_decendents();
		child.borrow_mut().parent = Some((self.clone(), child_num)); //Rc clone
		child.dirty_parents();
		let mut nodes: i32 = 0;
		{
			//handle a change in the number of decendents
			let ref old_child = tree.children[child_num].borrow();
			if old_child.decendent_count != child.borrow().decendent_count {
				nodes -= old_child.decendent_count;
				nodes += child.borrow().decendent_count;
				child.borrow_mut().stable_children = 0;
				self.mark_recount(child_num, nodes);
			}
		}
		tree.children[child_num] = child;
	}

	fn push_child(&self, child: Tree<T>) {
		//assume all new values are dirty
		child.dirty_decendents();
		child.borrow_mut().parent = Some((self.clone(), self.borrow().children.len())); //Rc clone
		child.dirty_parents();
		let index;
		let nodes;
		{
			let mut tree = self.borrow_mut();
			nodes = 1 + child.borrow().decendent_count;
			index = tree.children.len();
			tree.children.push(child);
		}
		self.mark_recount(index, nodes);
	}

	fn pop_child(&self) -> Option<Tree<T>> {
		//assume all new values are dirty
		let child = self.borrow_mut().children.pop();
		let nodes = match child {
			None => 0,
			Some(ref c) => 1 + c.borrow().decendent_count,
		};
		let index = std::cmp::max(self.borrow().children.len(),0);
		self.mark_recount(index, -nodes);
		child
	}
}

fn dump<T: Clone>(tree: &Tree<T>, vec: &mut PermutedVec<T>) {
	let mut tree = tree.borrow_mut();
    vec.push(tree.data.clone());
    tree.dirty_val = false;
    for kid in &tree.children { dump(&kid, vec); }
    tree.dirty_child = false;
    tree.stable_children = tree.children.len();
}

fn update<T: Clone>(tree: &Tree<T>, start_index: usize, vec: &mut PermutedVec<T>) {
	let mut tree = tree.borrow_mut();
	let mut index = start_index;
	if tree.dirty_val {
		if vec.len() <= index {
			vec.push(tree.data.clone())
		} else {
			vec.set(index, tree.data.clone());
		}
		tree.dirty_val = false;
	}
	if !tree.dirty_child {return}
	index += 1;

	//all the children with unchanged decendent counts
	for counted_kid in 0..tree.stable_children{
		update(&tree.children[counted_kid], index, vec);
		index += 1 + tree.children[counted_kid].borrow_mut().decendent_count as usize;
	}
	if tree.stable_children < tree.children.len() {
		{
			//first changed child may have unchanged decendents
			let first_changed = &tree.children[tree.stable_children];
			update(&first_changed, index, vec);
			index += 1 + first_changed.borrow().decendent_count as usize;
		}
		//dump any additional children
		if (tree.stable_children + 1) < tree.children.len() {
			vec.truncate(index);
			for uncounted_kid in (tree.stable_children + 1)..tree.children.len() {
				dump(&tree.children[uncounted_kid], vec);
			}
			tree.stable_children = tree.children.len();
		}
	}
}

#[test]
fn test_tree() {
    let main_tree = Tree::new_node(37);
    let mut vec = PermutedVec::new();
    dump(&main_tree, &mut vec);
    assert_eq!(vec.sorted_iter().collect::<Vec<&usize>>(), vec![&37]);
}

#[test]
fn test_update() {
	println!("start first dump");
	let main_tree = Tree::new_node(37);
	main_tree.push_child(Tree::new_node(42));
	main_tree.push_child(Tree::new_node(20));
	main_tree.push_child(Tree::new_node(63));
	let mut vec = PermutedVec::new();
	dump(&main_tree, &mut vec);
	println!("finished first dump");
    assert_eq!(vec.sorted_iter().collect::<Vec<&usize>>(), vec![&20,&37,&42,&63]);

	println!("start first update");
    main_tree.get_child(1).set_data(25);
    update(&main_tree, 0, &mut vec);
	println!("finished first update");
    assert_eq!(vec.sorted_iter().collect::<Vec<&usize>>(), vec![&25,&37,&42,&63]);

	println!("start add branch");
    let branch = main_tree.get_child(2);
    branch.push_child(Tree::new_node(47));
    branch.push_child(Tree::new_node(53));
    branch.push_child(Tree::new_node(61));
    branch.push_child(Tree::new_node(57));
    update(&main_tree, 0, &mut vec);
	println!("finished add branch");
    assert_eq!(
    	vec.sorted_iter().collect::<Vec<&usize>>(),
    	vec![&25,&37,&42,&47,&53,&57,&61,&63]
    );

	println!("start update branch");
    branch.get_child(2).set_data(77);
    update(&main_tree, 0, &mut vec);
	println!("finished update branch");
    assert_eq!(
    	vec.sorted_iter().collect::<Vec<&usize>>(),
    	vec![&25,&37,&42,&47,&53,&57,&63,&77]
    );

    println!("start early insertion");
    let branch = main_tree.get_child(1);
    branch.push_child(Tree::new_node(1));
    update(&main_tree, 0, &mut vec);
    println!("finished early insertion");
    assert_eq!(
    	vec.sorted_iter().collect::<Vec<&usize>>(),
    	vec![&1,&25,&37,&42,&47,&53,&57,&63,&77]
    );

    println!("start deep add");
    let branch = main_tree.get_child(2).get_child(2);
    let new_branch = Tree::new_node(100);
    new_branch.push_child(Tree::new_node(101));
    branch.push_child(new_branch);
    update(&main_tree, 0, &mut vec);
    println!("finished deep add");
    assert_eq!(
    	vec.sorted_iter().collect::<Vec<&usize>>(),
    	vec![&1,&25,&37,&42,&47,&53,&57,&63,&77,&100,&101]
    );

    println!("start deep edit");
    let branch = branch.get_child(0).get_child(0);
    branch.set_data(105);
    update(&main_tree, 0, &mut vec);
    println!("finished deep edit");
    assert_eq!(
    	vec.sorted_iter().collect::<Vec<&usize>>(),
    	vec![&1,&25,&37,&42,&47,&53,&57,&63,&77,&100,&105]
    );

    println!("start pop branch");
    let branch = main_tree.get_child(2).get_child(2);
    branch.pop_child();
    update(&main_tree, 0, &mut vec);
    println!("finished pop branch");
    assert_eq!(
    	vec.sorted_iter().collect::<Vec<&usize>>(),
    	vec![&1,&25,&37,&42,&47,&53,&57,&63,&77]
    );

}
<|endoftext|>


<commit_before>extern crate rand;
extern crate rdxsort;

use std::collections;
use std::f32;
use std::f64;
use std::hash;
use std::hash::Hash;
use std::hash::Hasher;
use std::iter;
use std::mem;
use std::ops;

use rand::{Rng, XorShiftRng};
use rand::distributions::{IndependentSample, Range};
use rand::distributions::range::SampleRange;

use rdxsort::*;

static CFG_N: usize = 10_000;

fn is_sorted<T>(data: &Vec<T>) -> bool
    where T: Clone,
          T: PartialOrd
{
    let mut last_entry: Option<T> = None;
    for x in data.iter().cloned() {
        match last_entry {
            Some(l) => {
                if !(l.le(&x)) {
                    return false;
                }
            }
            None => {}
        }
        last_entry = Some(x);
    }
    return true;
}

trait MyHash {
    fn hash_it<H>(&self, state: &mut H) where H: Hasher;
}

macro_rules! trivial_myhash {
    ($t:ty) => {
        impl MyHash for $t {
            fn hash_it<H>(&self, state: &mut H) where H: Hasher {
                self.hash(state);
            }
        }
    }
}

trivial_myhash!(i8);
trivial_myhash!(i16);
trivial_myhash!(i32);
trivial_myhash!(i64);
trivial_myhash!(u8);
trivial_myhash!(u16);
trivial_myhash!(u32);
trivial_myhash!(u64);

impl MyHash for f32 {
    fn hash_it<H>(&self, state: &mut H) where H: Hasher {
        let alias = unsafe {mem::transmute_copy::<f32, u32>(self)};
        alias.hash(state);
    }
}

impl MyHash for f64 {
    fn hash_it<H>(&self, state: &mut H) where H: Hasher {
        let alias = unsafe {mem::transmute_copy::<f64, u64>(self)};
        alias.hash(state);
    }
}

fn guess_entropy<T>(data: &Vec<T>) -> f64 where T: MyHash {
    let mut counter = collections::HashMap::<u64, usize>::new();

    for x in data {
        let mut hasher = hash::SipHasher::new();
        x.hash_it(&mut hasher);
        let key = hasher.finish();

        let state = counter.entry(key).or_insert(0);
        *state += 1;
    }

    counter.values().map(|&x| x as f64 / data.len() as f64).map(|x| -x * x.log2()).fold(0f64, |a, b| a + b)
}

fn test_generic<T>(data: Vec<T>)
    where T: Clone + PartialOrd,
          Vec<T>: RdxSort
{
    let mut data = data;
    let n = data.len();
    let mut data_sorted_ref = data.clone();
    data_sorted_ref.sort_by(|a, b| a.partial_cmp(b).unwrap());

    data.rdxsort();
    assert!(data.len() == n, "sorted data has wrong lenght!");
    assert!(is_sorted(&data), "data is not sorted!");
    for (x, y) in data.iter().zip(data_sorted_ref.iter()) {
        assert!(x == y, "sortd data does not match the reference!");
    }
}

fn test_rnd_generic<T>(vmin: T, vmax: T, vspecial: Vec<T>)
    where T: Clone + PartialOrd + SampleRange + MyHash,
          Vec<T>: RdxSort
{
    // config
    let entropy_threshold = 1f64;

    // generate data
    let r = Range::new(vmin.clone(), vmax.clone());
    let mut rng = XorShiftRng::new_unseeded();
    let mut data = Vec::with_capacity(CFG_N);
    for _ in (0 as usize)..CFG_N {
        data.push(r.ind_sample(&mut rng));
    }
    let mut positions = Vec::with_capacity(CFG_N);
    for i in 0..CFG_N {
        positions.push(i);
    }
    rng.shuffle(&mut positions[..]);
    assert!(vspecial.len() + 2 < CFG_N, "to many special values to test!");
    data[positions[0]] = vmin;
    data[positions[1]] = vmax;
    for (i, x) in positions.into_iter().skip(2).zip(vspecial.into_iter()) {
        data[i] = x;
    }
    assert!(data.len() == CFG_N, "generated data has wrong length!");
    assert!(guess_entropy(&data) >= entropy_threshold, "generated data does not contain enough entropy!");

    test_generic(data);
}

fn test_full_generic<T>(vmin: T, vmax: T)
    where T: Clone+ PartialOrd + ops::Add,
          ops::Range<T>: iter::Iterator,
          Vec<T>: iter::FromIterator<<ops::Range<T> as iter::Iterator>::Item>,
          Vec<T>: RdxSort
{
    // TODO: use inclusive range once it's stable
    let mut data: Vec<T> = (vmin.clone()..vmax.clone()).collect();
    data.push(vmax);

    let mut rng = XorShiftRng::new_unseeded();
    rng.shuffle(&mut data[..]);

    test_generic(data);
}

#[test]
fn test_rnd_bool() {
    let mut rng = XorShiftRng::new_unseeded();
    let data: Vec<bool> = rng.gen_iter::<bool>().take(CFG_N).collect();

    test_generic(data);
}

#[test]
fn test_rnd_i8() {
    test_rnd_generic::<i8>(i8::min_value(), i8::max_value(), vec![i8::min_value() + 1, -1i8, 0i8, 1i8, i8::max_value() - 1]);
}

#[test]
fn test_full_i8() {
    test_full_generic::<i8>(i8::min_value(), i8::max_value());
}

#[test]
fn test_rnd_i16() {
    test_rnd_generic::<i16>(i16::min_value(), i16::max_value(), vec![i16::min_value() + 1, -1i16, 0i16, 1i16, i16::max_value() - 1]);
}

#[test]
fn test_full_i16() {
    test_full_generic::<i16>(i16::min_value(), i16::max_value());
}

#[test]
fn test_rnd_i32() {
    test_rnd_generic::<i32>(i32::min_value(), i32::max_value(), vec![i32::min_value() + 1, -1i32, 0i32, 1i32, i32::max_value() - 1]);
}

#[test]
fn test_rnd_i64() {
    test_rnd_generic::<i64>(i64::min_value(), i64::max_value(), vec![i64::min_value() + 1, -1i64, 0i64, 1i64, i64::max_value() - 1]);
}

#[test]
fn test_rnd_u8() {
    test_rnd_generic::<u8>(u8::min_value(), u8::max_value(), vec![0u8, 1u8, u8::max_value() - 1]);
}

#[test]
fn test_full_u8() {
    test_full_generic::<u8>(u8::min_value(), u8::max_value());
}

#[test]
fn test_rnd_u16() {
    test_rnd_generic::<u16>(u16::min_value(), u16::max_value(), vec![0u16, 1u16, u16::max_value() - 1]);
}

#[test]
fn test_full_u16() {
    test_full_generic::<u16>(u16::min_value(), u16::max_value());
}

#[test]
fn test_rnd_u32() {
    test_rnd_generic::<u32>(u32::min_value(), u32::max_value(), vec![0u32, 1u32, u32::max_value() - 1]);
}

#[test]
fn test_rnd_u64() {
    test_rnd_generic::<u64>(u64::min_value(), u64::max_value(), vec![0u64, 1u64, u64::max_value() - 1]);
}

#[test]
fn test_rnd_f32() {
    // DO NOT use MIN/MAX here, since that overflows the RNG system!
    test_rnd_generic::<f32>(0f32, 1f32, vec![-f32::INFINITY, -0f32, 0f32, f32::INFINITY]);
}

#[test]
fn test_rnd_f64() {
    // DO NOT use MIN/MAX here, since that overflows the RNG system!
    test_rnd_generic::<f64>(0f64, 1f64, vec![-f64::INFINITY, -0f64, 0f64, f64::INFINITY]);
}
<commit_msg>tests: single + empty<commit_after>extern crate rand;
extern crate rdxsort;

use std::collections;
use std::hash;
use std::hash::Hash;
use std::hash::Hasher;
use std::iter;
use std::mem;
use std::ops;

use rand::{Rng, XorShiftRng};
use rand::distributions::{IndependentSample, Range};
use rand::distributions::range::SampleRange;

use rdxsort::*;

pub static CFG_N: usize = 10_000;

fn is_sorted<T>(data: &Vec<T>) -> bool
    where T: Clone,
          T: PartialOrd
{
    let mut last_entry: Option<T> = None;
    for x in data.iter().cloned() {
        match last_entry {
            Some(l) => {
                if !(l.le(&x)) {
                    return false;
                }
            }
            None => {}
        }
        last_entry = Some(x);
    }
    return true;
}

pub trait MyHash {
    fn hash_it<H>(&self, state: &mut H) where H: Hasher;
}

macro_rules! trivial_myhash {
    ($t:ty) => {
        impl MyHash for $t {
            fn hash_it<H>(&self, state: &mut H) where H: Hasher {
                self.hash(state);
            }
        }
    }
}

trivial_myhash!(i8);
trivial_myhash!(i16);
trivial_myhash!(i32);
trivial_myhash!(i64);
trivial_myhash!(u8);
trivial_myhash!(u16);
trivial_myhash!(u32);
trivial_myhash!(u64);

impl MyHash for f32 {
    fn hash_it<H>(&self, state: &mut H) where H: Hasher {
        let alias = unsafe {mem::transmute_copy::<f32, u32>(self)};
        alias.hash(state);
    }
}

impl MyHash for f64 {
    fn hash_it<H>(&self, state: &mut H) where H: Hasher {
        let alias = unsafe {mem::transmute_copy::<f64, u64>(self)};
        alias.hash(state);
    }
}

fn guess_entropy<T>(data: &Vec<T>) -> f64 where T: MyHash {
    let mut counter = collections::HashMap::<u64, usize>::new();

    for x in data {
        let mut hasher = hash::SipHasher::new();
        x.hash_it(&mut hasher);
        let key = hasher.finish();

        let state = counter.entry(key).or_insert(0);
        *state += 1;
    }

    counter.values().map(|&x| x as f64 / data.len() as f64).map(|x| -x * x.log2()).fold(0f64, |a, b| a + b)
}

pub fn test_generic<T>(data: Vec<T>)
    where T: Clone + PartialOrd,
          Vec<T>: RdxSort
{
    let mut data = data;
    let n = data.len();
    let mut data_sorted_ref = data.clone();
    data_sorted_ref.sort_by(|a, b| a.partial_cmp(b).unwrap());

    data.rdxsort();
    assert!(data.len() == n, "sorted data has wrong lenght!");
    assert!(is_sorted(&data), "data is not sorted!");
    for (x, y) in data.iter().zip(data_sorted_ref.iter()) {
        assert!(x == y, "sortd data does not match the reference!");
    }
}

pub fn test_empty_generic<T>()
    where T: Clone + PartialOrd,
          Vec<T>: RdxSort
{
    let data: Vec<T> = vec![];
    test_generic(data);
}

pub fn test_rnd_generic<T>(vmin: T, vmax: T, vspecial: Vec<T>)
    where T: Clone + PartialOrd + SampleRange + MyHash,
          Vec<T>: RdxSort
{
    // config
    let entropy_threshold = 1f64;

    // generate data
    let r = Range::new(vmin.clone(), vmax.clone());
    let mut rng = XorShiftRng::new_unseeded();
    let mut data = Vec::with_capacity(CFG_N);
    for _ in (0 as usize)..CFG_N {
        data.push(r.ind_sample(&mut rng));
    }
    let mut positions = Vec::with_capacity(CFG_N);
    for i in 0..CFG_N {
        positions.push(i);
    }
    rng.shuffle(&mut positions[..]);
    assert!(vspecial.len() + 2 < CFG_N, "to many special values to test!");
    data[positions[0]] = vmin;
    data[positions[1]] = vmax;
    for (i, x) in positions.into_iter().skip(2).zip(vspecial.into_iter()) {
        data[i] = x;
    }
    assert!(data.len() == CFG_N, "generated data has wrong length!");
    assert!(guess_entropy(&data) >= entropy_threshold, "generated data does not contain enough entropy!");

    test_generic(data);
}

pub fn test_single_generic<T>(x: T)
    where T: Clone + PartialOrd,
          Vec<T>: RdxSort
{
    let data: Vec<T> = vec![x];
    test_generic(data);
}

pub fn test_full_generic<T>(vmin: T, vmax: T)
    where T: Clone+ PartialOrd + ops::Add,
          ops::Range<T>: iter::Iterator,
          Vec<T>: iter::FromIterator<<ops::Range<T> as iter::Iterator>::Item>,
          Vec<T>: RdxSort
{
    // TODO: use inclusive range once it's stable
    let mut data: Vec<T> = (vmin.clone()..vmax.clone()).collect();
    data.push(vmax);

    let mut rng = XorShiftRng::new_unseeded();
    rng.shuffle(&mut data[..]);

    test_generic(data);
}

mod sub_bool {
    use super::*;

    use rand::{Rng, XorShiftRng};

    #[test]
    fn test_rnd_bool() {
        let mut rng = XorShiftRng::new_unseeded();
        let data: Vec<bool> = rng.gen_iter::<bool>().take(CFG_N).collect();

        test_generic(data);
    }

    #[test]
    fn test_empty_bool() {
        test_empty_generic::<bool>();
    }

    #[test]
    fn test_single_bool() {
        test_single_generic::<bool>(true);
    }
}

mod sub_i8 {
    use super::*;

    #[test]
    fn test_rnd_i8() {
        test_rnd_generic::<i8>(i8::min_value(), i8::max_value(), vec![i8::min_value() + 1, -1i8, 0i8, 1i8, i8::max_value() - 1]);
    }

    #[test]
    fn test_full_i8() {
        test_full_generic::<i8>(i8::min_value(), i8::max_value());
    }

    #[test]
    fn test_empty_i8() {
        test_empty_generic::<i8>();
    }

    #[test]
    fn test_single_i8() {
        test_single_generic::<i8>(3i8);
    }
}

mod sub_i16 {
    use super::*;

    #[test]
    fn test_rnd_i16() {
        test_rnd_generic::<i16>(i16::min_value(), i16::max_value(), vec![i16::min_value() + 1, -1i16, 0i16, 1i16, i16::max_value() - 1]);
    }

    #[test]
    fn test_full_i16() {
        test_full_generic::<i16>(i16::min_value(), i16::max_value());
    }

    #[test]
    fn test_empty_i16() {
        test_empty_generic::<i16>();
    }

    #[test]
    fn test_single_i16() {
        test_single_generic::<i16>(3i16);
    }
}

mod sub_i32 {
    use super::*;

    #[test]
    fn test_rnd_i32() {
        test_rnd_generic::<i32>(i32::min_value(), i32::max_value(), vec![i32::min_value() + 1, -1i32, 0i32, 1i32, i32::max_value() - 1]);
    }

    #[test]
    fn test_empty_i32() {
        test_empty_generic::<i32>();
    }

    #[test]
    fn test_single_i32() {
        test_single_generic::<i32>(3i32);
    }
}

mod sub_i64 {
    use super::*;

    #[test]
    fn test_rnd_i64() {
        test_rnd_generic::<i64>(i64::min_value(), i64::max_value(), vec![i64::min_value() + 1, -1i64, 0i64, 1i64, i64::max_value() - 1]);
    }

    #[test]
    fn test_empty_i64() {
        test_empty_generic::<i64>();
    }

    #[test]
    fn test_single_i64() {
        test_single_generic::<i64>(3i64);
    }
}

mod sub_u8 {
    use super::*;

    #[test]
    fn test_rnd_u8() {
        test_rnd_generic::<u8>(u8::min_value(), u8::max_value(), vec![0u8, 1u8, u8::max_value() - 1]);
    }

    #[test]
    fn test_full_u8() {
        test_full_generic::<u8>(u8::min_value(), u8::max_value());
    }

    #[test]
    fn test_empty_u8() {
        test_empty_generic::<u8>();
    }

    #[test]
    fn test_single_u8() {
        test_single_generic::<u8>(3u8);
    }
}

mod sub_u16 {
    use super::*;

    #[test]
    fn test_rnd_u16() {
        test_rnd_generic::<u16>(u16::min_value(), u16::max_value(), vec![0u16, 1u16, u16::max_value() - 1]);
    }

    #[test]
    fn test_full_u16() {
        test_full_generic::<u16>(u16::min_value(), u16::max_value());
    }

    #[test]
    fn test_empty_u16() {
        test_empty_generic::<u16>();
    }

    #[test]
    fn test_single_u16() {
        test_single_generic::<u16>(3u16);
    }
}

mod sub_u32 {
    use super::*;

    #[test]
    fn test_rnd_u32() {
        test_rnd_generic::<u32>(u32::min_value(), u32::max_value(), vec![0u32, 1u32, u32::max_value() - 1]);
    }

    #[test]
    fn test_empty_u32() {
        test_empty_generic::<u32>();
    }

    #[test]
    fn test_single_u32() {
        test_single_generic::<u32>(3u32);
    }
}

mod sub_u64 {
    use super::*;

    #[test]
    fn test_rnd_u64() {
        test_rnd_generic::<u64>(u64::min_value(), u64::max_value(), vec![0u64, 1u64, u64::max_value() - 1]);
    }

    #[test]
    fn test_empty_u64() {
        test_empty_generic::<u64>();
    }

    #[test]
    fn test_single_u64() {
        test_single_generic::<u64>(3u64);
    }
}

mod sub_f32 {
    use super::*;

    use std::f32;

    #[test]
    fn test_rnd_f32() {
        // DO NOT use MIN/MAX here, since that overflows the RNG system!
        test_rnd_generic::<f32>(0f32, 1f32, vec![-f32::INFINITY, -0f32, 0f32, f32::INFINITY]);
    }

    #[test]
    fn test_empty_f32() {
        test_empty_generic::<f32>();
    }

    #[test]
    fn test_single_f32() {
        test_single_generic::<f32>(3f32);
    }
}

mod sub_f64 {
    use super::*;

    use std::f64;

    #[test]
    fn test_rnd_f64() {
        // DO NOT use MIN/MAX here, since that overflows the RNG system!
        test_rnd_generic::<f64>(0f64, 1f64, vec![-f64::INFINITY, -0f64, 0f64, f64::INFINITY]);
    }

    #[test]
    fn test_empty_f64() {
        test_empty_generic::<f64>();
    }

    #[test]
    fn test_single_f64() {
        test_single_generic::<f64>(3f64);
    }
}
<|endoftext|>