<commit_before>use tokenizer::{TokenStream, Tokenizer, Token};
use std::collections::BTreeMap;
use Term;
use Document;
use Index;
use schema::FieldValue;
use schema::Value;
use tokenizer::BoxedTokenizer;
use htmlescape::encode_minimal;

#[derive(Debug)]
pub struct HighlightSection {
    start: usize,
    stop: usize,
}

impl HighlightSection {
    fn new(start: usize, stop: usize) -> HighlightSection {
        HighlightSection {
            start,
            stop
        }
    }
}

#[derive(Debug)]
pub struct FragmentCandidate {
    score: f32,
    start_offset: usize,
    stop_offset: usize,
    num_chars: usize,
    highlighted: Vec<HighlightSection>,
}

impl FragmentCandidate {

    /// Create a basic `FragmentCandidate`
    ///
    /// `score`, `num_chars` are set to 0
    /// and `highlighted` is set to empty vec
    /// stop_offset is set to start_offset, which is taken as a param.
    fn new(start_offset: usize) -> FragmentCandidate {
        FragmentCandidate{score: 0.0,
                          start_offset: start_offset,
                          stop_offset: start_offset,
                          num_chars: 0,
                          highlighted: vec![]}
    }

    /// Updates `score` and `highlighted` fields of the objects.
    ///
    /// taking the token and terms, the token is added to the fragment.
    /// if the token is one of the terms, the score
    /// and highlighted fields are updated in the fragment.
    fn try_add_token(&mut self, token: &Token, terms: &BTreeMap<String, f32>) {
        self.stop_offset = token.offset_to;

        if let Some(score) = terms.get(&token.text.to_lowercase()) {
            self.score += score;
            self.highlighted.push(HighlightSection{start: token.offset_from,
                                                   stop: token.offset_to});
        }
    }
}

#[derive(Debug)]
pub struct Snippet {
    fragments: String,
    highlighted: Vec<HighlightSection>,
}

const HIGHLIGHTEN_PREFIX:&str = "";
const HIGHLIGHTEN_POSTFIX:&str = "";

impl Snippet {

    /// Returns a hignlightned html from the `Snippet`.
    pub fn to_html(&self) -> String {
        let mut html = String::new();
        let mut start_from: usize = 0;

        for item in self.highlighted.iter() {
            html.push_str(&encode_minimal(&self.fragments[start_from..item.start]));
            html.push_str(HIGHLIGHTEN_PREFIX);
            html.push_str(&encode_minimal(&self.fragments[item.start..item.stop]));
            html.push_str(HIGHLIGHTEN_POSTFIX);
            start_from = item.stop;
        }
        html.push_str(&encode_minimal(&self.fragments[start_from..self.fragments.len()]));
        html
    }
}

/// Returns a non-empty list of "good" fragments.
///
/// If no target term is within the text, then the function
/// should return an empty Vec.
///
/// If a target term is within the text, then the returned
/// list is required to be non-empty.
///
/// The returned list is non-empty and contain less
/// than 12 possibly overlapping fragments.
///
/// All fragments should contain at least one target term
/// and have at most `max_num_chars` characters (not bytes).
///
/// It is ok to emit non-overlapping fragments, for instance,
/// one short and one long containing the same keyword, in order
/// to leave optimization opportunity to the fragment selector
/// upstream.
///
/// Fragments must be valid in the sense that `&text[fragment.start..fragment.stop]`\
/// has to be a valid string.
fn search_fragments<'a>(
    tokenizer: Box<BoxedTokenizer>,
    text: &'a str,
    terms: BTreeMap<String, f32>,
    max_num_chars: usize) -> Vec<FragmentCandidate> {
    let mut token_stream = tokenizer.token_stream(text);
    let mut fragment = FragmentCandidate::new(0);
    let mut fragments:Vec<FragmentCandidate> = vec![];

    while let Some(next) = token_stream.next() {
        if (next.offset_to - fragment.start_offset) > max_num_chars {
            if fragment.score > 0.0 {
                fragments.push(fragment)
            };
            fragment = FragmentCandidate::new(next.offset_from);
        } else {
            fragment.try_add_token(next, &terms);
        }
    }
    if fragment.score > 0.0 {
        fragments.push(fragment)
    }

    fragments
}

/// Returns a Snippet
///
/// Takes a vector of `FragmentCandidate`s and the text.
/// Figures out the best fragment from it and creates a snippet.
fn select_best_fragment_combination<'a>(fragments: Vec<FragmentCandidate>,
                                        text: &'a str,) -> Snippet {
    if let Some(init) = fragments.iter().nth(0) {
        let fragment = fragments.iter().skip(1).fold(init, |acc, item| {
            if item.score > acc.score { item } else { acc }
        });
        let fragment_text = &text[fragment.start_offset..fragment.stop_offset];
        let highlighted = fragment.highlighted.iter().map(|item| {
            HighlightSection{start: item.start-fragment.start_offset,
                             stop: item.stop-fragment.start_offset}
        }).collect();
        Snippet{fragments: fragment_text.to_owned(),
                highlighted: highlighted}
    } else {
        // when there no fragments to chose from,
        // for now create a empty snippet
        Snippet{fragments: String::new(),
                highlighted: vec![]}
    }
}

pub fn generate_snippet<'a>(
    doc: &'a [FieldValue],
    index: &Index,
    terms: Vec<Term>,
    max_num_chars: usize) -> Snippet {
    unimplemented!();
}

#[cfg(test)]
mod tests {
    use tokenizer::{SimpleTokenizer, box_tokenizer};
    use std::iter::Iterator;
    use std::collections::BTreeMap;
    use super::{search_fragments, select_best_fragment_combination};

    #[test]
    fn test_snippet() {
        let tokenizer = SimpleTokenizer;

        let t = box_tokenizer(tokenizer);

        let text = "Rust is a systems programming language sponsored by Mozilla which describes it as a \"safe, concurrent, practical language\", supporting functional and imperative-procedural paradigms. Rust is syntactically similar to C++[according to whom?], but its designers intend it to provide better memory safety while still maintaining performance.

Rust is free and open-source software, released under an MIT License, or Apache License 2.0. Its designers have refined the language through the experiences of writing the Servo web browser layout engine[14] and the Rust compiler. A large proportion of current commits to the project are from community members.[15]

Rust won first place for \"most loved programming language\" in the Stack Overflow Developer Survey in 2016, 2017, and 2018.
";

        let mut terms = BTreeMap::new();
        terms.insert(String::from("rust"), 1.0);
        terms.insert(String::from("language"), 0.9);

        let fragments = search_fragments(t, &text, terms, 100);
        assert_eq!(fragments.len(), 7);
        {
            let first = fragments.iter().nth(0).unwrap();
            assert_eq!(first.score, 1.9);
            assert_eq!(first.stop_offset, 89);
        }
        let snippet = select_best_fragment_combination(fragments, &text);
        assert_eq!(snippet.fragments, "Rust is a systems programming language sponsored by Mozilla which describes it as a \"safe".to_owned());
        assert_eq!(snippet.to_html(), "Rust is a systems programming language sponsored by Mozilla which describes it as a "safe".to_owned())
    }
}
<commit_msg>add a test and fix the bug of not calculating first token<commit_after>use tokenizer::{TokenStream, Tokenizer, Token};
use std::collections::BTreeMap;
use Term;
use Document;
use Index;
use schema::FieldValue;
use schema::Value;
use tokenizer::BoxedTokenizer;
use htmlescape::encode_minimal;

#[derive(Debug)]
pub struct HighlightSection {
    start: usize,
    stop: usize,
}

impl HighlightSection {
    fn new(start: usize, stop: usize) -> HighlightSection {
        HighlightSection {
            start,
            stop
        }
    }
}

#[derive(Debug)]
pub struct FragmentCandidate {
    score: f32,
    start_offset: usize,
    stop_offset: usize,
    num_chars: usize,
    highlighted: Vec<HighlightSection>,
}

impl FragmentCandidate {

    /// Create a basic `FragmentCandidate`
    ///
    /// `score`, `num_chars` are set to 0
    /// and `highlighted` is set to empty vec
    /// stop_offset is set to start_offset, which is taken as a param.
    fn new(start_offset: usize) -> FragmentCandidate {
        FragmentCandidate{score: 0.0,
                          start_offset: start_offset,
                          stop_offset: start_offset,
                          num_chars: 0,
                          highlighted: vec![]}
    }

    /// Updates `score` and `highlighted` fields of the objects.
    ///
    /// taking the token and terms, the token is added to the fragment.
    /// if the token is one of the terms, the score
    /// and highlighted fields are updated in the fragment.
    fn try_add_token(&mut self, token: &Token, terms: &BTreeMap<String, f32>) {
        self.stop_offset = token.offset_to;

        if let Some(score) = terms.get(&token.text.to_lowercase()) {
            self.score += score;
            self.highlighted.push(HighlightSection{start: token.offset_from,
                                                   stop: token.offset_to});
        }
    }
}

#[derive(Debug)]
pub struct Snippet {
    fragments: String,
    highlighted: Vec<HighlightSection>,
}

const HIGHLIGHTEN_PREFIX:&str = "";
const HIGHLIGHTEN_POSTFIX:&str = "";

impl Snippet {

    /// Returns a hignlightned html from the `Snippet`.
    pub fn to_html(&self) -> String {
        let mut html = String::new();
        let mut start_from: usize = 0;

        for item in self.highlighted.iter() {
            html.push_str(&encode_minimal(&self.fragments[start_from..item.start]));
            html.push_str(HIGHLIGHTEN_PREFIX);
            html.push_str(&encode_minimal(&self.fragments[item.start..item.stop]));
            html.push_str(HIGHLIGHTEN_POSTFIX);
            start_from = item.stop;
        }
        html.push_str(&encode_minimal(&self.fragments[start_from..self.fragments.len()]));
        html
    }
}

/// Returns a non-empty list of "good" fragments.
///
/// If no target term is within the text, then the function
/// should return an empty Vec.
///
/// If a target term is within the text, then the returned
/// list is required to be non-empty.
///
/// The returned list is non-empty and contain less
/// than 12 possibly overlapping fragments.
///
/// All fragments should contain at least one target term
/// and have at most `max_num_chars` characters (not bytes).
///
/// It is ok to emit non-overlapping fragments, for instance,
/// one short and one long containing the same keyword, in order
/// to leave optimization opportunity to the fragment selector
/// upstream.
///
/// Fragments must be valid in the sense that `&text[fragment.start..fragment.stop]`\
/// has to be a valid string.
fn search_fragments<'a>(
    tokenizer: Box<BoxedTokenizer>,
    text: &'a str,
    terms: BTreeMap<String, f32>,
    max_num_chars: usize) -> Vec<FragmentCandidate> {
    let mut token_stream = tokenizer.token_stream(text);
    let mut fragment = FragmentCandidate::new(0);
    let mut fragments:Vec<FragmentCandidate> = vec![];

    while let Some(next) = token_stream.next() {
        if (next.offset_to - fragment.start_offset) > max_num_chars {
            if fragment.score > 0.0 {
                fragments.push(fragment)
            };
            fragment = FragmentCandidate::new(next.offset_from);
        }
        fragment.try_add_token(next, &terms);
    }
    if fragment.score > 0.0 {
        fragments.push(fragment)
    }

    fragments
}

/// Returns a Snippet
///
/// Takes a vector of `FragmentCandidate`s and the text.
/// Figures out the best fragment from it and creates a snippet.
fn select_best_fragment_combination<'a>(fragments: Vec<FragmentCandidate>,
                                        text: &'a str,) -> Snippet {
    if let Some(init) = fragments.iter().nth(0) {
        let fragment = fragments.iter().skip(1).fold(init, |acc, item| {
            if item.score > acc.score { item } else { acc }
        });
        let fragment_text = &text[fragment.start_offset..fragment.stop_offset];
        let highlighted = fragment.highlighted.iter().map(|item| {
            HighlightSection{start: item.start-fragment.start_offset,
                             stop: item.stop-fragment.start_offset}
        }).collect();
        Snippet{fragments: fragment_text.to_owned(),
                highlighted: highlighted}
    } else {
        // when there no fragments to chose from,
        // for now create a empty snippet
        Snippet{fragments: String::new(),
                highlighted: vec![]}
    }
}

pub fn generate_snippet<'a>(
    doc: &'a [FieldValue],
    index: &Index,
    terms: Vec<Term>,
    max_num_chars: usize) -> Snippet {
    unimplemented!();
}

#[cfg(test)]
mod tests {
    use tokenizer::{SimpleTokenizer, box_tokenizer};
    use std::iter::Iterator;
    use std::collections::BTreeMap;
    use super::{search_fragments, select_best_fragment_combination};

    #[test]
    fn test_snippet() {
        let tokenizer = SimpleTokenizer;

        let boxed_tokenizer = box_tokenizer(tokenizer);

        let text = "Rust is a systems programming language sponsored by Mozilla which describes it as a \"safe, concurrent, practical language\", supporting functional and imperative-procedural paradigms. Rust is syntactically similar to C++[according to whom?], but its designers intend it to provide better memory safety while still maintaining performance.

Rust is free and open-source software, released under an MIT License, or Apache License 2.0. Its designers have refined the language through the experiences of writing the Servo web browser layout engine[14] and the Rust compiler. A large proportion of current commits to the project are from community members.[15]

Rust won first place for \"most loved programming language\" in the Stack Overflow Developer Survey in 2016, 2017, and 2018.
";

        let mut terms = BTreeMap::new();
        terms.insert(String::from("rust"), 1.0);
        terms.insert(String::from("language"), 0.9);

        let fragments = search_fragments(boxed_tokenizer, &text, terms, 100);
        assert_eq!(fragments.len(), 7);
        {
            let first = fragments.iter().nth(0).unwrap();
            assert_eq!(first.score, 1.9);
            assert_eq!(first.stop_offset, 89);
        }
        let snippet = select_best_fragment_combination(fragments, &text);
        assert_eq!(snippet.fragments, "Rust is a systems programming language sponsored by Mozilla which describes it as a \"safe".to_owned());
        assert_eq!(snippet.to_html(), "Rust is a systems programming language sponsored by Mozilla which describes it as a "safe".to_owned())
    }

    #[test]
    fn test_snippet_in_second_fragment() {
        let tokenizer = SimpleTokenizer;

        let boxed_tokenizer = box_tokenizer(tokenizer);

        let text = "a b c d e f g";

        let mut terms = BTreeMap::new();
        terms.insert(String::from("c"), 1.0);

        let fragments = search_fragments(boxed_tokenizer, &text, terms, 3);

        assert_eq!(fragments.len(), 1);
        {
            let first = fragments.iter().nth(0).unwrap();
            assert_eq!(first.score, 1.0);
            assert_eq!(first.start_offset, 4);
            assert_eq!(first.stop_offset, 6);
        }

        let snippet = select_best_fragment_combination(fragments, &text);
        assert_eq!(snippet.fragments, "c d");
        assert_eq!(snippet.to_html(), "c d");
    }
}
<|endoftext|>


<commit_before>use std::cell::RefCell;
use std::collections::HashMap;

pub mod path;
pub mod file;
pub mod parser;
pub mod json;

use module::Module;
use storage::file::File;
use storage::file::id::FileID;
use storage::file::id_type::FileIDType;
use storage::file::hash::FileHash;
use storage::parser::{FileHeaderParser, Parser, ParserError};
use storage::file::header::data::FileHeaderData;

type Cache = HashMap<FileID, RefCell<File>>;

pub struct Store {
    cache : RefCell<Cache>,
}

impl Store {

    pub fn new() -> Store {
        Store {
            cache: RefCell::new(HashMap::new()),
        }
    }

    fn put_in_cache(&self, f: File) -> FileID {
        let res = f.id().clone();
        self.cache.borrow_mut().insert(f.id().clone(), RefCell::new(f));
        res
    }

    pub fn new_file(&self, module: &Module)
        -> FileID
    {
        let f = File {
            owning_module_name: module.name(),
            header: FileHeaderData::Null,
            data: String::from(""),
            id: self.get_new_file_id(),
        };

        debug!("Create new File object: {:?}", &f);
        self.put_in_cache(f)
    }

    pub fn new_file_from_parser_result(&self,
                                       module: &Module,
                                       id: FileID,
                                       header: FileHeaderData,
                                       data: String)
        -> FileID
    {
        let f = File {
            owning_module_name: module.name(),
            header: header,
            data: data,
            id: id,
        };
        debug!("Create new File object from parser result: {:?}", f);
        self.put_in_cache(f)
    }

    pub fn new_file_with_header(&self,
                                module: &Module,
                                h: FileHeaderData)
        -> FileID
    {
        let f = File {
            owning_module_name: module.name(),
            header: h,
            data: String::from(""),
            id: self.get_new_file_id(),
        };
        debug!("Create new File object with header: {:?}", f);
        self.put_in_cache(f)
    }

    pub fn new_file_with_data(&self, module: &Module, d: String)
        -> FileID
    {
        let f = File {
            owning_module_name: module.name(),
            header: FileHeaderData::Null,
            data: d,
            id: self.get_new_file_id(),
        };
        debug!("Create new File object with data: {:?}", f);
        self.put_in_cache(f)
    }

    pub fn new_file_with_content(&self,
                                 module: &Module,
                                 h: FileHeaderData,
                                 d: String)
        -> FileID
    {
        let f = File {
            owning_module_name: module.name(),
            header: h,
            data: d,
            id: self.get_new_file_id(),
        };
        debug!("Create new File object with content: {:?}", f);
        self.put_in_cache(f)
    }

    pub fn persist(&self, file: &File) -> bool {
        unimplemented!()
    }

    pub fn load(&self, id: FileID) -> File {
        unimplemented!()
    }

    fn get_new_file_id(&self) -> FileID {
        use uuid::Uuid;
        let hash = FileHash::from(Uuid::new_v4().to_hyphenated_string());
        FileID::new(FileIDType::UUID, hash)
    }

}
<commit_msg>Implement Store::load()<commit_after>use std::rc::Rc;
use std::cell::RefCell;
use std::collections::HashMap;

pub mod path;
pub mod file;
pub mod parser;
pub mod json;

use module::Module;
use storage::file::File;
use storage::file::id::FileID;
use storage::file::id_type::FileIDType;
use storage::file::hash::FileHash;
use storage::parser::{FileHeaderParser, Parser, ParserError};
use storage::file::header::data::FileHeaderData;

type Cache = HashMap<FileID, Rc<RefCell<File>>>;

pub struct Store {
    cache : RefCell<Cache>,
}

impl Store {

    pub fn new() -> Store {
        Store {
            cache: RefCell::new(HashMap::new()),
        }
    }

    fn put_in_cache(&self, f: File) -> FileID {
        let res = f.id().clone();
        self.cache.borrow_mut().insert(f.id().clone(), Rc::new(RefCell::new(f)));
        res
    }

    pub fn new_file(&self, module: &Module)
        -> FileID
    {
        let f = File {
            owning_module_name: module.name(),
            header: FileHeaderData::Null,
            data: String::from(""),
            id: self.get_new_file_id(),
        };

        debug!("Create new File object: {:?}", &f);
        self.put_in_cache(f)
    }

    pub fn new_file_from_parser_result(&self,
                                       module: &Module,
                                       id: FileID,
                                       header: FileHeaderData,
                                       data: String)
        -> FileID
    {
        let f = File {
            owning_module_name: module.name(),
            header: header,
            data: data,
            id: id,
        };
        debug!("Create new File object from parser result: {:?}", f);
        self.put_in_cache(f)
    }

    pub fn new_file_with_header(&self,
                                module: &Module,
                                h: FileHeaderData)
        -> FileID
    {
        let f = File {
            owning_module_name: module.name(),
            header: h,
            data: String::from(""),
            id: self.get_new_file_id(),
        };
        debug!("Create new File object with header: {:?}", f);
        self.put_in_cache(f)
    }

    pub fn new_file_with_data(&self, module: &Module, d: String)
        -> FileID
    {
        let f = File {
            owning_module_name: module.name(),
            header: FileHeaderData::Null,
            data: d,
            id: self.get_new_file_id(),
        };
        debug!("Create new File object with data: {:?}", f);
        self.put_in_cache(f)
    }

    pub fn new_file_with_content(&self,
                                 module: &Module,
                                 h: FileHeaderData,
                                 d: String)
        -> FileID
    {
        let f = File {
            owning_module_name: module.name(),
            header: h,
            data: d,
            id: self.get_new_file_id(),
        };
        debug!("Create new File object with content: {:?}", f);
        self.put_in_cache(f)
    }

    pub fn persist(&self, file: &File) -> bool {
        unimplemented!()
    }

    pub fn load(&self, id: &FileID) -> Option<Rc<RefCell<File>>> {
        debug!("Loading '{:?}'", id);
        self.cache.borrow().get(id).cloned()
    }

    fn get_new_file_id(&self) -> FileID {
        use uuid::Uuid;
        let hash = FileHash::from(Uuid::new_v4().to_hyphenated_string());
        FileID::new(FileIDType::UUID, hash)
    }

}
<|endoftext|>


<commit_before>use std::{thread,
          time::{Duration,
                 Instant}};

/// How long to wait for an Ack after we ping
const PING_TIMING_DEFAULT_MS: u64 = 1000;
/// How long to wait for an Ack after we PingReq - should be at least 2x the PING_TIMING_DEFAULT_MS
const PINGREQ_TIMING_DEFAULT_MS: u64 = 2100;
/// How many protocol periods before a suspect member is marked as confirmed.
const SUSPICION_TIMEOUT_DEFAULT_PROTOCOL_PERIODS: u64 = 3;
/// How long to wait between each time we send rumors out.
const GOSSIP_INTERVAL_DEFAULT_MS: u64 = 1000;
/// How long before we set a confirmed member to a departed member, removing them from quorums
///   just for your own sanity - this is 3 days.
const DEPARTURE_TIMEOUT_DEFAULT_MS: u64 = 259_200_000;

/// The timing of the outbound threads.
#[derive(Debug, Clone)]
pub struct Timing {
    ping_ms: u64,
    pingreq_ms: u64,
    gossip_interval_ms: u64,
    suspicion_timeout_protocol_periods: u64,
    departure_timeout_ms: u64,
}

impl Default for Timing {
    fn default() -> Timing {
        Timing { ping_ms: PING_TIMING_DEFAULT_MS,
                 pingreq_ms: PINGREQ_TIMING_DEFAULT_MS,
                 gossip_interval_ms: GOSSIP_INTERVAL_DEFAULT_MS,
                 suspicion_timeout_protocol_periods: SUSPICION_TIMEOUT_DEFAULT_PROTOCOL_PERIODS,
                 departure_timeout_ms: DEPARTURE_TIMEOUT_DEFAULT_MS, }
    }
}

impl Timing {
    /// How long a gossip period should last.
    fn gossip_interval(&self) -> Duration { Duration::from_millis(self.gossip_interval_ms) }

    /// How long is a protocol period, in millis.
    pub fn protocol_period_ms(&self) -> u64 { self.ping_ms + self.pingreq_ms }

    /// How long a ping has to timeout.
    pub fn ping(&self) -> Duration { Duration::from_millis(self.ping_ms) }

    /// How long a pingreq has to timeout.
    pub fn pingreq(&self) -> Duration { Duration::from_millis(self.pingreq_ms) }

    /// How long to space out individual SWIM probe rounds
    fn swim_probe_interval(&self) -> Duration {
        Duration::from_millis(self.ping_ms + self.pingreq_ms)
    }

    /// If the amount of time since `starting_point` is less than a
    /// gossip interval, sleep for the remainder of that gossip interval.
    pub fn sleep_for_remaining_gossip_interval(&self, starting_point: Instant) {
        maybe_sleep(starting_point, self.gossip_interval())
    }

    /// How long before this suspect entry times out
    pub fn suspicion_timeout_duration(&self) -> Duration {
        Duration::from_millis(self.protocol_period_ms() * self.suspicion_timeout_protocol_periods)
    }

    /// If the amount of time since `starting_point` is less than a
    /// SWIM protocol probe interval, sleep for the remainder of that
    /// interval.
    pub fn sleep_for_remaining_swim_protocol_interval(&self, starting_point: Instant) {
        maybe_sleep(starting_point, self.swim_probe_interval())
    }

    pub fn departure_timeout_duration(&self) -> Duration {
        Duration::from_millis(self.departure_timeout_ms)
    }
}

/// If the amount of time elapsed from `start` is less than `timeout`,
/// sleep for the difference.
fn maybe_sleep(start: Instant, timeout: Duration) {
    if let Some(amount) = timeout.checked_sub(start.elapsed()) {
        thread::sleep(amount)
    }
}
<commit_msg>Timing: Add `ping` field as a Duration<commit_after>use std::{thread,
          time::{Duration,
                 Instant}};

/// How long to wait for an Ack after we ping
const PING_TIMING_DEFAULT_MS: u64 = 1000;
/// How long to wait for an Ack after we PingReq - should be at least 2x the PING_TIMING_DEFAULT_MS
const PINGREQ_TIMING_DEFAULT_MS: u64 = 2100;
/// How many protocol periods before a suspect member is marked as confirmed.
const SUSPICION_TIMEOUT_DEFAULT_PROTOCOL_PERIODS: u64 = 3;
/// How long to wait between each time we send rumors out.
const GOSSIP_INTERVAL_DEFAULT_MS: u64 = 1000;
/// How long before we set a confirmed member to a departed member, removing them from quorums
///   just for your own sanity - this is 3 days.
const DEPARTURE_TIMEOUT_DEFAULT_MS: u64 = 259_200_000;

/// The timing of the outbound threads.
#[derive(Debug, Clone)]
pub struct Timing {
    ping: Duration,
    pingreq_ms: u64,
    gossip_interval_ms: u64,
    suspicion_timeout_protocol_periods: u64,
    departure_timeout_ms: u64,
}

impl Default for Timing {
    fn default() -> Timing {
        Timing { ping: Duration::from_millis(PING_TIMING_DEFAULT_MS),
                 pingreq_ms: PINGREQ_TIMING_DEFAULT_MS,
                 gossip_interval_ms: GOSSIP_INTERVAL_DEFAULT_MS,
                 suspicion_timeout_protocol_periods: SUSPICION_TIMEOUT_DEFAULT_PROTOCOL_PERIODS,
                 departure_timeout_ms: DEPARTURE_TIMEOUT_DEFAULT_MS, }
    }
}

impl Timing {
    /// How long a gossip period should last.
    fn gossip_interval(&self) -> Duration { Duration::from_millis(self.gossip_interval_ms) }

    /// How long is a protocol period, in millis.
    pub fn protocol_period_ms(&self) -> u64 { PING_TIMING_DEFAULT_MS + self.pingreq_ms }

    /// How long a ping has to timeout.
    pub fn ping(&self) -> Duration { self.ping }

    /// How long a pingreq has to timeout.
    pub fn pingreq(&self) -> Duration { Duration::from_millis(self.pingreq_ms) }

    /// How long to space out individual SWIM probe rounds
    fn swim_probe_interval(&self) -> Duration {
        Duration::from_millis(PING_TIMING_DEFAULT_MS + self.pingreq_ms)
    }

    /// If the amount of time since `starting_point` is less than a
    /// gossip interval, sleep for the remainder of that gossip interval.
    pub fn sleep_for_remaining_gossip_interval(&self, starting_point: Instant) {
        maybe_sleep(starting_point, self.gossip_interval())
    }

    /// How long before this suspect entry times out
    pub fn suspicion_timeout_duration(&self) -> Duration {
        Duration::from_millis(self.protocol_period_ms() * self.suspicion_timeout_protocol_periods)
    }

    /// If the amount of time since `starting_point` is less than a
    /// SWIM protocol probe interval, sleep for the remainder of that
    /// interval.
    pub fn sleep_for_remaining_swim_protocol_interval(&self, starting_point: Instant) {
        maybe_sleep(starting_point, self.swim_probe_interval())
    }

    pub fn departure_timeout_duration(&self) -> Duration {
        Duration::from_millis(self.departure_timeout_ms)
    }
}

/// If the amount of time elapsed from `start` is less than `timeout`,
/// sleep for the difference.
fn maybe_sleep(start: Instant, timeout: Duration) {
    if let Some(amount) = timeout.checked_sub(start.elapsed()) {
        thread::sleep(amount)
    }
}
<|endoftext|>


<commit_before>//! Tests for `include` config field.

use super::config::{assert_error, write_config, write_config_at, ConfigBuilder};
use cargo_test_support::{no_such_file_err_msg, paths, project};
use std::fs;

#[cargo_test]
fn gated() {
    // Requires -Z flag.
    write_config("include='other'");
    write_config_at(
        ".cargo/other",
        "
        othervalue = 1
        ",
    );
    let config = ConfigBuilder::new().build();
    assert_eq!(config.get::<Option<i32>>("othervalue").unwrap(), None);
    let config = ConfigBuilder::new().unstable_flag("config-include").build();
    assert_eq!(config.get::<i32>("othervalue").unwrap(), 1);
}

#[cargo_test]
fn simple() {
    // Simple test.
    write_config_at(
        ".cargo/config",
        "
        include = 'other'
        key1 = 1
        key2 = 2
        ",
    );
    write_config_at(
        ".cargo/other",
        "
        key2 = 3
        key3 = 4
        ",
    );
    let config = ConfigBuilder::new().unstable_flag("config-include").build();
    assert_eq!(config.get::<i32>("key1").unwrap(), 1);
    assert_eq!(config.get::<i32>("key2").unwrap(), 2);
    assert_eq!(config.get::<i32>("key3").unwrap(), 4);
}

#[cargo_test]
fn works_with_cli() {
    write_config_at(
        ".cargo/config.toml",
        "
        include = 'other.toml'
        [build]
        rustflags = ['-W', 'unused']
        ",
    );
    write_config_at(
        ".cargo/other.toml",
        "
        [build]
        rustflags = ['-W', 'unsafe-code']
        ",
    );
    let p = project().file("src/lib.rs", "").build();
    p.cargo("build -v")
        .with_stderr(
            "\
[COMPILING] foo v0.0.1 [..]
[RUNNING] `rustc [..]-W unused`
[FINISHED] [..]
",
        )
        .run();
    p.cargo("build -v -Z config-include")
        .masquerade_as_nightly_cargo()
        .with_stderr(
            "\
[COMPILING] foo v0.0.1 [..]
[RUNNING] `rustc [..]-W unsafe-code -W unused`
[FINISHED] [..]
",
        )
        .run();
}

#[cargo_test]
fn left_to_right() {
    // How it merges multiple includes.
    write_config_at(
        ".cargo/config",
        "
        include = ['one', 'two']
        primary = 1
        ",
    );
    write_config_at(
        ".cargo/one",
        "
        one = 1
        primary = 2
        ",
    );
    write_config_at(
        ".cargo/two",
        "
        two = 2
        primary = 3
        ",
    );
    let config = ConfigBuilder::new().unstable_flag("config-include").build();
    assert_eq!(config.get::<i32>("primary").unwrap(), 1);
    assert_eq!(config.get::<i32>("one").unwrap(), 1);
    assert_eq!(config.get::<i32>("two").unwrap(), 2);
}

#[cargo_test]
fn missing_file() {
    // Error when there's a missing file.
    write_config("include='missing'");
    let config = ConfigBuilder::new()
        .unstable_flag("config-include")
        .build_err();
    assert_error(
        config.unwrap_err(),
        &format!(
            "\
could not load Cargo configuration

Caused by:
  failed to load config include `missing` from `[..]/.cargo/config`

Caused by:
  failed to read configuration file `[..]/.cargo/missing`

Caused by:
  {}",
            no_such_file_err_msg()
        ),
    );
}

#[cargo_test]
fn cycle() {
    // Detects a cycle.
    write_config_at(".cargo/config", "include='one'");
    write_config_at(".cargo/one", "include='two'");
    write_config_at(".cargo/two", "include='config'");
    let config = ConfigBuilder::new()
        .unstable_flag("config-include")
        .build_err();
    assert_error(
        config.unwrap_err(),
        "\
could not load Cargo configuration

Caused by:
  failed to load config include `one` from `[..]/.cargo/config`

Caused by:
  failed to load config include `two` from `[..]/.cargo/one`

Caused by:
  failed to load config include `config` from `[..]/.cargo/two`

Caused by:
  config `include` cycle detected with path `[..]/.cargo/config`",
    );
}

#[cargo_test]
fn cli_include() {
    // Using --config with include.
    // CLI takes priority over files.
    write_config_at(
        ".cargo/config",
        "
        foo = 1
        bar = 2
        ",
    );
    write_config_at(".cargo/config-foo", "foo = 2");
    let config = ConfigBuilder::new()
        .unstable_flag("config-include")
        .config_arg("include='.cargo/config-foo'")
        .build();
    assert_eq!(config.get::<i32>("foo").unwrap(), 2);
    assert_eq!(config.get::<i32>("bar").unwrap(), 2);
}

#[cargo_test]
fn bad_format() {
    // Not a valid format.
    write_config("include = 1");
    let config = ConfigBuilder::new()
        .unstable_flag("config-include")
        .build_err();
    assert_error(
        config.unwrap_err(),
        "\
could not load Cargo configuration

Caused by:
  `include` expected a string or list, but found integer in `[..]/.cargo/config`",
    );
}

#[cargo_test]
fn cli_include_failed() {
    // Error message when CLI include fails to load.
    let config = ConfigBuilder::new()
        .unstable_flag("config-include")
        .config_arg("include='foobar'")
        .build_err();
    assert_error(
        config.unwrap_err(),
        &format!(
            "\
failed to load --config include

Caused by:
  failed to load config include `foobar` from `--config cli option`

Caused by:
  failed to read configuration file `[..]/foobar`

Caused by:
  {}",
            no_such_file_err_msg()
        ),
    );
}

#[cargo_test]
fn cli_merge_failed() {
    // Error message when CLI include merge fails.
    write_config("foo = ['a']");
    write_config_at(
        ".cargo/other",
        "
        foo = 'b'
        ",
    );
    let config = ConfigBuilder::new()
        .unstable_flag("config-include")
        .config_arg("include='.cargo/other'")
        .build_err();
    // Maybe this error message should mention it was from an include file?
    assert_error(
        config.unwrap_err(),
        "\
failed to merge --config key `foo` into `[..]/.cargo/config`

Caused by:
  failed to merge config value from `[..]/.cargo/other` into `[..]/.cargo/config`: \
  expected array, but found string",
    );
}

#[cargo_test]
fn cli_path() {
    // --config path_to_file
    fs::write(paths::root().join("myconfig.toml"), "key = 123").unwrap();
    let config = ConfigBuilder::new()
        .cwd(paths::root())
        .unstable_flag("config-include")
        .config_arg("myconfig.toml")
        .build();
    assert_eq!(config.get::<u32>("key").unwrap(), 123);

    let config = ConfigBuilder::new()
        .unstable_flag("config-include")
        .config_arg("missing.toml")
        .build_err();
    assert_error(
        config.unwrap_err(),
        "\
failed to parse --config argument `missing.toml`

Caused by:
  expected an equals, found eof at line 1 column 13",
    );
}
<commit_msg>Fix up config_include test<commit_after>//! Tests for `include` config field.

use super::config::{assert_error, write_config, write_config_at, ConfigBuilder};
use cargo_test_support::{no_such_file_err_msg, paths, project};
use std::fs;

#[cargo_test]
fn gated() {
    // Requires -Z flag.
    write_config("include='other'");
    write_config_at(
        ".cargo/other",
        "
        othervalue = 1
        ",
    );
    let config = ConfigBuilder::new().build();
    assert_eq!(config.get::<Option<i32>>("othervalue").unwrap(), None);
    let config = ConfigBuilder::new().unstable_flag("config-include").build();
    assert_eq!(config.get::<i32>("othervalue").unwrap(), 1);
}

#[cargo_test]
fn simple() {
    // Simple test.
    write_config_at(
        ".cargo/config",
        "
        include = 'other'
        key1 = 1
        key2 = 2
        ",
    );
    write_config_at(
        ".cargo/other",
        "
        key2 = 3
        key3 = 4
        ",
    );
    let config = ConfigBuilder::new().unstable_flag("config-include").build();
    assert_eq!(config.get::<i32>("key1").unwrap(), 1);
    assert_eq!(config.get::<i32>("key2").unwrap(), 2);
    assert_eq!(config.get::<i32>("key3").unwrap(), 4);
}

#[cargo_test]
fn works_with_cli() {
    write_config_at(
        ".cargo/config.toml",
        "
        include = 'other.toml'
        [build]
        rustflags = ['-W', 'unused']
        ",
    );
    write_config_at(
        ".cargo/other.toml",
        "
        [build]
        rustflags = ['-W', 'unsafe-code']
        ",
    );
    let p = project().file("src/lib.rs", "").build();
    p.cargo("build -v")
        .with_stderr(
            "\
[COMPILING] foo v0.0.1 [..]
[RUNNING] `rustc [..]-W unused`
[FINISHED] [..]
",
        )
        .run();
    p.cargo("build -v -Z config-include")
        .masquerade_as_nightly_cargo()
        .with_stderr(
            "\
[COMPILING] foo v0.0.1 [..]
[RUNNING] `rustc [..]-W unsafe-code -W unused`
[FINISHED] [..]
",
        )
        .run();
}

#[cargo_test]
fn left_to_right() {
    // How it merges multiple includes.
    write_config_at(
        ".cargo/config",
        "
        include = ['one', 'two']
        primary = 1
        ",
    );
    write_config_at(
        ".cargo/one",
        "
        one = 1
        primary = 2
        ",
    );
    write_config_at(
        ".cargo/two",
        "
        two = 2
        primary = 3
        ",
    );
    let config = ConfigBuilder::new().unstable_flag("config-include").build();
    assert_eq!(config.get::<i32>("primary").unwrap(), 1);
    assert_eq!(config.get::<i32>("one").unwrap(), 1);
    assert_eq!(config.get::<i32>("two").unwrap(), 2);
}

#[cargo_test]
fn missing_file() {
    // Error when there's a missing file.
    write_config("include='missing'");
    let config = ConfigBuilder::new()
        .unstable_flag("config-include")
        .build_err();
    assert_error(
        config.unwrap_err(),
        &format!(
            "\
could not load Cargo configuration

Caused by:
  failed to load config include `missing` from `[..]/.cargo/config`

Caused by:
  failed to read configuration file `[..]/.cargo/missing`

Caused by:
  {}",
            no_such_file_err_msg()
        ),
    );
}

#[cargo_test]
fn cycle() {
    // Detects a cycle.
    write_config_at(".cargo/config", "include='one'");
    write_config_at(".cargo/one", "include='two'");
    write_config_at(".cargo/two", "include='config'");
    let config = ConfigBuilder::new()
        .unstable_flag("config-include")
        .build_err();
    assert_error(
        config.unwrap_err(),
        "\
could not load Cargo configuration

Caused by:
  failed to load config include `one` from `[..]/.cargo/config`

Caused by:
  failed to load config include `two` from `[..]/.cargo/one`

Caused by:
  failed to load config include `config` from `[..]/.cargo/two`

Caused by:
  config `include` cycle detected with path `[..]/.cargo/config`",
    );
}

#[cargo_test]
fn cli_include() {
    // Using --config with include.
    // CLI takes priority over files.
    write_config_at(
        ".cargo/config",
        "
        foo = 1
        bar = 2
        ",
    );
    write_config_at(".cargo/config-foo", "foo = 2");
    let config = ConfigBuilder::new()
        .unstable_flag("config-include")
        .config_arg("include='.cargo/config-foo'")
        .build();
    assert_eq!(config.get::<i32>("foo").unwrap(), 2);
    assert_eq!(config.get::<i32>("bar").unwrap(), 2);
}

#[cargo_test]
fn bad_format() {
    // Not a valid format.
    write_config("include = 1");
    let config = ConfigBuilder::new()
        .unstable_flag("config-include")
        .build_err();
    assert_error(
        config.unwrap_err(),
        "\
could not load Cargo configuration

Caused by:
  `include` expected a string or list, but found integer in `[..]/.cargo/config`",
    );
}

#[cargo_test]
fn cli_include_failed() {
    // Error message when CLI include fails to load.
    let config = ConfigBuilder::new()
        .unstable_flag("config-include")
        .config_arg("include='foobar'")
        .build_err();
    assert_error(
        config.unwrap_err(),
        &format!(
            "\
failed to load --config include

Caused by:
  failed to load config include `foobar` from `--config cli option`

Caused by:
  failed to read configuration file `[..]/foobar`

Caused by:
  {}",
            no_such_file_err_msg()
        ),
    );
}

#[cargo_test]
fn cli_merge_failed() {
    // Error message when CLI include merge fails.
    write_config("foo = ['a']");
    write_config_at(
        ".cargo/other",
        "
        foo = 'b'
        ",
    );
    let config = ConfigBuilder::new()
        .unstable_flag("config-include")
        .config_arg("include='.cargo/other'")
        .build_err();
    // Maybe this error message should mention it was from an include file?
    assert_error(
        config.unwrap_err(),
        "\
failed to merge --config key `foo` into `[..]/.cargo/config`

Caused by:
  failed to merge config value from `[..]/.cargo/other` into `[..]/.cargo/config`: \
  expected array, but found string",
    );
}

#[cargo_test]
fn cli_path() {
    // --config path_to_file
    fs::write(paths::root().join("myconfig.toml"), "key = 123").unwrap();
    let config = ConfigBuilder::new()
        .cwd(paths::root())
        .unstable_flag("config-include")
        .config_arg("myconfig.toml")
        .build();
    assert_eq!(config.get::<u32>("key").unwrap(), 123);

    let config = ConfigBuilder::new()
        .unstable_flag("config-include")
        .config_arg("missing.toml")
        .build_err();
    assert_error(
        config.unwrap_err(),
        "\
failed to parse value from --config argument `missing.toml` as a dotted key expression

Caused by:
  TOML parse error at line 1, column 13
  |
1 | missing.toml
  |             ^
Unexpected `end of input`
Expected `.` or `=`",
    );
}
<|endoftext|>


<commit_before>extern crate mp4parse;

use std::env;
use std::fs::File;
use std::io::{Read, Take};
use std::thread;

fn limit<'a>(f: &'a mut File, h: &mp4parse::BoxHeader) -> Take<&'a mut File> {
    f.take(h.size - h.offset)
}

fn read_box(f: &mut File) {
    mp4parse::read_box_header(f).and_then(|h| {
        match &(mp4parse::fourcc_to_string(h.name))[..] {
            "ftyp" => {
                let mut content = limit(f, &h);
                mp4parse::read_ftyp(&mut content, &h).and_then(|ftyp| {
                    println!("{}", ftyp);
                    Some(ftyp)
                })
            },
            _ => {
                println!("{}", h);
                mp4parse::skip_box_content(f, &h);
                None
            },
        }
    });
}

fn dump_file(filename: String) {
    let mut f = File::open(filename).unwrap();
    let task = thread::spawn(move || {
        loop {
            read_box(&mut f);
        }
    });
    // Catch and ignore any panics in the thread.
    task.join().ok();
}

fn main() {
    for filename in env::args().skip(1) {
        println!("-- dump of '{}' --", filename);
        dump_file(filename);
    }
}
<commit_msg>Try recursing into moov boxes.<commit_after>extern crate mp4parse;

use std::env;
use std::fs::File;
use std::io::{Read, Seek, Take};
use std::io::Cursor;
use std::thread;

fn limit<'a, T: Read>(f: &'a mut T, h: &mp4parse::BoxHeader) -> Take<&'a mut T> {
    f.take(h.size - h.offset)
}

fn read_box<T: Read + Seek>(f: &mut T) {
    mp4parse::read_box_header(f).and_then(|h| {
        match &(mp4parse::fourcc_to_string(h.name))[..] {
            "ftyp" => {
                let mut content = limit(f, &h);
                mp4parse::read_ftyp(&mut content, &h).and_then(|ftyp| {
                    println!("{}", ftyp);
                    Some(ftyp)
                })
            },
            "moov" => {
                println!("{} -- recursing", h);
                let buf: Vec<u8> = limit(f, &h)
                    .bytes()
                    .map(|u| u.unwrap())
                    .collect();
                let mut content = Cursor::new(buf);
                read_box(&mut content);
                println!("{} -- end", h);
                None
            },
            _ => {
                println!("{}", h);
                mp4parse::skip_box_content(f, &h).unwrap();
                None
            },
        }
    });
}

fn dump_file(filename: String) {
    let mut f = File::open(filename).unwrap();
    let task = thread::spawn(move || {
        loop {
            read_box(&mut f);
        }
    });
    // Catch and ignore any panics in the thread.
    task.join().ok();
}

fn main() {
    for filename in env::args().skip(1) {
        println!("-- dump of '{}' --", filename);
        dump_file(filename);
    }
}
<|endoftext|>


<commit_before>#![allow(dead_code)]

#[macro_use]
extern crate bitfield;

use std::net::Ipv4Addr;

bitfield! {
    struct IpV4Header(MSB0 [u8]);
    impl Debug;
    u32;
    get_version, _: 3, 0;
    get_ihl, _: 7, 4;
    get_dscp, _: 13, 8;
    get_ecn, _: 15, 14;
    get_total_length, _: 31, 16;
    get_identification, _: 47, 31;
    get_df, _: 49;
    get_mf, _: 50;
    get_fragment_offset, _: 63, 51;
    get_time_to_live, _: 71, 64;
    get_protocol, _: 79, 72;
    get_header_checksum, _: 95, 79;
    u8, get_source_address, _: 103, 96, 4;
    u32, into Ipv4Addr, get_destination_address, _: 159, 128;
}

impl<T: AsRef<[u8]> + AsMut<[u8]>> IpV4Header<T> {
    fn get_source_as_ip_addr(&self) -> Ipv4Addr {
        let mut src = [0; 4];
        for (i, src) in src.iter_mut().enumerate() {
            *src = self.get_source_address(i);
        }
        src.into()
    }
}

fn main() {
    let data = [
        0x45, 0x00, 0x00, 0x40, 0x69, 0x27, 0x40, 0x00, 0x40, 0x11, 0x4d, 0x0d, 0xc0, 0xa8, 0x01,
        0x2a, 0xc0, 0xa8, 0x01, 0xfe,
    ];

    let header = IpV4Header(data);

    assert_eq!(header.get_version(), 4);
    assert_eq!(header.get_total_length(), 64);
    assert_eq!(header.get_identification(), 0x6927);
    assert!(header.get_df());
    assert!(!header.get_mf());
    assert_eq!(header.get_fragment_offset(), 0);
    assert_eq!(header.get_protocol(), 0x11);
    println!(
        "from {} to {}",
        header.get_source_as_ip_addr(),
        header.get_destination_address()
    );

    println!("{:#?}", header);
}
<commit_msg>Fix bitfield positions<commit_after>#![allow(dead_code)]

#[macro_use]
extern crate bitfield;

use std::net::Ipv4Addr;

bitfield! {
    struct IpV4Header(MSB0 [u8]);
    impl Debug;
    u32;
    get_version, _: 3, 0;
    get_ihl, _: 7, 4;
    get_dscp, _: 13, 8;
    get_ecn, _: 15, 14;
    get_total_length, _: 31, 16;
    get_identification, _: 47, 32;
    get_df, _: 49;
    get_mf, _: 50;
    get_fragment_offset, _: 63, 51;
    get_time_to_live, _: 71, 64;
    get_protocol, _: 79, 72;
    get_header_checksum, _: 95, 80;
    u8, get_source_address, _: 103, 96, 4;
    u32, into Ipv4Addr, get_destination_address, _: 159, 128;
}

impl<T: AsRef<[u8]> + AsMut<[u8]>> IpV4Header<T> {
    fn get_source_as_ip_addr(&self) -> Ipv4Addr {
        let mut src = [0; 4];
        for (i, src) in src.iter_mut().enumerate() {
            *src = self.get_source_address(i);
        }
        src.into()
    }
}

fn main() {
    let data = [
        0x45, 0x00, 0x00, 0x40, 0x69, 0x27, 0x40, 0x00, 0x40, 0x11, 0x4d, 0x0d, 0xc0, 0xa8, 0x01,
        0x2a, 0xc0, 0xa8, 0x01, 0xfe,
    ];

    let header = IpV4Header(data);

    assert_eq!(header.get_version(), 4);
    assert_eq!(header.get_total_length(), 64);
    assert_eq!(header.get_identification(), 0x6927);
    assert!(header.get_df());
    assert!(!header.get_mf());
    assert_eq!(header.get_fragment_offset(), 0);
    assert_eq!(header.get_protocol(), 0x11);
    println!(
        "from {} to {}",
        header.get_source_as_ip_addr(),
        header.get_destination_address()
    );

    println!("{:#?}", header);
}
<|endoftext|>


<commit_before>extern crate termion;

#[cfg(feature = "nightly")]
fn main() {
    use termion::{TermRead, TermWrite, IntoRawMode, Key};
    use std::io::{Write, stdout, stdin};

    let stdin = stdin();
    let mut stdout = stdout().into_raw_mode().unwrap();

    stdout.clear().unwrap();
    stdout.goto(0, 0).unwrap();
    stdout.write(b"q to exit. Type stuff, use alt, and so on.").unwrap();
    stdout.hide_cursor().unwrap();
    stdout.flush().unwrap();

    for c in stdin.keys() {
        stdout.goto(5, 5).unwrap();
        stdout.clear_line().unwrap();
        match c {
            Key::Char('q') => break,
            Key::Char(c) => println!("{}", c),
            Key::Alt(c) => println!("^{}", c),
            Key::Ctrl(c) => println!("*{}", c),
            Key::Left => println!("←"),
            Key::Right => println!("→"),
            Key::Up => println!("↑"),
            Key::Down => println!("↓"),
            Key::Backspace => println!("×"),
            Key::Invalid => println!("???"),
            Key::Error => println!("ERROR"),
            _ => {},
        }
        stdout.flush().unwrap();
    }

    stdout.show_cursor().unwrap();
}
#[cfg(not(feature = "nightly"))]
fn main() {
    println!("To run this example, you need to enable the `nightly` feature. Use Rust nightly and compile with `--features nightly`.")
}
<commit_msg>Fixed keys example<commit_after>extern crate termion;

#[cfg(feature = "nightly")]
fn main() {
    use termion::{TermRead, TermWrite, IntoRawMode, Key};
    use std::io::{Write, stdout, stdin};

    let stdin = stdin();
    let mut stdout = stdout().into_raw_mode().unwrap();

    stdout.clear().unwrap();
    stdout.goto(0, 0).unwrap();
    stdout.write(b"q to exit. Type stuff, use alt, and so on.").unwrap();
    stdout.hide_cursor().unwrap();
    stdout.flush().unwrap();

    for c in stdin.keys() {
        stdout.goto(5, 5).unwrap();
        stdout.clear_line().unwrap();
        match c {
            Key::Char('q') => break,
            Key::Char(c) => println!("{}", c),
            Key::Alt(c) => println!("^{}", c),
            Key::Ctrl(c) => println!("*{}", c),
            Key::Left => println!("←"),
            Key::Right => println!("→"),
            Key::Up => println!("↑"),
            Key::Down => println!("↓"),
            Key::Backspace => println!("×"),
            Key::Invalid => println!("???"),
            Key::Error(_) => println!("ERROR"),
            _ => {},
        }
        stdout.flush().unwrap();
    }

    stdout.show_cursor().unwrap();
}
#[cfg(not(feature = "nightly"))]
fn main() {
    println!("To run this example, you need to enable the `nightly` feature. Use Rust nightly and compile with `--features nightly`.")
}
<|endoftext|>


<commit_before>extern crate hound;

use std::env;

fn main() {
    let fname = env::args().nth(1).expect("no file given");
    let mut reader = hound::WavReader::open(fname).unwrap();
    let (sig, unsig, n) = reader.samples::<i16>()
                             .fold((0_f64, 0_f64, 0_u32), |(sig, unsig, n), s| {
        let sample = s.unwrap();
        let sig = sig + sample as f64;
        let unsig = unsig + (sample as u16) as f64;
        (sig, unsig, n + 1)
    });
    println!("mean signed: {}\nmean unsigned: {}", sig / n as f64, unsig / n as f64);

    let mut reader = hound::WavReader::open(fname).unwrap();
    let (dsig, dunsig, n) = reader.samples::<i16>()
}
<commit_msg>add rms computation to mean example<commit_after>extern crate hound;

use std::env;

fn main() {
    let fname = env::args().nth(1).expect("no file given");
    let mut reader = hound::WavReader::open(&fname).unwrap();
    let samples: Vec<i16> = reader.samples().map(|s| s.unwrap()).collect();
    let (sig, unsig, n) = samples.iter().fold((0_f64, 0_f64, 0_u32), |(sig, unsig, n), &s| {
        let sample = s;
        let sig = sig + sample as f64;
        let unsig = unsig + (sample as u16) as f64;
        (sig, unsig, n + 1)
    });
    let ms = sig / n as f64;
    let mu = unsig / n as f64;
    println!("mean signed:   {}\nmean unsigned: {}", ms, mu);

    let (dsig, dunsig, n) = samples.iter().fold((0_f64, 0_f64, 0_u32), |(dsig, dunsig, n), &s| {
        let sample = s;
        let ds = sample as f64 - ms;
        let du = (sample as u16) as f64 - mu;
        (dsig + ds * ds, dunsig + du * du, n + 1)
    });
    let ds = dsig / n as f64;
    let du = dunsig / n as f64;
    println!("rms signed:    {}\nrms unsigned:  {}", ds.sqrt(), du.sqrt());
}
<|endoftext|>


<commit_before>use geometry::prims::TriangleOptions;
use geometry::{Mesh, Prim};
use image::GenericImage;
use material::materials::CookTorranceMaterial;
use raytracer::compositor::{Surface, ColorRGBA};
use std::fs::File;
use std::path::Path;
use std::io::{BufRead, BufReader};
use vec3::Vec3;

/// This is limited to only CookTorranceMaterials, as I couldn't get a Box<Material> to clone
/// a new material for each triangle primitive in the object model.
pub fn from_obj(material: CookTorranceMaterial, flip_normals: bool, filename: &str) -> Result<Mesh, String> {
    let file_handle = match File::open(&filename) {
        Ok(f) => f,
        Err(err) => return Err(format!("{}", err))
    };

    let total_bytes = match file_handle.metadata() {
        Ok(metadata) => metadata.len(),
        Err(err) => return Err(format!("{}", err))
    };

    let file = BufReader::new(file_handle);

    let start_time = ::time::get_time();
    let print_every = 2048u32;
    let mut current_line = 0;
    let mut processed_bytes = 0;

    let mut vertices: Vec<Vec3> = Vec::new();
    let mut normals : Vec<Vec3> = Vec::new();
    let mut triangles: Vec<Box<Prim+Send+Sync>> = Vec::new();
    let mut tex_coords: Vec<Vec<f64>> = Vec::new();
    let normal_scale = if flip_normals { -1.0 } else { 1.0 };

    for line_iter in file.lines() {
        let line = line_iter.unwrap();
        let tokens: Vec<&str> = line[..].split_whitespace().collect();
        if tokens.len() == 0 { continue }

        match tokens[0] {
            "v" => {
                vertices.push(Vec3 {
                    x: tokens[1].parse().unwrap(),
                    y: tokens[2].parse().unwrap(),
                    z: tokens[3].parse().unwrap()
                });
            },
            "vt" => {
                tex_coords.push(vec![
                    tokens[1].parse().unwrap(),
                    tokens[2].parse().unwrap()
                ]);
            },
            "vn" => {
                normals.push(Vec3 {
                    x: tokens[1].parse::<f64>().unwrap() * normal_scale,
                    y: tokens[2].parse::<f64>().unwrap() * normal_scale,
                    z: tokens[3].parse::<f64>().unwrap() * normal_scale
                });
            },
            "f" => {
                // ["f", "1/2/3", "2/2/2", "12//4"] => [[1, 2, 3], [2, 2, 2], [12, -1u, 4]]
                let tail = match tokens.split_first() {
                    Some((_, tail)) => tail,
                    None => return Err("Face syntax of OBJ not supported or malformed".to_owned())
                };

                let pairs: Vec<Vec<usize>> = tail.iter().map( |token| {
                    let str_tokens: Vec<&str> = token.split('/').collect();
                    str_tokens.iter().map( |str_tok| {
                        match str_tok.parse::<usize>().ok() {
                            Some(usize_tok) => usize_tok - 1, // Have to offset as OBJ is 1-indexed
                            None => !0 // No data available/not supplied (eg. `//` as a token)
                        }
                    }).collect()
                }).collect();

                // If no texture coordinates were supplied, default to zero.
                // We stored nothing supplied as !0
                let (u, v) = if pairs[0][1] != !0 {
                    (vec![
                        tex_coords[pairs[0][1]][0],
                        tex_coords[pairs[1][1]][0],
                        tex_coords[pairs[2][1]][0]
                    ],
                    vec![
                        tex_coords[pairs[0][1]][1],
                        tex_coords[pairs[1][1]][1],
                        tex_coords[pairs[2][1]][1]
                    ])
                } else {
                    (vec![0.0, 0.0, 0.0],
                     vec![0.0, 0.0, 0.0])
                };

                let mut triopts = TriangleOptions::new(
                    vertices[pairs[0][0]],
                    vertices[pairs[1][0]],
                    vertices[pairs[2][0]]);

                triopts.material(Box::new(material.clone()));
                triopts.normals([
                    normals[pairs[0][2]],
                    normals[pairs[1][2]],
                    normals[pairs[2][2]],
                ]);
                triopts.texinfo([(u[0], v[0]), (u[1], v[1]), (u[2], v[2])]);

                triangles.push(Box::new(triopts.build()));
            },
            _ => {}
        }

        current_line += 1;
        processed_bytes += line.as_bytes().len();
        if current_line % print_every == 0 {
            ::util::print_progress("Bytes", start_time.clone(), processed_bytes, total_bytes as usize);
        }
    }

    // Cheat the progress meter
    ::util::print_progress("Bytes", start_time, total_bytes as usize, total_bytes as usize);

    Ok(Mesh { triangles: triangles })
}

pub fn from_image<P: AsRef<Path>>(path: P) -> Result<Surface, String> {
    let image = match ::image::open(path) {
        Ok(image) => image.to_rgba(),
        Err(err) => return Err(format!("{}", err))
    };

    let mut surface = Surface::new(image.width() as usize,
                                   image.height() as usize,
                                   ColorRGBA::transparent());

    for (src, dst_pixel) in image.pixels().zip(surface.iter_pixels_mut()) {
        *dst_pixel = ColorRGBA::new_rgba(src[0], src[1], src[2], src[3]);
    }

    Ok(surface)
}

#[test]
pub fn test_obj_loads_correct_number_of_triangles() {
    let material: CookTorranceMaterial = Default::default();
    let mesh = from_obj(material, false, "test/res/cube.obj")
            .ok().expect("failed to laod test obj `test/res/cube.obj`");

    assert_eq!(mesh.triangles.len(), 12);
}

#[test]
pub fn test_from_png24() {
    let surface = from_image("test/res/png24.png")
            .ok().expect("failed to load test image `test/res/png24.png`");

    let expected_image: [[(u8, u8, u8, u8); 10]; 2] = [[
        (0, 0, 0, 255), (1, 1, 1, 255), (2, 2, 2, 255),
        (3, 3, 3, 255), (4, 4, 4, 255), (5, 5, 5, 255),
        (6, 6, 6, 255), (7, 7, 7, 255), (8, 8, 8, 255),
        (9, 9, 9, 255)
    ], [
        (255, 0, 0, 255), (255, 0, 0, 127), (255, 0, 0, 0),
        (0, 255, 0, 255), (0, 255, 0, 127), (0, 255, 0, 0),
        (0, 0, 255, 255), (0, 0, 255, 127), (0, 0, 255, 0),
        (0, 0, 0, 0)
    ]];

    for y in (0..1) {
        for x in (0..9) {
            let pixel = surface[(x, y)];
            let expected = expected_image[y][x];
            assert_eq!(expected, (pixel.r, pixel.g, pixel.b, pixel.a));
        }
    }
}
<commit_msg>Track Rust nightly<commit_after>use geometry::prims::TriangleOptions;
use geometry::{Mesh, Prim};
use image::GenericImage;
use material::materials::CookTorranceMaterial;
use raytracer::compositor::{Surface, ColorRGBA};
use std::fs::File;
use std::path::Path;
use std::io::{BufRead, BufReader};
use vec3::Vec3;

/// This is limited to only CookTorranceMaterials, as I couldn't get a Box<Material> to clone
/// a new material for each triangle primitive in the object model.
pub fn from_obj(material: CookTorranceMaterial, flip_normals: bool, filename: &str) -> Result<Mesh, String> {
    let file_handle = match File::open(&filename) {
        Ok(f) => f,
        Err(err) => return Err(format!("{}", err))
    };

    let total_bytes = match file_handle.metadata() {
        Ok(metadata) => metadata.len(),
        Err(err) => return Err(format!("{}", err))
    };

    let file = BufReader::new(file_handle);

    let start_time = ::time::get_time();
    let print_every = 2048u32;
    let mut current_line = 0;
    let mut processed_bytes = 0;

    let mut vertices: Vec<Vec3> = Vec::new();
    let mut normals : Vec<Vec3> = Vec::new();
    let mut triangles: Vec<Box<Prim+Send+Sync>> = Vec::new();
    let mut tex_coords: Vec<Vec<f64>> = Vec::new();
    let normal_scale = if flip_normals { -1.0 } else { 1.0 };

    for line_iter in file.lines() {
        let line = line_iter.unwrap();
        let tokens: Vec<&str> = line[..].split_whitespace().collect();
        if tokens.len() == 0 { continue }

        match tokens[0] {
            "v" => {
                vertices.push(Vec3 {
                    x: tokens[1].parse().unwrap(),
                    y: tokens[2].parse().unwrap(),
                    z: tokens[3].parse().unwrap()
                });
            },
            "vt" => {
                tex_coords.push(vec![
                    tokens[1].parse().unwrap(),
                    tokens[2].parse().unwrap()
                ]);
            },
            "vn" => {
                normals.push(Vec3 {
                    x: tokens[1].parse::<f64>().unwrap() * normal_scale,
                    y: tokens[2].parse::<f64>().unwrap() * normal_scale,
                    z: tokens[3].parse::<f64>().unwrap() * normal_scale
                });
            },
            "f" => {
                // ["f", "1/2/3", "2/2/2", "12//4"] => [[1, 2, 3], [2, 2, 2], [12, -1u, 4]]
                let tail = match tokens.split_first() {
                    Some((_, tail)) => tail,
                    None => return Err("Face syntax of OBJ not supported or malformed".to_owned())
                };

                let pairs: Vec<Vec<usize>> = tail.iter().map( |token| {
                    let str_tokens: Vec<&str> = token.split('/').collect();
                    str_tokens.iter().map( |str_tok| {
                        match str_tok.parse::<usize>().ok() {
                            Some(usize_tok) => usize_tok - 1, // Have to offset as OBJ is 1-indexed
                            None => !0 // No data available/not supplied (eg. `//` as a token)
                        }
                    }).collect()
                }).collect();

                // If no texture coordinates were supplied, default to zero.
                // We stored nothing supplied as !0
                let (u, v) = if pairs[0][1] != !0 {
                    (vec![
                        tex_coords[pairs[0][1]][0],
                        tex_coords[pairs[1][1]][0],
                        tex_coords[pairs[2][1]][0]
                    ],
                    vec![
                        tex_coords[pairs[0][1]][1],
                        tex_coords[pairs[1][1]][1],
                        tex_coords[pairs[2][1]][1]
                    ])
                } else {
                    (vec![0.0, 0.0, 0.0],
                     vec![0.0, 0.0, 0.0])
                };

                let mut triopts = TriangleOptions::new(
                    vertices[pairs[0][0]],
                    vertices[pairs[1][0]],
                    vertices[pairs[2][0]]);

                triopts.material(Box::new(material.clone()));
                triopts.normals([
                    normals[pairs[0][2]],
                    normals[pairs[1][2]],
                    normals[pairs[2][2]],
                ]);
                triopts.texinfo([(u[0], v[0]), (u[1], v[1]), (u[2], v[2])]);

                triangles.push(Box::new(triopts.build()));
            },
            _ => {}
        }

        current_line += 1;
        processed_bytes += line.as_bytes().len();
        if current_line % print_every == 0 {
            ::util::print_progress("Bytes", start_time.clone(), processed_bytes, total_bytes as usize);
        }
    }

    // Cheat the progress meter
    ::util::print_progress("Bytes", start_time, total_bytes as usize, total_bytes as usize);

    Ok(Mesh { triangles: triangles })
}

pub fn from_image<P: AsRef<Path>>(path: P) -> Result<Surface, String> {
    let image = match ::image::open(path) {
        Ok(image) => image.to_rgba(),
        Err(err) => return Err(format!("{}", err))
    };

    let mut surface = Surface::new(image.width() as usize,
                                   image.height() as usize,
                                   ColorRGBA::transparent());

    for (src, dst_pixel) in image.pixels().zip(surface.iter_pixels_mut()) {
        *dst_pixel = ColorRGBA::new_rgba(src[0], src[1], src[2], src[3]);
    }

    Ok(surface)
}

#[test]
pub fn test_obj_loads_correct_number_of_triangles() {
    let material: CookTorranceMaterial = Default::default();
    let mesh = from_obj(material, false, "test/res/cube.obj")
            .ok().expect("failed to laod test obj `test/res/cube.obj`");

    assert_eq!(mesh.triangles.len(), 12);
}

#[test]
pub fn test_from_png24() {
    let surface = from_image("test/res/png24.png")
            .ok().expect("failed to load test image `test/res/png24.png`");

    let expected_image: [[(u8, u8, u8, u8); 10]; 2] = [[
        (0, 0, 0, 255), (1, 1, 1, 255), (2, 2, 2, 255),
        (3, 3, 3, 255), (4, 4, 4, 255), (5, 5, 5, 255),
        (6, 6, 6, 255), (7, 7, 7, 255), (8, 8, 8, 255),
        (9, 9, 9, 255)
    ], [
        (255, 0, 0, 255), (255, 0, 0, 127), (255, 0, 0, 0),
        (0, 255, 0, 255), (0, 255, 0, 127), (0, 255, 0, 0),
        (0, 0, 255, 255), (0, 0, 255, 127), (0, 0, 255, 0),
        (0, 0, 0, 0)
    ]];

    for y in 0..1 {
        for x in 0..9 {
            let pixel = surface[(x, y)];
            let expected = expected_image[y][x];
            assert_eq!(expected, (pixel.r, pixel.g, pixel.b, pixel.a));
        }
    }
}
<|endoftext|>


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

use v2::{buffer, Extensions, Extras, Index};

/// [Defines a method for retrieving data from within a `BufferView`]
/// (https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#accessors)
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Accessor {
    /// The index of the parent `BufferView` this accessor reads from.
    #[serde(rename = "bufferView")]
    pub buffer_view: Index<buffer::View>,
    /// The offset relative to the start of the parent `BufferView` in bytes
    #[serde(rename = "byteOffset")]
    pub byte_offset: u32,
    /// The number of elements within the `BufferView` (N.B. not number of bytes)
    pub count: u32,
    /// The data type of each element
    #[serde(rename = "componentType")]
    pub component_type: ComponentType,
    /// Optional data targeting official extensions
    pub extensions: Extensions,
    /// Optional application specific data
    pub extras: Extras,
    /// The multiplicity of each element
    #[serde(rename = "type")]
    pub kind: Kind,
    /// Minimum value of each element in this attribute
    #[serde(default)]
    pub min: Vec<f32>,
    /// Maximum value of each element in this attribute
    #[serde(default)]
    pub max: Vec<f32>,
    /// Optional user-defined name for this object
    pub name: Option<String>,
    /// Specifies whether integer data values should be normalized
    #[serde(default)]
    pub normalized: bool,
    /// Sparse storage of attributes that deviate from their initialization value
    pub sparse: Option<SparseStorage>,
}

// TODO: Complete documentation
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct SparseIndices {
    /// The index of the parent `BufferView` containing the sparse indices
    #[serde(rename = "byteOffset")]
    pub buffer_view: Index<buffer::Buffer>,
    /// The offset relative to the start of the parent `BufferView` in bytes
    #[serde(default, rename = "byteOffset")]
    pub byte_offset: u32,
    /// The indices data type
    // N.B. Not all values are valid but it would be pedantic to have more than
    // one `Component` enum and would also create inconsistency with the regular
    // `accessor::Accessor` struct.
    pub component_type: ComponentType,
    /// Optional data targeting official extensions
    pub extensions: Extensions,
    /// Optional application specific data
    pub extras: Extras,
}

/// Sparse storage of attributes that deviate from their initialization value
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct SparseStorage {
    /// Number of entries stored in the sparse array
    pub count: u32,
    /// Optional data targeting official extensions
    pub extensions: Extensions,
    /// Optional application specific data
    pub extras: Extras,
    // TODO: Complete documentation
    pub indices: SparseIndices,
    // TODO: Complete documentation
    pub values: SparseValues,
}

// TODO: Complete documentation
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct SparseValues {
    /// The index of the parent `BufferView` containing the sparse values
    #[serde(rename = "byteOffset")]
    pub buffer_view: Index<buffer::View>,
    /// The offset relative to the start of the parent `BufferView` in bytes
    #[serde(default, rename = "byteOffset")]
    pub byte_offset: u32,
    /// Optional data targeting official extensions
    pub extensions: Extensions,
    /// Optional application specific data
    pub extras: Extras,
}

enum_number! {
    ComponentType {
        I8 = 5120,
        U8 = 5121,
        I16 = 5122,
        U16 = 5123,
        U32 = 5125,
        F32 = 5126,
    }
}

enum_string! {
    Kind {
        Scalar = "SCALAR",
        Vec2 = "VEC2",
        Vec3 = "VEC3",
        Vec4 = "VEC4",
        Mat2 = "MAT2",
        Mat3 = "MAT3",
        Mat4 = "MAT4",
    }
}
<commit_msg>Fixed unreachable pattern<commit_after>
// Copyright 2017 The gltf Library Developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use v2::{buffer, Extensions, Extras, Index};

/// [Defines a method for retrieving data from within a `BufferView`]
/// (https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#accessors)
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Accessor {
    /// The index of the parent `BufferView` this accessor reads from.
    #[serde(rename = "bufferView")]
    pub buffer_view: Index<buffer::View>,
    /// The offset relative to the start of the parent `BufferView` in bytes
    #[serde(rename = "byteOffset")]
    pub byte_offset: u32,
    /// The number of elements within the `BufferView` (N.B. not number of bytes)
    pub count: u32,
    /// The data type of each element
    #[serde(rename = "componentType")]
    pub component_type: ComponentType,
    /// Optional data targeting official extensions
    pub extensions: Extensions,
    /// Optional application specific data
    pub extras: Extras,
    /// The multiplicity of each element
    #[serde(rename = "type")]
    pub kind: Kind,
    /// Minimum value of each element in this attribute
    #[serde(default)]
    pub min: Vec<f32>,
    /// Maximum value of each element in this attribute
    #[serde(default)]
    pub max: Vec<f32>,
    /// Optional user-defined name for this object
    pub name: Option<String>,
    /// Specifies whether integer data values should be normalized
    #[serde(default)]
    pub normalized: bool,
    /// Sparse storage of attributes that deviate from their initialization value
    pub sparse: Option<SparseStorage>,
}

// TODO: Complete documentation
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct SparseIndices {
    /// The index of the parent `BufferView` containing the sparse indices
    #[serde(rename = "bufferView")]
    pub buffer_view: Index<buffer::Buffer>,
    /// The offset relative to the start of the parent `BufferView` in bytes
    #[serde(default, rename = "byteOffset")]
    pub byte_offset: u32,
    /// The indices data type
    // N.B. Not all values are valid but it would be pedantic to have more than
    // one `Component` enum and would also create inconsistency with the regular
    // `accessor::Accessor` struct.
    pub component_type: ComponentType,
    /// Optional data targeting official extensions
    pub extensions: Extensions,
    /// Optional application specific data
    pub extras: Extras,
}

/// Sparse storage of attributes that deviate from their initialization value
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct SparseStorage {
    /// Number of entries stored in the sparse array
    pub count: u32,
    /// Optional data targeting official extensions
    pub extensions: Extensions,
    /// Optional application specific data
    pub extras: Extras,
    // TODO: Complete documentation
    pub indices: SparseIndices,
    // TODO: Complete documentation
    pub values: SparseValues,
}

// TODO: Complete documentation
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct SparseValues {
    /// The index of the parent `BufferView` containing the sparse values
    #[serde(rename = "bufferView")]
    pub buffer_view: Index<buffer::View>,
    /// The offset relative to the start of the parent `BufferView` in bytes
    #[serde(default, rename = "byteOffset")]
    pub byte_offset: u32,
    /// Optional data targeting official extensions
    pub extensions: Extensions,
    /// Optional application specific data
    pub extras: Extras,
}

enum_number! {
    ComponentType {
        I8 = 5120,
        U8 = 5121,
        I16 = 5122,
        U16 = 5123,
        U32 = 5125,
        F32 = 5126,
    }
}

enum_string! {
    Kind {
        Scalar = "SCALAR",
        Vec2 = "VEC2",
        Vec3 = "VEC3",
        Vec4 = "VEC4",
        Mat2 = "MAT2",
        Mat3 = "MAT3",
        Mat4 = "MAT4",
    }
}
<|endoftext|>


<commit_before>//! Completes mod declarations.

use base_db::{SourceDatabaseExt, VfsPath};
use hir::{Module, ModuleSource};
use ide_db::RootDatabase;
use rustc_hash::FxHashSet;

use crate::{CompletionItem, CompletionItemKind};

use super::{
    completion_context::CompletionContext, completion_item::CompletionKind,
    completion_item::Completions,
};

/// Complete mod declaration, i.e. `mod <|> ;`
pub(super) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
    let mod_under_caret = match &ctx.mod_declaration_under_caret {
        Some(mod_under_caret) if mod_under_caret.item_list().is_some() => return None,
        Some(mod_under_caret) => mod_under_caret,
        None => return None,
    };

    let _p = profile::span("completion::complete_mod");

    let current_module = ctx.scope.module()?;

    let module_definition_file =
        current_module.definition_source(ctx.db).file_id.original_file(ctx.db);
    let source_root = ctx.db.source_root(ctx.db.file_source_root(module_definition_file));
    let directory_to_look_for_submodules = directory_to_look_for_submodules(
        current_module,
        ctx.db,
        source_root.path_for_file(&module_definition_file)?,
    )?;

    let existing_mod_declarations = current_module
        .children(ctx.db)
        .filter_map(|module| Some(module.name(ctx.db)?.to_string()))
        .collect::<FxHashSet<_>>();

    let module_declaration_file =
        current_module.declaration_source(ctx.db).map(|module_declaration_source_file| {
            module_declaration_source_file.file_id.original_file(ctx.db)
        });

    source_root
        .iter()
        .filter(|submodule_candidate_file| submodule_candidate_file != &module_definition_file)
        .filter(|submodule_candidate_file| {
            Some(submodule_candidate_file) != module_declaration_file.as_ref()
        })
        .filter_map(|submodule_file| {
            let submodule_path = source_root.path_for_file(&submodule_file)?;
            let directory_with_submodule = submodule_path.parent()?;
            match submodule_path.name_and_extension()? {
                ("lib", Some("rs")) | ("main", Some("rs")) => None,
                ("mod", Some("rs")) => {
                    if directory_with_submodule.parent()? == directory_to_look_for_submodules {
                        match directory_with_submodule.name_and_extension()? {
                            (directory_name, None) => Some(directory_name.to_owned()),
                            _ => None,
                        }
                    } else {
                        None
                    }
                }
                (file_name, Some("rs"))
                    if directory_with_submodule == directory_to_look_for_submodules =>
                {
                    Some(file_name.to_owned())
                }
                _ => None,
            }
        })
        .filter(|name| !existing_mod_declarations.contains(name))
        .for_each(|submodule_name| {
            let mut label = submodule_name;
            if mod_under_caret.semicolon_token().is_none() {
                label.push(';')
            }
            acc.add(
                CompletionItem::new(CompletionKind::Magic, ctx.source_range(), &label)
                    .kind(CompletionItemKind::Module),
            )
        });

    Some(())
}

fn directory_to_look_for_submodules(
    module: Module,
    db: &RootDatabase,
    module_file_path: &VfsPath,
) -> Option<VfsPath> {
    let directory_with_module_path = module_file_path.parent()?;
    let base_directory = match module_file_path.name_and_extension()? {
        ("mod", Some("rs")) | ("lib", Some("rs")) | ("main", Some("rs")) => {
            Some(directory_with_module_path)
        }
        (regular_rust_file_name, Some("rs")) => {
            if matches!(
                (
                    directory_with_module_path
                        .parent()
                        .as_ref()
                        .and_then(|path| path.name_and_extension()),
                    directory_with_module_path.name_and_extension(),
                ),
                (Some(("src", None)), Some(("bin", None)))
            ) {
                // files in /src/bin/ can import each other directly
                Some(directory_with_module_path)
            } else {
                directory_with_module_path.join(regular_rust_file_name)
            }
        }
        _ => None,
    }?;

    let mut resulting_path = base_directory;
    for module in module_chain_to_containing_module_file(module, db) {
        if let Some(name) = module.name(db) {
            resulting_path = resulting_path.join(&name.to_string())?;
        }
    }

    Some(resulting_path)
}

fn module_chain_to_containing_module_file(
    current_module: Module,
    db: &RootDatabase,
) -> Vec<Module> {
    let mut path = Vec::new();

    let mut current_module = Some(current_module);
    while let Some(ModuleSource::Module(_)) =
        current_module.map(|module| module.definition_source(db).value)
    {
        if let Some(module) = current_module {
            path.insert(0, module);
            current_module = module.parent(db);
        } else {
            current_module = None;
        }
    }

    path
}

#[cfg(test)]
mod tests {
    use crate::completion::{test_utils::completion_list, CompletionKind};
    use expect_test::{expect, Expect};

    fn check(ra_fixture: &str, expect: Expect) {
        let actual = completion_list(ra_fixture, CompletionKind::Magic);
        expect.assert_eq(&actual);
    }

    #[test]
    fn lib_module_completion() {
        check(
            r#"
            //- /lib.rs
            mod <|>
            //- /foo.rs
            fn foo() {}
            //- /foo/ignored_foo.rs
            fn ignored_foo() {}
            //- /bar/mod.rs
            fn bar() {}
            //- /bar/ignored_bar.rs
            fn ignored_bar() {}
        "#,
            expect![[r#"
                md bar;
                md foo;
            "#]],
        );
    }

    #[test]
    fn no_module_completion_with_module_body() {
        check(
            r#"
            //- /lib.rs
            mod <|> {

            }
            //- /foo.rs
            fn foo() {}
        "#,
            expect![[r#""#]],
        );
    }

    #[test]
    fn main_module_completion() {
        check(
            r#"
            //- /main.rs
            mod <|>
            //- /foo.rs
            fn foo() {}
            //- /foo/ignored_foo.rs
            fn ignored_foo() {}
            //- /bar/mod.rs
            fn bar() {}
            //- /bar/ignored_bar.rs
            fn ignored_bar() {}
        "#,
            expect![[r#"
                md bar;
                md foo;
            "#]],
        );
    }

    #[test]
    fn main_test_module_completion() {
        check(
            r#"
            //- /main.rs
            mod tests {
                mod <|>;
            }
            //- /tests/foo.rs
            fn foo() {}
        "#,
            expect![[r#"
                md foo
            "#]],
        );
    }

    #[test]
    fn directly_nested_module_completion() {
        check(
            r#"
            //- /lib.rs
            mod foo;
            //- /foo.rs
            mod <|>;
            //- /foo/bar.rs
            fn bar() {}
            //- /foo/bar/ignored_bar.rs
            fn ignored_bar() {}
            //- /foo/baz/mod.rs
            fn baz() {}
            //- /foo/moar/ignored_moar.rs
            fn ignored_moar() {}
        "#,
            expect![[r#"
                md bar
                md baz
            "#]],
        );
    }

    #[test]
    fn nested_in_source_module_completion() {
        check(
            r#"
            //- /lib.rs
            mod foo;
            //- /foo.rs
            mod bar {
                mod <|>
            }
            //- /foo/bar/baz.rs
            fn baz() {}
        "#,
            expect![[r#"
                md baz;
            "#]],
        );
    }

    // FIXME binart modules are not picked up in tests
    // #[test]
    // fn regular_bin_module_completion() {
    //     check(
    //         r#"
    //         //- /src/main.rs
    //         fn main() {}
    //         //- /src/main/foo.rs
    //         mod <|>
    //         //- /src/main/bar.rs
    //         fn bar() {}
    //         //- /src/main/bar/bar_ignored.rs
    //         fn bar_ignored() {}
    //     "#,
    //         expect![[r#"
    //             md bar;
    //         "#]],
    //     );
    // }

    #[test]
    fn already_declared_bin_module_completion_omitted() {
        check(
            r#"
            //- /src/main.rs
            fn main() {}
            //- /src/main/foo.rs
            mod <|>
            //- /src/main/bar.rs
            mod foo;
            fn bar() {}
            //- /src/main/bar/bar_ignored.rs
            fn bar_ignored() {}
        "#,
            expect![[r#""#]],
        );
    }
}
<commit_msg>Adjust the test comment<commit_after>//! Completes mod declarations.

use base_db::{SourceDatabaseExt, VfsPath};
use hir::{Module, ModuleSource};
use ide_db::RootDatabase;
use rustc_hash::FxHashSet;

use crate::{CompletionItem, CompletionItemKind};

use super::{
    completion_context::CompletionContext, completion_item::CompletionKind,
    completion_item::Completions,
};

/// Complete mod declaration, i.e. `mod <|> ;`
pub(super) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
    let mod_under_caret = match &ctx.mod_declaration_under_caret {
        Some(mod_under_caret) if mod_under_caret.item_list().is_some() => return None,
        Some(mod_under_caret) => mod_under_caret,
        None => return None,
    };

    let _p = profile::span("completion::complete_mod");

    let current_module = ctx.scope.module()?;

    let module_definition_file =
        current_module.definition_source(ctx.db).file_id.original_file(ctx.db);
    let source_root = ctx.db.source_root(ctx.db.file_source_root(module_definition_file));
    let directory_to_look_for_submodules = directory_to_look_for_submodules(
        current_module,
        ctx.db,
        source_root.path_for_file(&module_definition_file)?,
    )?;

    let existing_mod_declarations = current_module
        .children(ctx.db)
        .filter_map(|module| Some(module.name(ctx.db)?.to_string()))
        .collect::<FxHashSet<_>>();

    let module_declaration_file =
        current_module.declaration_source(ctx.db).map(|module_declaration_source_file| {
            module_declaration_source_file.file_id.original_file(ctx.db)
        });

    source_root
        .iter()
        .filter(|submodule_candidate_file| submodule_candidate_file != &module_definition_file)
        .filter(|submodule_candidate_file| {
            Some(submodule_candidate_file) != module_declaration_file.as_ref()
        })
        .filter_map(|submodule_file| {
            let submodule_path = source_root.path_for_file(&submodule_file)?;
            let directory_with_submodule = submodule_path.parent()?;
            match submodule_path.name_and_extension()? {
                ("lib", Some("rs")) | ("main", Some("rs")) => None,
                ("mod", Some("rs")) => {
                    if directory_with_submodule.parent()? == directory_to_look_for_submodules {
                        match directory_with_submodule.name_and_extension()? {
                            (directory_name, None) => Some(directory_name.to_owned()),
                            _ => None,
                        }
                    } else {
                        None
                    }
                }
                (file_name, Some("rs"))
                    if directory_with_submodule == directory_to_look_for_submodules =>
                {
                    Some(file_name.to_owned())
                }
                _ => None,
            }
        })
        .filter(|name| !existing_mod_declarations.contains(name))
        .for_each(|submodule_name| {
            let mut label = submodule_name;
            if mod_under_caret.semicolon_token().is_none() {
                label.push(';')
            }
            acc.add(
                CompletionItem::new(CompletionKind::Magic, ctx.source_range(), &label)
                    .kind(CompletionItemKind::Module),
            )
        });

    Some(())
}

fn directory_to_look_for_submodules(
    module: Module,
    db: &RootDatabase,
    module_file_path: &VfsPath,
) -> Option<VfsPath> {
    let directory_with_module_path = module_file_path.parent()?;
    let base_directory = match module_file_path.name_and_extension()? {
        ("mod", Some("rs")) | ("lib", Some("rs")) | ("main", Some("rs")) => {
            Some(directory_with_module_path)
        }
        (regular_rust_file_name, Some("rs")) => {
            if matches!(
                (
                    directory_with_module_path
                        .parent()
                        .as_ref()
                        .and_then(|path| path.name_and_extension()),
                    directory_with_module_path.name_and_extension(),
                ),
                (Some(("src", None)), Some(("bin", None)))
            ) {
                // files in /src/bin/ can import each other directly
                Some(directory_with_module_path)
            } else {
                directory_with_module_path.join(regular_rust_file_name)
            }
        }
        _ => None,
    }?;

    let mut resulting_path = base_directory;
    for module in module_chain_to_containing_module_file(module, db) {
        if let Some(name) = module.name(db) {
            resulting_path = resulting_path.join(&name.to_string())?;
        }
    }

    Some(resulting_path)
}

fn module_chain_to_containing_module_file(
    current_module: Module,
    db: &RootDatabase,
) -> Vec<Module> {
    let mut path = Vec::new();

    let mut current_module = Some(current_module);
    while let Some(ModuleSource::Module(_)) =
        current_module.map(|module| module.definition_source(db).value)
    {
        if let Some(module) = current_module {
            path.insert(0, module);
            current_module = module.parent(db);
        } else {
            current_module = None;
        }
    }

    path
}

#[cfg(test)]
mod tests {
    use crate::completion::{test_utils::completion_list, CompletionKind};
    use expect_test::{expect, Expect};

    fn check(ra_fixture: &str, expect: Expect) {
        let actual = completion_list(ra_fixture, CompletionKind::Magic);
        expect.assert_eq(&actual);
    }

    #[test]
    fn lib_module_completion() {
        check(
            r#"
            //- /lib.rs
            mod <|>
            //- /foo.rs
            fn foo() {}
            //- /foo/ignored_foo.rs
            fn ignored_foo() {}
            //- /bar/mod.rs
            fn bar() {}
            //- /bar/ignored_bar.rs
            fn ignored_bar() {}
        "#,
            expect![[r#"
                md bar;
                md foo;
            "#]],
        );
    }

    #[test]
    fn no_module_completion_with_module_body() {
        check(
            r#"
            //- /lib.rs
            mod <|> {

            }
            //- /foo.rs
            fn foo() {}
        "#,
            expect![[r#""#]],
        );
    }

    #[test]
    fn main_module_completion() {
        check(
            r#"
            //- /main.rs
            mod <|>
            //- /foo.rs
            fn foo() {}
            //- /foo/ignored_foo.rs
            fn ignored_foo() {}
            //- /bar/mod.rs
            fn bar() {}
            //- /bar/ignored_bar.rs
            fn ignored_bar() {}
        "#,
            expect![[r#"
                md bar;
                md foo;
            "#]],
        );
    }

    #[test]
    fn main_test_module_completion() {
        check(
            r#"
            //- /main.rs
            mod tests {
                mod <|>;
            }
            //- /tests/foo.rs
            fn foo() {}
        "#,
            expect![[r#"
                md foo
            "#]],
        );
    }

    #[test]
    fn directly_nested_module_completion() {
        check(
            r#"
            //- /lib.rs
            mod foo;
            //- /foo.rs
            mod <|>;
            //- /foo/bar.rs
            fn bar() {}
            //- /foo/bar/ignored_bar.rs
            fn ignored_bar() {}
            //- /foo/baz/mod.rs
            fn baz() {}
            //- /foo/moar/ignored_moar.rs
            fn ignored_moar() {}
        "#,
            expect![[r#"
                md bar
                md baz
            "#]],
        );
    }

    #[test]
    fn nested_in_source_module_completion() {
        check(
            r#"
            //- /lib.rs
            mod foo;
            //- /foo.rs
            mod bar {
                mod <|>
            }
            //- /foo/bar/baz.rs
            fn baz() {}
        "#,
            expect![[r#"
                md baz;
            "#]],
        );
    }

    // FIXME binary modules are not supported in tests properly
    // Binary modules are a bit special, they allow importing the modules from `/src/bin`
    // and that's why are good to test two things:
    // * no cycles are allowed in mod declarations
    // * no modules from the parent directory are proposed
    // Unfortunately, binary modules support is in cargo not rustc,
    // hence the test does not work now
    //
    // #[test]
    // fn regular_bin_module_completion() {
    //     check(
    //         r#"
    //         //- /src/bin.rs
    //         fn main() {}
    //         //- /src/bin/foo.rs
    //         mod <|>
    //         //- /src/bin/bar.rs
    //         fn bar() {}
    //         //- /src/bin/bar/bar_ignored.rs
    //         fn bar_ignored() {}
    //     "#,
    //         expect![[r#"
    //             md bar;
    //         "#]],
    //     );
    // }

    #[test]
    fn already_declared_bin_module_completion_omitted() {
        check(
            r#"
            //- /src/bin.rs
            fn main() {}
            //- /src/bin/foo.rs
            mod <|>
            //- /src/bin/bar.rs
            mod foo;
            fn bar() {}
            //- /src/bin/bar/bar_ignored.rs
            fn bar_ignored() {}
        "#,
            expect![[r#""#]],
        );
    }
}
<|endoftext|>