<commit_before>use std::time::Instant;

#[cfg(not(any(feature = "test", test)))]
use interpolation::lerp;

use radians::{self, Radians};
use state::{TurtleState, Path};
#[cfg(not(any(feature = "test", test)))]
use extensions::AsMillis;

pub trait Animation {
    /// Advance the animation forward.
    ///
    /// The animation will use the timer it stores to calculate the current state it should be at.
    fn advance(&self, turtle: &mut TurtleState) -> AnimationStatus;
}

// During tests, we disable animations and so it appears that this is dead code when it is not
// dead code in reality
// See Cargo.toml for an explanation of this attribute
#[cfg_attr(any(feature = "test", test), allow(dead_code))]
pub enum AnimationStatus {
    /// Returned if the animation is still running.
    ///
    /// If a Path is provided, it should be rendered.
    /// Make sure to check whether the path's pen is enabled before rendering.
    Running(Option<Path>),
    /// Returned if the animation is complete.
    /// The advance() function should not be called anymore after this.
    /// The returned path, if any, should be added to the list of paths to be rendered every frame.
    /// Make sure to check whether the path's pen is enabled before rendering.
    Complete(Option<Path>),
}

/// Rotates given angle in the given direction by the given rotation
///
/// Let's say you have a starting angle X. Standard angles go counterclockwise, so
/// if clockwise is true, we need to subtract the `rotation` from X resulting in
/// `X - rotation`. If clockwise is false, we can just add normally.
fn rotate(angle: Radians, rotation: Radians, clockwise: bool) -> Radians {
    let angle = if clockwise { angle - rotation } else { angle + rotation };
    // Normalize the angle to be between 0 and 2*pi
    // Formula adapted from: https://stackoverflow.com/a/24234924/551904
    // More info: https://stackoverflow.com/a/28316446/551904
    angle - radians::TWO_PI * (angle / radians::TWO_PI).floor()
}

/// Animate the turtle moving from a start point to an end point
/// while keeping its heading the same throughout (in a straight line)
pub struct MoveAnimation {
    /// path.start and path.end are used in lerp
    pub path: Path,
    /// timer since the start of the animation
    ///
    /// used with total_millis to calculate t in lerp
    pub timer: Instant,
    /// the total time the animation is meant to take based on the speed and length from
    /// start to finish
    ///
    /// if this is zero, we'll jump to the end
    pub total_millis: f64,
}

impl Animation for MoveAnimation {
    /// Advance the animation forward.
    ///
    /// The animation will use the timer it stores to calculate the current state it should be at.
    // See Cargo.toml for an explanation of this attribute
    #[cfg(not(any(feature = "test", test)))]
    fn advance(&self, turtle: &mut TurtleState) -> AnimationStatus {
        use self::AnimationStatus::*;

        let MoveAnimation {ref path, ref timer, total_millis} = *self;
        let elapsed = timer.elapsed().as_millis() as f64;
        if elapsed >= total_millis {
            turtle.position = path.end;
            Complete(Some(path.clone()))
        }
        else {
            // t is the total progress made in the animation so far
            let t = elapsed / total_millis;
            turtle.position = lerp(&path.start, &path.end, &t);

            Running(Some(Path {
                start: path.start,
                end: turtle.position,
                pen: path.pen.clone(),
            }))
        }
    }

    // See Cargo.toml for an explanation of this attribute
    #[cfg(any(feature = "test", test))]
    fn advance(&self, turtle: &mut TurtleState) -> AnimationStatus {
        use self::AnimationStatus::*;

        // No animation during testing
        let MoveAnimation {ref path, ..} = *self;
        turtle.position = path.end;
        Complete(Some(path.clone()))
    }
}

/// Rotate the turtle in place by the given angle in the direction specified
pub struct RotateAnimation {
    /// turtle.heading when the animation was started
    pub start: Radians,
    /// The total angle to move by.
    ///
    /// Linearly interpolated based on the elapsed time to get the delta that should be added
    /// to start to get the current heading in the animation
    pub delta_angle: Radians,
    /// The direction of rotation
    pub clockwise: bool,
    /// timer since the start of the animation
    ///
    /// used with total_millis to calculate t in lerp
    pub timer: Instant,
    /// the total time the animation is meant to take based on the speed and length from
    /// start to finish
    ///
    /// if this is zero, we'll jump to the end
    pub total_millis: f64,
}

impl Animation for RotateAnimation {
    /// Advance the animation forward.
    ///
    /// The animation will use the timer it stores to calculate the current state it should be at.
    // See Cargo.toml for an explanation of this attribute
    #[cfg(not(any(feature = "test", test)))]
    fn advance(&self, turtle: &mut TurtleState) -> AnimationStatus {
        use self::AnimationStatus::*;

        let RotateAnimation {start, delta_angle, clockwise, ref timer, total_millis} = *self;
        let elapsed = timer.elapsed().as_millis() as f64;

        if elapsed >= total_millis {
            turtle.heading = rotate(start, delta_angle, clockwise);

            Complete(None)
        }
        else {
            // t is the total progress made in the animation so far
            let t = elapsed / total_millis;
            // Only rotate as much as the animation has proceeded so far
            let angle = lerp(&radians::ZERO, &delta_angle, &t);
            turtle.heading = rotate(start, angle, clockwise);
            assert!(!turtle.heading.is_nan(), "bug: heading became NaN");

            Running(None)
        }
    }

    // See Cargo.toml for an explanation of this attribute
    #[cfg(any(feature = "test", test))]
    fn advance(&self, turtle: &mut TurtleState) -> AnimationStatus {
        use self::AnimationStatus::*;

        // No animation during testing
        let RotateAnimation {start, delta_angle, clockwise, ..} = *self;
        turtle.heading = rotate(start, delta_angle, clockwise);

        Complete(None)
    }
}
<commit_msg>as_millis may be added to std one day<commit_after>use std::time::Instant;

#[cfg(not(any(feature = "test", test)))]
use interpolation::lerp;

use radians::{self, Radians};
use state::{TurtleState, Path};
#[cfg(not(any(feature = "test", test)))]
use extensions::AsMillis;

pub trait Animation {
    /// Advance the animation forward.
    ///
    /// The animation will use the timer it stores to calculate the current state it should be at.
    fn advance(&self, turtle: &mut TurtleState) -> AnimationStatus;
}

// During tests, we disable animations and so it appears that this is dead code when it is not
// dead code in reality
// See Cargo.toml for an explanation of this attribute
#[cfg_attr(any(feature = "test", test), allow(dead_code))]
pub enum AnimationStatus {
    /// Returned if the animation is still running.
    ///
    /// If a Path is provided, it should be rendered.
    /// Make sure to check whether the path's pen is enabled before rendering.
    Running(Option<Path>),
    /// Returned if the animation is complete.
    /// The advance() function should not be called anymore after this.
    /// The returned path, if any, should be added to the list of paths to be rendered every frame.
    /// Make sure to check whether the path's pen is enabled before rendering.
    Complete(Option<Path>),
}

/// Rotates given angle in the given direction by the given rotation
///
/// Let's say you have a starting angle X. Standard angles go counterclockwise, so
/// if clockwise is true, we need to subtract the `rotation` from X resulting in
/// `X - rotation`. If clockwise is false, we can just add normally.
fn rotate(angle: Radians, rotation: Radians, clockwise: bool) -> Radians {
    let angle = if clockwise { angle - rotation } else { angle + rotation };
    // Normalize the angle to be between 0 and 2*pi
    // Formula adapted from: https://stackoverflow.com/a/24234924/551904
    // More info: https://stackoverflow.com/a/28316446/551904
    angle - radians::TWO_PI * (angle / radians::TWO_PI).floor()
}

/// Animate the turtle moving from a start point to an end point
/// while keeping its heading the same throughout (in a straight line)
pub struct MoveAnimation {
    /// path.start and path.end are used in lerp
    pub path: Path,
    /// timer since the start of the animation
    ///
    /// used with total_millis to calculate t in lerp
    pub timer: Instant,
    /// the total time the animation is meant to take based on the speed and length from
    /// start to finish
    ///
    /// if this is zero, we'll jump to the end
    pub total_millis: f64,
}

impl Animation for MoveAnimation {
    /// Advance the animation forward.
    ///
    /// The animation will use the timer it stores to calculate the current state it should be at.
    // See Cargo.toml for an explanation of this attribute
    #[cfg(not(any(feature = "test", test)))]
    fn advance(&self, turtle: &mut TurtleState) -> AnimationStatus {
        use self::AnimationStatus::*;

        let MoveAnimation {ref path, ref timer, total_millis} = *self;
        let elapsed = AsMillis::as_millis(&timer.elapsed()) as f64;
        if elapsed >= total_millis {
            turtle.position = path.end;
            Complete(Some(path.clone()))
        }
        else {
            // t is the total progress made in the animation so far
            let t = elapsed / total_millis;
            turtle.position = lerp(&path.start, &path.end, &t);

            Running(Some(Path {
                start: path.start,
                end: turtle.position,
                pen: path.pen.clone(),
            }))
        }
    }

    // See Cargo.toml for an explanation of this attribute
    #[cfg(any(feature = "test", test))]
    fn advance(&self, turtle: &mut TurtleState) -> AnimationStatus {
        use self::AnimationStatus::*;

        // No animation during testing
        let MoveAnimation {ref path, ..} = *self;
        turtle.position = path.end;
        Complete(Some(path.clone()))
    }
}

/// Rotate the turtle in place by the given angle in the direction specified
pub struct RotateAnimation {
    /// turtle.heading when the animation was started
    pub start: Radians,
    /// The total angle to move by.
    ///
    /// Linearly interpolated based on the elapsed time to get the delta that should be added
    /// to start to get the current heading in the animation
    pub delta_angle: Radians,
    /// The direction of rotation
    pub clockwise: bool,
    /// timer since the start of the animation
    ///
    /// used with total_millis to calculate t in lerp
    pub timer: Instant,
    /// the total time the animation is meant to take based on the speed and length from
    /// start to finish
    ///
    /// if this is zero, we'll jump to the end
    pub total_millis: f64,
}

impl Animation for RotateAnimation {
    /// Advance the animation forward.
    ///
    /// The animation will use the timer it stores to calculate the current state it should be at.
    // See Cargo.toml for an explanation of this attribute
    #[cfg(not(any(feature = "test", test)))]
    fn advance(&self, turtle: &mut TurtleState) -> AnimationStatus {
        use self::AnimationStatus::*;

        let RotateAnimation {start, delta_angle, clockwise, ref timer, total_millis} = *self;
        let elapsed = AsMillis::as_millis(&timer.elapsed()) as f64;

        if elapsed >= total_millis {
            turtle.heading = rotate(start, delta_angle, clockwise);

            Complete(None)
        }
        else {
            // t is the total progress made in the animation so far
            let t = elapsed / total_millis;
            // Only rotate as much as the animation has proceeded so far
            let angle = lerp(&radians::ZERO, &delta_angle, &t);
            turtle.heading = rotate(start, angle, clockwise);
            assert!(!turtle.heading.is_nan(), "bug: heading became NaN");

            Running(None)
        }
    }

    // See Cargo.toml for an explanation of this attribute
    #[cfg(any(feature = "test", test))]
    fn advance(&self, turtle: &mut TurtleState) -> AnimationStatus {
        use self::AnimationStatus::*;

        // No animation during testing
        let RotateAnimation {start, delta_angle, clockwise, ..} = *self;
        turtle.heading = rotate(start, delta_angle, clockwise);

        Complete(None)
    }
}
<|endoftext|>


<commit_before>use super::utils::*;
use super::{Name, NameWordOrInitial};
use unicode_segmentation::UnicodeSegmentation;

pub const MIN_SURNAME_CHAR_MATCH: usize = 4;
pub const MIN_GIVEN_NAME_CHAR_MATCH: usize = 3;

impl Name {

    /// Might this name represent the same person as another name?
    ///
    /// # Examples
    /// ```
    /// use human_name::Name;
    ///
    /// let j_doe = Name::parse("J. Doe").unwrap();
    /// let jane_doe = Name::parse("Jane Doe").unwrap();
    /// let john_m_doe = Name::parse("John M. Doe").unwrap();
    /// let john_l_doe = Name::parse("John L. Doe").unwrap();
    ///
    /// assert!(j_doe.consistent_with(&john_m_doe));
    /// assert!(j_doe.consistent_with(&john_l_doe));
    /// assert!(j_doe.consistent_with(&jane_doe));
    /// assert!(j_doe.consistent_with(&j_doe));
    /// assert!(!john_m_doe.consistent_with(&john_l_doe));
    /// assert!(!jane_doe.consistent_with(&john_l_doe));
    /// ```
    ///
    /// # Defining "consistency"
    ///
    /// Requires that all known parts are consistent, which means at minimum,
    /// the final words of the surnames match, and one ordered set of first
    /// and middle initials is a superset of the other. If given and/or middle
    /// names and/or suffixes are present in both names, they must match as well.
    ///
    /// Ignores case and non-alphanumeric characters, as well as accents and
    /// other combining marks. In the case of given and middle names, allows
    /// one name to be a prefix of the other, without requiring the prefix
    /// end at a word boundary as we do with surname suffix matches (this
    /// captures cases like "Jin Li"/"Jinli"/"Jin-Li", where the same name
    /// may be transliterated in different ways, as well as *some* nicknames).
    ///
    /// # Limitations
    ///
    /// There will be false positives "Jan Doe" is probably not "Jane Doe",
    /// and false negatives "Dave Judd" might be "David Judd". And, of course,
    /// even identical names do not necessarily represent the same person.
    ///
    /// Given limited information, we err on the side of false positives. This
    /// kind of matching will be most useful in cases where we already have
    /// reason to believe that a single individual's name appears twice, and we
    /// are trying to figure out exactly where, e.g. a particular author's index
    /// in the list of authors of a co-authored paper.
    ///
    #[cfg_attr(rustfmt, rustfmt_skip)]
    pub fn consistent_with(&self, other: &Name) -> bool {
        // Order matters, both for efficiency (initials check is the fastest,
        // coincidentally-identical surnames are less likely than for given names),
        // and for correctness (the given/middle names check assumes a positive
        // result for the middle initials check)
        self.initials_consistent(other) &&
        self.surname_consistent(other) &&
        self.given_and_middle_names_consistent(other) &&
        self.suffix_consistent(other)
    }

    fn initials_consistent(&self, other: &Name) -> bool {
        if self.goes_by_middle_name() == other.goes_by_middle_name() {
            // Normal case: neither goes by a middle name (as far as we know)
            // or both do, so we require the first initial to be the same
            // and one set of middle initials to equal or contain the other
            if self.first_initial() != other.first_initial() {
                return false;
            }

            let my_middle = self.middle_initials();
            let their_middle = other.middle_initials();

            my_middle.is_none() || their_middle.is_none() ||
            my_middle.unwrap().contains(their_middle.unwrap()) ||
            their_middle.unwrap().contains(my_middle.unwrap())
        } else if self.goes_by_middle_name() {
            // Otherwise, we stop requiring the first initial to be the same,
            // because it might have been included in one context and omitted
            // elsewhere, but instead we assume that the name with the initial
            // prior to the middle name includes a full set of initials, so
            // we require the other version to be equal or included
            self.initials().contains(other.initials())
        } else {
            other.initials().contains(self.initials())
        }
    }

    fn given_and_middle_names_consistent(&self, other: &Name) -> bool {
        if self.surname_index == 0 || other.surname_index == 0 {
            return true;
        }

        // We know the initials are equal or one is a superset of the other if
        // we've got this far
        if self.initials().len() >= other.initials().len() {
            self.given_and_middle_names_consistent_with_less_complete(other)
        } else {
            other.given_and_middle_names_consistent_with_less_complete(self)
        }
    }

    fn given_and_middle_names_consistent_with_less_complete(&self, other: &Name) -> bool {
        // Align words using their initials, and for each initial where we know
        // the word for both names, require that the words are an exact match
        // (ignoring case etc), or that one is a prefix of the other with length
        // >= MIN_GIVEN_NAME_CHAR_MATCH.

        let mut their_initials = other.initials().chars().peekable();
        let mut their_initial_index = 0;
        let mut their_word_indices = other.word_indices_in_initials.iter().peekable();
        let mut their_words = other.words[0..other.suffix_index].iter();
        let mut suffix_for_prior_prefix_match: Option<&str> = None;
        let mut consistency_refuted = false;

        self.with_each_given_name_or_initial( &mut |part, _| {
            if consistency_refuted {
                // We already found words that fail to match
                return;
            }

            macro_rules! require_word_match {
                ($my_word:expr, $their_word:expr) => { {
                    let mut my_chars = $my_word.chars().filter_map(lowercase_if_alpha);
                    let mut their_chars = $their_word.chars().filter_map(lowercase_if_alpha);
                    let mut matched = 0;

                    loop {
                        let my_char = my_chars.next();
                        let their_char = their_chars.next();

                        if my_char.is_none() && their_char.is_none() {
                            // Exact match; continue
                            break;
                        } else if (my_char.is_none() || their_char.is_none()) && matched >= MIN_GIVEN_NAME_CHAR_MATCH {
                            // Prefix match; continue
                            if my_char.is_none() && their_initials.peek().is_none() {
                                // We'll only use this when the name with fewer words
                                // (theirs) is out of words, but the last matching part
                                // of the name with more words (ours) was only a prefix
                                // (see comment below)
                                suffix_for_prior_prefix_match = Some(&$their_word[matched..]);
                            }
                            break;
                        } else if my_char != their_char {
                            // Failed match; abort
                            consistency_refuted = true;
                            return;
                        } else {
                            matched += 1;
                        }
                    }
                } }
            }

            if suffix_for_prior_prefix_match.is_some() && their_initials.peek().is_none() {
                // Edge case: where we have a word and they don't, if our prior
                // word was just a prefix of their prior word, require our current
                // word to match the rest of their prior word (to catch cases
                // like Jinli == Jin-Li == Jin Li, != Jin Yi).
                //
                // This logic is imperfect in the presence of middle initials,
                // but that's an edge case to an edge case.
                if let NameWordOrInitial::Word(my_word) = part {
                    require_word_match!(my_word, suffix_for_prior_prefix_match.unwrap());
                }
            }

            if their_initials.peek().is_none() || their_word_indices.peek().is_none() {
                // We've matched everything available
                return;
            }

            let my_initial = part.initial();
            if my_initial != *their_initials.peek().unwrap() {
                // We have an initial they don't, continue
                return;
            }

            // The initials match, so we know we'll want to increment both
            their_initials.next();
            their_initial_index += 1;
            suffix_for_prior_prefix_match = None;

            if their_word_indices.peek().unwrap().0 != their_initial_index - 1 {
                // They don't have a word for this initial
                return;
            }

            let &(j, k) = their_word_indices.next().unwrap();

            // Edge case: Their word is hyphenated and so corresponds to
            // multiple initials
            for _ in j+1..k {
                their_initials.next();
                their_initial_index += 1;
            }

            let their_word = their_words.next().unwrap();

            match part {
                NameWordOrInitial::Initial(_) => {
                    // We don't have a word for this initial
                    return;
                },
                NameWordOrInitial::Word(my_word) => {
                    // Both have words for this initial, so check if they match
                    require_word_match!(my_word, their_word);
                },
            }
        });

        !consistency_refuted
    }

    fn surname_consistent(&self, other: &Name) -> bool {
        let mut my_words = self.surnames()
                               .iter()
                               .flat_map(|w| w.unicode_words())
                               .rev();

        let mut their_words = other.surnames()
                                   .iter()
                                   .flat_map(|w| w.unicode_words())
                                   .rev();

        let mut my_word = my_words.next();
        let mut their_word = their_words.next();
        let mut matching_chars = 0;

        // Require either an exact match (ignoring case etc), or a partial match
        // of len >= MIN_SURNAME_CHAR_MATCH and breaking on a word boundary
        loop {
            // No words remaining for some surname - that's ok if it's true of
            // both, or if the components that match are long enough
            if my_word.is_none() || their_word.is_none() {
                return my_word == their_word || matching_chars >= MIN_SURNAME_CHAR_MATCH;
            }

            macro_rules! reverse_lowercase_alpha_chars {
                ($word:expr) => {
                    $word.unwrap().chars().rev().filter_map(lowercase_if_alpha)
                }
            }

            let mut my_chars = reverse_lowercase_alpha_chars!(my_word);
            let mut their_chars = reverse_lowercase_alpha_chars!(their_word);

            let mut my_char = my_chars.next();
            let mut their_char = their_chars.next();

            loop {
                if my_char.is_none() && their_char.is_none() {
                    // The words matched exactly, try the next word
                    my_word = my_words.next();
                    their_word = their_words.next();
                    break;
                } else if my_char.is_none() {
                    // My word is a suffix of their word, check my next word
                    // against the rest of their word
                    my_word = my_words.next();
                    if my_word.is_none() {
                        // There is no next word, so this is a suffix-only match,
                        // and we don't allow those
                        return false;
                    } else {
                        // Continue the inner loop but incrementing through my
                        // next word
                        my_chars = reverse_lowercase_alpha_chars!(my_word);
                        my_char = my_chars.next();
                    }
                } else if their_char.is_none() {
                    // Their word is a suffix of my word, check their next word
                    // against the rest of my_words
                    their_word = their_words.next();
                    if their_word.is_none() {
                        // There is no next word, so this is a suffix-only match,
                        // and we don't allow those
                        return false;
                    } else {
                        // Continue the inner loop but incrementing through their
                        // next word
                        their_chars = reverse_lowercase_alpha_chars!(their_word);
                        their_char = their_chars.next();
                    }
                } else if my_char != their_char {
                    // We found a conflict and can short-circuit
                    return false;
                } else {
                    // Characters matched, continue the inner loop
                    matching_chars += 1;
                    my_char = my_chars.next();
                    their_char = their_chars.next();
                }
            }
        }
    }

    fn suffix_consistent(&self, other: &Name) -> bool {
        self.suffix().is_none() || other.suffix().is_none() || self.suffix() == other.suffix()
    }
}

impl<'a> NameWordOrInitial<'a> {
    pub fn initial(&self) -> char {
        match self {
            &NameWordOrInitial::Word(word) => {
                word.chars().nth(0).unwrap()
            }
            &NameWordOrInitial::Initial(initial) => {
                initial
            }
        }
    }
}
<commit_msg>Reordering to try to clarify given_and_middle_names_consistent_with_less_complete slightly<commit_after>use super::utils::*;
use super::{Name, NameWordOrInitial};
use unicode_segmentation::UnicodeSegmentation;

pub const MIN_SURNAME_CHAR_MATCH: usize = 4;
pub const MIN_GIVEN_NAME_CHAR_MATCH: usize = 3;

impl Name {

    /// Might this name represent the same person as another name?
    ///
    /// # Examples
    /// ```
    /// use human_name::Name;
    ///
    /// let j_doe = Name::parse("J. Doe").unwrap();
    /// let jane_doe = Name::parse("Jane Doe").unwrap();
    /// let john_m_doe = Name::parse("John M. Doe").unwrap();
    /// let john_l_doe = Name::parse("John L. Doe").unwrap();
    ///
    /// assert!(j_doe.consistent_with(&john_m_doe));
    /// assert!(j_doe.consistent_with(&john_l_doe));
    /// assert!(j_doe.consistent_with(&jane_doe));
    /// assert!(j_doe.consistent_with(&j_doe));
    /// assert!(!john_m_doe.consistent_with(&john_l_doe));
    /// assert!(!jane_doe.consistent_with(&john_l_doe));
    /// ```
    ///
    /// # Defining "consistency"
    ///
    /// Requires that all known parts are consistent, which means at minimum,
    /// the final words of the surnames match, and one ordered set of first
    /// and middle initials is a superset of the other. If given and/or middle
    /// names and/or suffixes are present in both names, they must match as well.
    ///
    /// Ignores case and non-alphanumeric characters, as well as accents and
    /// other combining marks. In the case of given and middle names, allows
    /// one name to be a prefix of the other, without requiring the prefix
    /// end at a word boundary as we do with surname suffix matches (this
    /// captures cases like "Jin Li"/"Jinli"/"Jin-Li", where the same name
    /// may be transliterated in different ways, as well as *some* nicknames).
    ///
    /// # Limitations
    ///
    /// There will be false positives "Jan Doe" is probably not "Jane Doe",
    /// and false negatives "Dave Judd" might be "David Judd". And, of course,
    /// even identical names do not necessarily represent the same person.
    ///
    /// Given limited information, we err on the side of false positives. This
    /// kind of matching will be most useful in cases where we already have
    /// reason to believe that a single individual's name appears twice, and we
    /// are trying to figure out exactly where, e.g. a particular author's index
    /// in the list of authors of a co-authored paper.
    ///
    #[cfg_attr(rustfmt, rustfmt_skip)]
    pub fn consistent_with(&self, other: &Name) -> bool {
        // Order matters, both for efficiency (initials check is the fastest,
        // coincidentally-identical surnames are less likely than for given names),
        // and for correctness (the given/middle names check assumes a positive
        // result for the middle initials check)
        self.initials_consistent(other) &&
        self.surname_consistent(other) &&
        self.given_and_middle_names_consistent(other) &&
        self.suffix_consistent(other)
    }

    fn initials_consistent(&self, other: &Name) -> bool {
        if self.goes_by_middle_name() == other.goes_by_middle_name() {
            // Normal case: neither goes by a middle name (as far as we know)
            // or both do, so we require the first initial to be the same
            // and one set of middle initials to equal or contain the other
            if self.first_initial() != other.first_initial() {
                return false;
            }

            let my_middle = self.middle_initials();
            let their_middle = other.middle_initials();

            my_middle.is_none() || their_middle.is_none() ||
            my_middle.unwrap().contains(their_middle.unwrap()) ||
            their_middle.unwrap().contains(my_middle.unwrap())
        } else if self.goes_by_middle_name() {
            // Otherwise, we stop requiring the first initial to be the same,
            // because it might have been included in one context and omitted
            // elsewhere, but instead we assume that the name with the initial
            // prior to the middle name includes a full set of initials, so
            // we require the other version to be equal or included
            self.initials().contains(other.initials())
        } else {
            other.initials().contains(self.initials())
        }
    }

    fn given_and_middle_names_consistent(&self, other: &Name) -> bool {
        if self.surname_index == 0 || other.surname_index == 0 {
            return true;
        }

        // We know the initials are equal or one is a superset of the other if
        // we've got this far
        if self.initials().len() >= other.initials().len() {
            self.given_and_middle_names_consistent_with_less_complete(other)
        } else {
            other.given_and_middle_names_consistent_with_less_complete(self)
        }
    }

    fn given_and_middle_names_consistent_with_less_complete(&self, other: &Name) -> bool {
        // Align words using their initials, and for each initial where we know
        // the word for both names, require that the words are an exact match
        // (ignoring case etc), or that one is a prefix of the other with length
        // >= MIN_GIVEN_NAME_CHAR_MATCH.

        let mut their_initials = other.initials().chars().peekable();
        let mut their_initial_index = 0;
        let mut their_word_indices = other.word_indices_in_initials.iter().peekable();
        let mut their_words = other.words[0..other.suffix_index].iter();
        let mut suffix_for_prior_prefix_match: Option<&str> = None;
        let mut consistency_refuted = false;

        self.with_each_given_name_or_initial( &mut |part, _| {
            macro_rules! require_word_match {
                ($my_word:expr, $their_word:expr) => { {
                    let mut my_chars = $my_word.chars().filter_map(lowercase_if_alpha);
                    let mut their_chars = $their_word.chars().filter_map(lowercase_if_alpha);
                    let mut matched = 0;

                    loop {
                        let my_char = my_chars.next();
                        let their_char = their_chars.next();

                        if my_char.is_none() && their_char.is_none() {
                            // Exact match; continue
                            return;
                        } else if (my_char.is_none() || their_char.is_none()) && matched >= MIN_GIVEN_NAME_CHAR_MATCH {
                            // Prefix match; continue
                            if my_char.is_none() && their_initials.peek().is_none() {
                                // We'll only use this when the name with fewer words
                                // (theirs) is out of words, but the last matching part
                                // of the name with more words (ours) was only a prefix
                                // (see comment below)
                                suffix_for_prior_prefix_match = Some(&$their_word[matched..]);
                            }
                            return;
                        } else if my_char != their_char {
                            // Failed match; abort
                            consistency_refuted = true;
                            return;
                        } else {
                            matched += 1;
                        }
                    }
                } }
            }

            if consistency_refuted {
                // We already found words that fail to match
                return;
            }

            if their_initials.peek().is_none() || their_word_indices.peek().is_none() {
                if suffix_for_prior_prefix_match.is_some() {
                    // Edge case: where we have a word and they don't, if our prior
                    // word was just a prefix of their prior word, require our current
                    // word to match the rest of their prior word (to catch cases
                    // like Jinli == Jin-Li == Jin Li, != Jin Yi).
                    //
                    // This logic is imperfect in the presence of middle initials,
                    // but that's an edge case to an edge case.
                    if let NameWordOrInitial::Word(my_word) = part {
                        require_word_match!(my_word, suffix_for_prior_prefix_match.unwrap());
                    }
                }

                // We've matched everything available
                return;
            }

            let my_initial = part.initial();
            if my_initial != *their_initials.peek().unwrap() {
                // We have an initial they don't, continue
                return;
            }

            // The initials match, so we know we'll want to increment both
            their_initials.next();
            their_initial_index += 1;
            suffix_for_prior_prefix_match = None;

            if their_word_indices.peek().unwrap().0 != their_initial_index - 1 {
                // They don't have a word for this initial
                return;
            }

            let &(j, k) = their_word_indices.next().unwrap();

            // Edge case: Their word is hyphenated and so corresponds to
            // multiple initials
            for _ in j+1..k {
                their_initials.next();
                their_initial_index += 1;
            }

            let their_word = their_words.next().unwrap();

            match part {
                NameWordOrInitial::Initial(_) => {
                    // We don't have a word for this initial
                    return;
                },
                NameWordOrInitial::Word(my_word) => {
                    // Both have words for this initial, so check if they match
                    require_word_match!(my_word, their_word);
                },
            }
        });

        !consistency_refuted
    }

    fn surname_consistent(&self, other: &Name) -> bool {
        let mut my_words = self.surnames()
                               .iter()
                               .flat_map(|w| w.unicode_words())
                               .rev();

        let mut their_words = other.surnames()
                                   .iter()
                                   .flat_map(|w| w.unicode_words())
                                   .rev();

        let mut my_word = my_words.next();
        let mut their_word = their_words.next();
        let mut matching_chars = 0;

        // Require either an exact match (ignoring case etc), or a partial match
        // of len >= MIN_SURNAME_CHAR_MATCH and breaking on a word boundary
        loop {
            // No words remaining for some surname - that's ok if it's true of
            // both, or if the components that match are long enough
            if my_word.is_none() || their_word.is_none() {
                return my_word == their_word || matching_chars >= MIN_SURNAME_CHAR_MATCH;
            }

            macro_rules! reverse_lowercase_alpha_chars {
                ($word:expr) => {
                    $word.unwrap().chars().rev().filter_map(lowercase_if_alpha)
                }
            }

            let mut my_chars = reverse_lowercase_alpha_chars!(my_word);
            let mut their_chars = reverse_lowercase_alpha_chars!(their_word);

            let mut my_char = my_chars.next();
            let mut their_char = their_chars.next();

            loop {
                if my_char.is_none() && their_char.is_none() {
                    // The words matched exactly, try the next word
                    my_word = my_words.next();
                    their_word = their_words.next();
                    break;
                } else if my_char.is_none() {
                    // My word is a suffix of their word, check my next word
                    // against the rest of their word
                    my_word = my_words.next();
                    if my_word.is_none() {
                        // There is no next word, so this is a suffix-only match,
                        // and we don't allow those
                        return false;
                    } else {
                        // Continue the inner loop but incrementing through my
                        // next word
                        my_chars = reverse_lowercase_alpha_chars!(my_word);
                        my_char = my_chars.next();
                    }
                } else if their_char.is_none() {
                    // Their word is a suffix of my word, check their next word
                    // against the rest of my_words
                    their_word = their_words.next();
                    if their_word.is_none() {
                        // There is no next word, so this is a suffix-only match,
                        // and we don't allow those
                        return false;
                    } else {
                        // Continue the inner loop but incrementing through their
                        // next word
                        their_chars = reverse_lowercase_alpha_chars!(their_word);
                        their_char = their_chars.next();
                    }
                } else if my_char != their_char {
                    // We found a conflict and can short-circuit
                    return false;
                } else {
                    // Characters matched, continue the inner loop
                    matching_chars += 1;
                    my_char = my_chars.next();
                    their_char = their_chars.next();
                }
            }
        }
    }

    fn suffix_consistent(&self, other: &Name) -> bool {
        self.suffix().is_none() || other.suffix().is_none() || self.suffix() == other.suffix()
    }
}

impl<'a> NameWordOrInitial<'a> {
    pub fn initial(&self) -> char {
        match self {
            &NameWordOrInitial::Word(word) => {
                word.chars().nth(0).unwrap()
            }
            &NameWordOrInitial::Initial(initial) => {
                initial
            }
        }
    }
}
<|endoftext|>


<commit_before>use rosxmlrpc;
use rosxmlrpc::XmlRpcValue;
use rustc_serialize::{Decodable, Encodable};
use std::collections::HashMap;
use std::sync::mpsc::{Sender, Receiver};
use std::sync::mpsc;
use std::sync::Mutex;
use std::error::Error as ErrorTrait;
use nix::unistd::getpid;
use super::error::ServerError as Error;
use super::value::Topic;
use tcpros::{self, Message, Publisher, Subscriber};

pub struct Slave {
    server: rosxmlrpc::Server,
    req: Mutex<Receiver<(String, rosxmlrpc::server::ParameterIterator)>>,
    res: Mutex<Sender<rosxmlrpc::server::Answer>>,
    subscriptions: HashMap<String, Subscriber>,
    publications: HashMap<String, Publisher>,
    master_uri: String,
    name: String,
}

struct SlaveHandler {
    req: Mutex<Sender<(String, rosxmlrpc::server::ParameterIterator)>>,
    res: Mutex<Receiver<rosxmlrpc::server::Answer>>,
}

type SerdeResult<T> = Result<T, Error>;

impl Slave {
    pub fn new(master_uri: &str, server_uri: &str, name: &str) -> Result<Slave, Error> {
        let (tx_req, rx_req) = mpsc::channel();
        let (tx_res, rx_res) = mpsc::channel();
        let server = rosxmlrpc::Server::new(server_uri,
                                            SlaveHandler {
                                                req: Mutex::new(tx_req),
                                                res: Mutex::new(rx_res),
                                            })?;
        Ok(Slave {
            server: server,
            subscriptions: HashMap::new(),
            publications: HashMap::new(),
            master_uri: master_uri.to_owned(),
            req: Mutex::new(rx_req),
            res: Mutex::new(tx_res),
            name: String::from(name),
        })
    }

    pub fn uri(&self) -> &str {
        return &self.server.uri;
    }

    fn get_bus_stats(&self,
                     req: &mut rosxmlrpc::server::ParameterIterator)
                     -> SerdeResult<BusStats> {
        let caller_id = pop::<String>(req)?;
        if caller_id != "" {
            // TODO: implement actual stats displaying
            Ok(BusStats {
                publish: Vec::new(),
                subscribe: Vec::new(),
                service: ServiceStats {
                    bytes_received: 0,
                    bytes_sent: 0,
                    number_of_requests: 0,
                },
            })
        } else {
            Err(Error::Protocol("Empty strings given".to_owned()))
        }
    }

    fn get_bus_info(&self,
                    req: &mut rosxmlrpc::server::ParameterIterator)
                    -> SerdeResult<Vec<BusInfo>> {
        let caller_id = pop::<String>(req)?;
        if caller_id != "" {
            // TODO: implement actual info displaying
            Ok(Vec::new())
        } else {
            Err(Error::Protocol("Empty strings given".to_owned()))
        }
    }

    fn encode_response<T: Encodable>(&self,
                                     response: SerdeResult<T>,
                                     message: &str)
                                     -> SerdeResult<()> {
        let mut res = rosxmlrpc::server::Answer::new();
        match response {
            Ok(value) => res.add(&(1i32, message, value)),
            Err(err) => res.add(&(-1i32, err.description(), 0)),
        }?;

        self.res.lock().unwrap().send(res).unwrap();
        Ok(())
    }

    fn param_update(&self, req: &mut rosxmlrpc::server::ParameterIterator) -> SerdeResult<i32> {
        let caller_id = pop::<String>(req)?;
        let key = pop::<String>(req)?;
        let value = req.next()
            .ok_or(Error::Protocol(String::from("Missing parameter")))?
            .value();
        if caller_id != "" && key != "" {
            // TODO: implement handling of parameter updates
            println!("{} {} {}", caller_id, key, value);
            Ok(0)
        } else {
            Err(Error::Protocol("Emtpy strings given".to_owned()))
        }
    }

    fn get_pid(&self, req: &mut rosxmlrpc::server::ParameterIterator) -> SerdeResult<i32> {
        let caller_id = pop::<String>(req)?;
        if caller_id != "" {
            Ok(getpid())
        } else {
            Err(Error::Protocol("Empty strings given".to_owned()))
        }
    }

    fn shutdown(&mut self, req: &mut rosxmlrpc::server::ParameterIterator) -> SerdeResult<i32> {
        let caller_id = pop::<String>(req)?;
        let message = pop::<String>(req).unwrap_or(String::from(""));
        if caller_id != "" {
            println!("Server is shutting down because: {}", message);
            match self.server.shutdown() {
                Ok(()) => Ok(0),
                Err(_) => Err(Error::Critical("Failed to shutdown server".to_owned())),
            }
        } else {
            Err(Error::Protocol("Empty strings given".to_owned()))
        }
    }

    pub fn add_publication<T>(&mut self, hostname: &str, topic: &str) -> Result<(), tcpros::Error>
        where T: Message
    {
        use std::collections::hash_map::Entry;
        match self.publications.entry(String::from(topic)) {
            Entry::Occupied(entry) => {
                let publisher = entry.get();
                if publisher.msg_type != T::msg_type() {
                    Err(tcpros::Error::Mismatch)
                } else {
                    Ok(())
                }
            }
            Entry::Vacant(entry) => {
                let publisher = Publisher::new::<T, _>(format!("{}:0", hostname).as_str(), topic)?;
                entry.insert(publisher);
                Ok(())
            }
        }
    }

    pub fn get_publication(&mut self, topic: &str) -> Option<&Publisher> {
        self.publications.get(topic)
    }

    pub fn remove_publication(&mut self, topic: &str) {
        self.publications.remove(topic);
    }

    fn get_publications(&self,
                        req: &mut rosxmlrpc::server::ParameterIterator)
                        -> SerdeResult<Vec<Topic>> {
        let caller_id = pop::<String>(req)?;
        if caller_id != "" {
            Ok(self.publications
                .values()
                .map(|ref v| {
                    return Topic {
                        name: v.topic.clone(),
                        datatype: v.msg_type.clone(),
                    };
                })
                .collect())
        } else {
            Err(Error::Protocol("Empty strings given".to_owned()))
        }
    }

    pub fn add_subscription<T, F>(&mut self, topic: &str, callback: F) -> Option<&mut Subscriber>
        where T: Message + Decodable,
              F: Fn(T) -> () + Send + 'static
    {
        use std::collections::hash_map::Entry;
        match self.subscriptions.entry(String::from(topic)) {
            Entry::Occupied(..) => {
                error!("Duplicate subscription to topic '{}' attempted", topic);
                None
            }
            Entry::Vacant(entry) => {
                let subscriber = Subscriber::new::<T, F>(&self.name, topic, callback);
                Some(entry.insert(subscriber))
            }
        }
    }

    pub fn get_subscription(&mut self, topic: &str) -> Option<&mut Subscriber> {
        self.subscriptions.get_mut(topic)
    }

    pub fn remove_subscription(&mut self, topic: &str) {
        self.subscriptions.remove(topic);
    }

    fn get_subscriptions(&self,
                         req: &mut rosxmlrpc::server::ParameterIterator)
                         -> SerdeResult<Vec<Topic>> {
        let caller_id = pop::<String>(req)?;
        if caller_id != "" {
            Ok(self.subscriptions
                .values()
                .map(|ref v| {
                    return Topic {
                        name: v.topic.clone(),
                        datatype: v.msg_type.clone(),
                    };
                })
                .collect())
        } else {
            Err(Error::Protocol("Empty strings given".to_owned()))
        }
    }

    fn publisher_update(&mut self,
                        req: &mut rosxmlrpc::server::ParameterIterator)
                        -> SerdeResult<i32> {
        let caller_id = pop::<String>(req)?;
        let topic = pop::<String>(req)?;
        let publishers = pop::<Vec<String>>(req)?;
        if caller_id != "" && topic != "" && publishers.iter().all(|ref x| x.as_str() != "") {
            if let Some(mut subscription) = self.subscriptions.get_mut(&topic) {
                for publisher in publishers {
                    if let Err(err) = connect_to_publisher(&mut subscription,
                                                           &self.name,
                                                           &publisher,
                                                           &topic) {
                        return Err(err);
                    }
                }
            }
            Ok(0)
        } else {
            Err(Error::Protocol("Empty strings given".to_owned()))
        }
    }

    fn get_master_uri(&self, req: &mut rosxmlrpc::server::ParameterIterator) -> SerdeResult<&str> {
        let caller_id = pop::<String>(req)?;
        if caller_id != "" {
            Ok(&self.master_uri)
        } else {
            Err(Error::Protocol("Empty strings given".to_owned()))
        }
    }

    fn request_topic(&self,
                     req: &mut rosxmlrpc::server::ParameterIterator)
                     -> SerdeResult<(String, String, i32)> {
        let caller_id = pop::<String>(req)?;
        let topic = pop::<String>(req)?;
        let protocols = req.next()
            .ok_or(Error::Protocol(String::from("Missing parameter")))?
            .value();
        let publisher = self.publications
            .get(&topic)
            .ok_or(Error::Protocol("Requested topic not published by node".to_owned()))?;
        if caller_id != "" && topic != "" {
            if let XmlRpcValue::Array(protocols) = protocols {
                let mut has_tcpros = false;
                for protocol in protocols {
                    if let XmlRpcValue::Array(protocol) = protocol {
                        if let Some(&XmlRpcValue::String(ref name)) = protocol.get(0) {
                            has_tcpros |= name == "TCPROS";
                        }
                    }
                }
                if has_tcpros {
                    Ok(("TCPROS".to_owned(), publisher.ip.clone(), publisher.port as i32))
                } else {
                    Err(Error::Protocol("No matching protocols available".to_owned()))
                }
            } else {
                Err(Error::Protocol("Protocols need to be provided as [ [String, \
                                     XmlRpcLegalValue] ]"
                    .to_owned()))
            }
        } else {
            Err(Error::Protocol("Empty parameters given".to_owned()))
        }
    }

    fn handle_call(&mut self,
                   method_name: &str,
                   req: &mut rosxmlrpc::server::ParameterIterator)
                   -> SerdeResult<()> {
        println!("HANDLING METHOD: {}", method_name);
        match method_name {
            "getBusStats" => self.encode_response(self.get_bus_stats(req), "Bus stats"),
            "getBusInfo" => self.encode_response(self.get_bus_info(req), "Bus stats"),
            "getMasterUri" => self.encode_response(self.get_master_uri(req), "Master URI"),
            "shutdown" => {
                let data = self.shutdown(req);
                self.encode_response(data, "Shutdown")
            }
            "getPid" => self.encode_response(self.get_pid(req), "PID"),
            "getSubscriptions" => {
                self.encode_response(self.get_subscriptions(req), "List of subscriptions")
            }
            "getPublications" => {
                self.encode_response(self.get_publications(req), "List of publications")
            }
            "paramUpdate" => self.encode_response(self.param_update(req), "Parameter updated"),
            "publisherUpdate" => {
                let answer = self.publisher_update(req);
                self.encode_response(answer, "Publishers updated")
            }
            "requestTopic" => self.encode_response(self.request_topic(req), "Chosen protocol"),
            name => {
                self.encode_response::<i32>(Err(Error::Protocol(format!("Unimplemented method: \
                                                                         {}",
                                                                        name))),
                                            "")
            }
        }
    }

    pub fn handle_calls(&mut self) -> Result<(), String> {
        loop {
            let recv = self.req.lock().unwrap().recv();
            match recv {
                Err(_) => return Ok(()),
                Ok((method_name, mut req)) => {
                    if let Err(err) = self.handle_call(&method_name, &mut req) {
                        match err {
                            Error::Critical(msg) => {
                                return Err(msg);
                            }
                            _ => {
                                println!("{}", err);
                            }
                        }
                    }
                }
            }
        }
    }

    pub fn handle_call_queue(&mut self) -> Result<mpsc::TryRecvError, Error> {
        loop {
            let recv = self.req.lock().unwrap().try_recv();
            match recv {
                Err(err) => return Ok(err),
                Ok((method_name, mut req)) => self.handle_call(&method_name, &mut req)?,
            }
        }
    }
}

impl rosxmlrpc::server::XmlRpcServer for SlaveHandler {
    fn handle(&self,
              method_name: &str,
              req: rosxmlrpc::server::ParameterIterator)
              -> rosxmlrpc::server::Answer {
        println!("CALLED METHOD: {}", method_name);
        self.req.lock().unwrap().send((method_name.to_owned(), req)).unwrap();
        self.res.lock().unwrap().recv().unwrap()
    }
}

fn pop<T: Decodable>(req: &mut rosxmlrpc::server::ParameterIterator) -> SerdeResult<T> {
    req.next()
        .ok_or(Error::Protocol(String::from("Missing parameter")))?
        .read()
        .map_err(|v| Error::Decoding(v))
}

fn connect_to_publisher(subscriber: &mut Subscriber,
                        caller_id: &str,
                        publisher: &str,
                        topic: &str)
                        -> Result<(), Error> {
    let (protocol, hostname, port) = request_topic(publisher, caller_id, topic)?;
    if protocol != "TCPROS" {
        // This should never happen, due to the nature of ROS
        panic!("Expected TCPROS protocol from ROS publisher");
    }
    subscriber.connect_to((hostname.as_str(), port as u16)).map_err(|err| Error::Io(err))
}

fn request_topic(publisher_uri: &str,
                 caller_id: &str,
                 topic: &str)
                 -> Result<(String, String, i32), rosxmlrpc::error::Error> {
    let mut request = rosxmlrpc::client::Request::new("requestTopic");
    request.add(&caller_id)?;
    request.add(&topic)?;
    request.add(&[["TCPROS"]])?;
    let client = rosxmlrpc::Client::new(publisher_uri);
    let protocols = client.request::<(i32, String, (String, String, i32))>(request)?;
    Ok(protocols.2)
}

#[derive(RustcEncodable)]
pub struct BusStats {
    pub publish: Vec<PublishStats>,
    pub subscribe: Vec<SubscribeStats>,
    pub service: ServiceStats,
}

#[derive(RustcEncodable)]
pub struct PublishStats {
    pub name: String,
    pub data_sent: String,
    pub connection_data: PublishConnectionData,
}

#[derive(RustcEncodable)]
pub struct PublishConnectionData {
    pub connection_id: String,
    pub bytes_sent: i32,
    pub number_sent: i32,
    pub connected: bool,
}

#[derive(RustcEncodable)]
pub struct SubscribeStats {
    pub name: String,
    pub connection_data: SubscribeConnectionData,
}

#[derive(RustcEncodable)]
pub struct SubscribeConnectionData {
    pub connection_id: String,
    pub bytes_received: i32,
    pub drop_estimate: i32,
    pub connected: bool,
}

#[derive(RustcEncodable)]
pub struct ServiceStats {
    pub number_of_requests: i32,
    pub bytes_received: i32,
    pub bytes_sent: i32,
}

#[derive(RustcEncodable)]
pub struct BusInfo {
    pub connection_id: String,
    pub destination_id: String,
    pub direction: String,
    pub transport: String,
    pub topic: String,
    pub connected: bool,
}
<commit_msg>Flatten Slave API code<commit_after>use rosxmlrpc;
use rosxmlrpc::XmlRpcValue;
use rustc_serialize::{Decodable, Encodable};
use std::collections::HashMap;
use std::sync::mpsc::{Sender, Receiver};
use std::sync::mpsc;
use std::sync::Mutex;
use std::error::Error as ErrorTrait;
use nix::unistd::getpid;
use super::error::ServerError as Error;
use super::value::Topic;
use tcpros::{self, Message, Publisher, Subscriber};

pub struct Slave {
    server: rosxmlrpc::Server,
    req: Mutex<Receiver<(String, rosxmlrpc::server::ParameterIterator)>>,
    res: Mutex<Sender<rosxmlrpc::server::Answer>>,
    subscriptions: HashMap<String, Subscriber>,
    publications: HashMap<String, Publisher>,
    master_uri: String,
    name: String,
}

struct SlaveHandler {
    req: Mutex<Sender<(String, rosxmlrpc::server::ParameterIterator)>>,
    res: Mutex<Receiver<rosxmlrpc::server::Answer>>,
}

type SerdeResult<T> = Result<T, Error>;

impl Slave {
    pub fn new(master_uri: &str, server_uri: &str, name: &str) -> Result<Slave, Error> {
        let (tx_req, rx_req) = mpsc::channel();
        let (tx_res, rx_res) = mpsc::channel();
        let server = rosxmlrpc::Server::new(server_uri,
                                            SlaveHandler {
                                                req: Mutex::new(tx_req),
                                                res: Mutex::new(rx_res),
                                            })?;
        Ok(Slave {
            server: server,
            subscriptions: HashMap::new(),
            publications: HashMap::new(),
            master_uri: String::from(master_uri),
            req: Mutex::new(rx_req),
            res: Mutex::new(tx_res),
            name: String::from(name),
        })
    }

    pub fn uri(&self) -> &str {
        return &self.server.uri;
    }

    fn get_bus_stats(&self,
                     req: &mut rosxmlrpc::server::ParameterIterator)
                     -> SerdeResult<BusStats> {
        let caller_id = pop::<String>(req)?;
        if caller_id == "" {
            return Err(Error::Protocol(String::from("Empty strings given")));
        }
        // TODO: implement actual stats displaying
        Ok(BusStats {
            publish: Vec::new(),
            subscribe: Vec::new(),
            service: ServiceStats {
                bytes_received: 0,
                bytes_sent: 0,
                number_of_requests: 0,
            },
        })
    }

    fn get_bus_info(&self,
                    req: &mut rosxmlrpc::server::ParameterIterator)
                    -> SerdeResult<Vec<BusInfo>> {
        let caller_id = pop::<String>(req)?;
        if caller_id == "" {
            return Err(Error::Protocol(String::from("Empty strings given")));
        }
        // TODO: implement actual info displaying
        Ok(Vec::new())
    }

    fn encode_response<T: Encodable>(&self,
                                     response: SerdeResult<T>,
                                     message: &str)
                                     -> SerdeResult<()> {
        let mut res = rosxmlrpc::server::Answer::new();
        match response {
            Ok(value) => res.add(&(1i32, message, value)),
            Err(err) => res.add(&(-1i32, err.description(), 0)),
        }?;

        self.res.lock().unwrap().send(res).unwrap();
        Ok(())
    }

    fn param_update(&self, req: &mut rosxmlrpc::server::ParameterIterator) -> SerdeResult<i32> {
        let caller_id = pop::<String>(req)?;
        let key = pop::<String>(req)?;
        // We don't do anything with parameter updates
        let value = req.next();
        if let None = value {
            return Err(Error::Protocol(String::from("Missing parameter")));
        }
        if caller_id == "" || key == "" {
            return Err(Error::Protocol(String::from("Empty strings given")));
        }
        Ok(0)
    }

    fn get_pid(&self, req: &mut rosxmlrpc::server::ParameterIterator) -> SerdeResult<i32> {
        let caller_id = pop::<String>(req)?;
        if caller_id == "" {
            return Err(Error::Protocol(String::from("Empty strings given")));
        }
        Ok(getpid())
    }

    fn shutdown(&mut self, req: &mut rosxmlrpc::server::ParameterIterator) -> SerdeResult<i32> {
        let caller_id = pop::<String>(req)?;
        let message = pop::<String>(req).unwrap_or(String::from(""));
        if caller_id == "" {
            return Err(Error::Protocol(String::from("Empty strings given")));
        }
        info!("Server is shutting down because: {}", message);
        match self.server.shutdown() {
            Ok(()) => Ok(0),
            Err(_) => Err(Error::Critical(String::from("Failed to shutdown server"))),
        }
    }

    pub fn add_publication<T>(&mut self, hostname: &str, topic: &str) -> Result<(), tcpros::Error>
        where T: Message
    {
        use std::collections::hash_map::Entry;
        match self.publications.entry(String::from(topic)) {
            Entry::Occupied(publisher_entry) => {
                if publisher_entry.get().msg_type != T::msg_type() {
                    Err(tcpros::Error::Mismatch)
                } else {
                    Ok(())
                }
            }
            Entry::Vacant(entry) => {
                let publisher = Publisher::new::<T, _>(format!("{}:0", hostname).as_str(), topic)?;
                entry.insert(publisher);
                Ok(())
            }
        }
    }

    pub fn get_publication(&mut self, topic: &str) -> Option<&Publisher> {
        self.publications.get(topic)
    }

    pub fn remove_publication(&mut self, topic: &str) {
        self.publications.remove(topic);
    }

    fn get_publications(&self,
                        req: &mut rosxmlrpc::server::ParameterIterator)
                        -> SerdeResult<Vec<Topic>> {
        let caller_id = pop::<String>(req)?;
        if caller_id == "" {
            return Err(Error::Protocol(String::from("Empty strings given")));
        }
        Ok(self.publications
            .values()
            .map(|ref v| {
                return Topic {
                    name: v.topic.clone(),
                    datatype: v.msg_type.clone(),
                };
            })
            .collect())
    }

    pub fn add_subscription<T, F>(&mut self, topic: &str, callback: F) -> Option<&mut Subscriber>
        where T: Message + Decodable,
              F: Fn(T) -> () + Send + 'static
    {
        use std::collections::hash_map::Entry;
        match self.subscriptions.entry(String::from(topic)) {
            Entry::Occupied(..) => {
                error!("Duplicate subscription to topic '{}' attempted", topic);
                None
            }
            Entry::Vacant(entry) => {
                let subscriber = Subscriber::new::<T, F>(&self.name, topic, callback);
                Some(entry.insert(subscriber))
            }
        }
    }

    pub fn get_subscription(&mut self, topic: &str) -> Option<&mut Subscriber> {
        self.subscriptions.get_mut(topic)
    }

    pub fn remove_subscription(&mut self, topic: &str) {
        self.subscriptions.remove(topic);
    }

    fn get_subscriptions(&self,
                         req: &mut rosxmlrpc::server::ParameterIterator)
                         -> SerdeResult<Vec<Topic>> {
        let caller_id = pop::<String>(req)?;
        if caller_id == "" {
            return Err(Error::Protocol(String::from("Empty strings given")));
        }
        Ok(self.subscriptions
            .values()
            .map(|ref v| {
                return Topic {
                    name: v.topic.clone(),
                    datatype: v.msg_type.clone(),
                };
            })
            .collect())
    }

    fn publisher_update(&mut self,
                        req: &mut rosxmlrpc::server::ParameterIterator)
                        -> SerdeResult<i32> {
        let caller_id = pop::<String>(req)?;
        let topic = pop::<String>(req)?;
        let publishers = pop::<Vec<String>>(req)?;
        if caller_id == "" || topic == "" || publishers.iter().any(|ref x| x.as_str() == "") {
            return Err(Error::Protocol(String::from("Empty strings given")));
        }
        if let Some(mut subscription) = self.subscriptions.get_mut(&topic) {
            for publisher in publishers {
                if let Err(err) = connect_to_publisher(&mut subscription,
                                                       &self.name,
                                                       &publisher,
                                                       &topic) {
                    return Err(err);
                }
            }
        }
        Ok(0)
    }

    fn get_master_uri(&self, req: &mut rosxmlrpc::server::ParameterIterator) -> SerdeResult<&str> {
        let caller_id = pop::<String>(req)?;
        if caller_id == "" {
            return Err(Error::Protocol(String::from("Empty strings given")));
        }
        Ok(&self.master_uri)
    }

    fn request_topic(&self,
                     req: &mut rosxmlrpc::server::ParameterIterator)
                     -> SerdeResult<(String, String, i32)> {
        let caller_id = pop::<String>(req)?;
        let topic = pop::<String>(req)?;
        let protocols = req.next()
            .ok_or(Error::Protocol(String::from("Missing parameter")))?
            .value();
        let publisher = self.publications
            .get(&topic)
            .ok_or(Error::Protocol(String::from("Requested topic not published by node")))?;
        if caller_id == "" || topic == "" {
            return Err(Error::Protocol(String::from("Empty strings given")));
        }
        let protocols = match protocols {
            XmlRpcValue::Array(protocols) => protocols,
            _ => {
                return Err(Error::Protocol(String::from("Protocols need to be provided as [ \
                                                         [String, XmlRpcLegalValue] ]")))
            }
        };
        let mut has_tcpros = false;
        for protocol in protocols {
            if let XmlRpcValue::Array(protocol) = protocol {
                if let Some(&XmlRpcValue::String(ref name)) = protocol.get(0) {
                    has_tcpros |= name == "TCPROS";
                }
            }
        }
        if has_tcpros {
            Ok((String::from("TCPROS"), publisher.ip.clone(), publisher.port as i32))
        } else {
            Err(Error::Protocol(String::from("No matching protocols available")))
        }
    }

    fn handle_call(&mut self,
                   method_name: &str,
                   req: &mut rosxmlrpc::server::ParameterIterator)
                   -> SerdeResult<()> {
        println!("HANDLING METHOD: {}", method_name);
        match method_name {
            "getBusStats" => self.encode_response(self.get_bus_stats(req), "Bus stats"),
            "getBusInfo" => self.encode_response(self.get_bus_info(req), "Bus stats"),
            "getMasterUri" => self.encode_response(self.get_master_uri(req), "Master URI"),
            "shutdown" => {
                let data = self.shutdown(req);
                self.encode_response(data, "Shutdown")
            }
            "getPid" => self.encode_response(self.get_pid(req), "PID"),
            "getSubscriptions" => {
                self.encode_response(self.get_subscriptions(req), "List of subscriptions")
            }
            "getPublications" => {
                self.encode_response(self.get_publications(req), "List of publications")
            }
            "paramUpdate" => self.encode_response(self.param_update(req), "Parameter updated"),
            "publisherUpdate" => {
                let answer = self.publisher_update(req);
                self.encode_response(answer, "Publishers updated")
            }
            "requestTopic" => self.encode_response(self.request_topic(req), "Chosen protocol"),
            name => {
                self.encode_response::<i32>(Err(Error::Protocol(format!("Unimplemented method: \
                                                                         {}",
                                                                        name))),
                                            "")
            }
        }
    }

    pub fn handle_calls(&mut self) -> Result<(), String> {
        loop {
            let recv = self.req.lock().unwrap().recv();
            match recv {
                Err(_) => return Ok(()),
                Ok((method_name, mut req)) => {
                    if let Err(err) = self.handle_call(&method_name, &mut req) {
                        match err {
                            Error::Critical(msg) => {
                                return Err(msg);
                            }
                            _ => {
                                println!("{}", err);
                            }
                        }
                    }
                }
            }
        }
    }

    pub fn handle_call_queue(&mut self) -> Result<mpsc::TryRecvError, Error> {
        loop {
            let recv = self.req.lock().unwrap().try_recv();
            match recv {
                Err(err) => return Ok(err),
                Ok((method_name, mut req)) => self.handle_call(&method_name, &mut req)?,
            }
        }
    }
}

impl rosxmlrpc::server::XmlRpcServer for SlaveHandler {
    fn handle(&self,
              method_name: &str,
              req: rosxmlrpc::server::ParameterIterator)
              -> rosxmlrpc::server::Answer {
        println!("CALLED METHOD: {}", method_name);
        self.req.lock().unwrap().send((String::from(method_name), req)).unwrap();
        self.res.lock().unwrap().recv().unwrap()
    }
}

fn pop<T: Decodable>(req: &mut rosxmlrpc::server::ParameterIterator) -> SerdeResult<T> {
    req.next()
        .ok_or(Error::Protocol(String::from("Missing parameter")))?
        .read()
        .map_err(|v| Error::Decoding(v))
}

fn connect_to_publisher(subscriber: &mut Subscriber,
                        caller_id: &str,
                        publisher: &str,
                        topic: &str)
                        -> Result<(), Error> {
    let (protocol, hostname, port) = request_topic(publisher, caller_id, topic)?;
    if protocol != "TCPROS" {
        // This should never happen, due to the nature of ROS
        panic!("Expected TCPROS protocol from ROS publisher");
    }
    subscriber.connect_to((hostname.as_str(), port as u16)).map_err(|err| Error::Io(err))
}

fn request_topic(publisher_uri: &str,
                 caller_id: &str,
                 topic: &str)
                 -> Result<(String, String, i32), rosxmlrpc::error::Error> {
    let mut request = rosxmlrpc::client::Request::new("requestTopic");
    request.add(&caller_id)?;
    request.add(&topic)?;
    request.add(&[["TCPROS"]])?;
    let client = rosxmlrpc::Client::new(publisher_uri);
    let protocols = client.request::<(i32, String, (String, String, i32))>(request)?;
    Ok(protocols.2)
}

#[derive(RustcEncodable)]
pub struct BusStats {
    pub publish: Vec<PublishStats>,
    pub subscribe: Vec<SubscribeStats>,
    pub service: ServiceStats,
}

#[derive(RustcEncodable)]
pub struct PublishStats {
    pub name: String,
    pub data_sent: String,
    pub connection_data: PublishConnectionData,
}

#[derive(RustcEncodable)]
pub struct PublishConnectionData {
    pub connection_id: String,
    pub bytes_sent: i32,
    pub number_sent: i32,
    pub connected: bool,
}

#[derive(RustcEncodable)]
pub struct SubscribeStats {
    pub name: String,
    pub connection_data: SubscribeConnectionData,
}

#[derive(RustcEncodable)]
pub struct SubscribeConnectionData {
    pub connection_id: String,
    pub bytes_received: i32,
    pub drop_estimate: i32,
    pub connected: bool,
}

#[derive(RustcEncodable)]
pub struct ServiceStats {
    pub number_of_requests: i32,
    pub bytes_received: i32,
    pub bytes_sent: i32,
}

#[derive(RustcEncodable)]
pub struct BusInfo {
    pub connection_id: String,
    pub destination_id: String,
    pub direction: String,
    pub transport: String,
    pub topic: String,
    pub connected: bool,
}
<|endoftext|>


<commit_before>use nes::system::SystemState;
use nes::bus::BusKind;

#[derive(Copy, Clone)]
pub struct Page {
    start: usize,
    end: usize,
}

#[derive(Default)]
pub struct Pages {
    current_size: usize,
    data: Vec<u8>,
}

impl Pages {
    pub fn new() -> Pages {
        Pages {
            current_size: 0,
            data: Vec::new(),
        }
    }

    pub fn alloc_kb(&mut self, kb: usize) -> Page {
        let start = self.data.len();
        let end = start + (kb * 0x400);
        self.data.resize(end, 0);
        Page { start: start, end: end }
    }

    pub fn read(&self, page: Page, addr: u16) -> u8 {
        self.data[page.start + addr as usize]
    }

    pub fn write(&mut self, page: Page, addr: u16, val: u8) {
        self.data[page.start + addr as usize] = val;
    }
}

pub struct MemoryBlock {
    kb: usize,
    page: Page
}

impl MemoryBlock {
    pub fn new(kb: usize, pages: &mut Pages) -> MemoryBlock {
        MemoryBlock {
            kb: kb,
            page: pages.alloc_kb(kb),
        }
    }

    pub fn peek(&self, bus: BusKind, state: &SystemState, addr: u16) -> u8 {
        state.mem.read(self.page, addr)
    }

    pub fn read(&self, bus: BusKind, state: &mut SystemState, addr: u16) -> u8 {
        state.mem.read(self.page, addr)
    }

    pub fn write(&self, bus: BusKind, state: &mut SystemState, addr: u16, val: u8) {
        state.mem.write(self.page, addr, val);
    }
}
<commit_msg>Include missed file<commit_after>use nes::system::{System, SystemState};
use nes::cartridge::Cartridge;
use nes::bus::BusKind;

#[derive(Copy, Clone)]
pub struct Page {
    start: usize,
    end: usize,
}

#[derive(Default)]
pub struct Pages {
    current_size: usize,
    data: Vec<u8>,
}

impl Pages {
    pub fn new() -> Pages {
        Pages {
            current_size: 0,
            data: Vec::new(),
        }
    }

    pub fn alloc_kb(&mut self, kb: usize) -> Page {
        let start = self.data.len();
        let end = start + (kb * 0x400);
        self.data.resize(end, 0);
        Page { start: start, end: end }
    }

    pub fn read(&self, page: Page, addr: u16) -> u8 {
        self.data[page.start + addr as usize]
    }

    pub fn write(&mut self, page: Page, addr: u16, val: u8) {
        self.data[page.start + addr as usize] = val;
    }
}

pub struct MemoryBlock {
    kb: usize,
    page: Page
}

impl MemoryBlock {
    pub fn new(kb: usize, pages: &mut Pages) -> MemoryBlock {
        MemoryBlock {
            kb: kb,
            page: pages.alloc_kb(kb),
        }
    }

    pub fn peek(&self, bus: BusKind, state: &SystemState, addr: u16) -> u8 {
        state.mem.read(self.page, addr)
    }

    pub fn read(&self, bus: BusKind, state: &mut SystemState, addr: u16) -> u8 {
        state.mem.read(self.page, addr)
    }

    pub fn write(&self, bus: BusKind, state: &mut SystemState, addr: u16, val: u8) {
        state.mem.write(self.page, addr, val);
    }
}

pub enum MemKind {
    Prg,
    Chr,
}

pub struct Banks {
    data: Vec<Page>,
    kind: MemKind,
}

impl Banks {
    pub fn load(cart: &Cartridge, kind: MemKind) -> Banks {
        let data: &[u8] = match kind {
            MemKind::Prg => &*cart.prg_rom,
            MemKind::Chr => &*cart.chr_rom,
        };
        let mut v = Vec::new();
        let mut x = 0;
        while x + 0x400 <= data.len() {
            let start = x;
            x += 0x400;
            let end = x;
            v.push(Page { start: start, end: end });
        }
        Banks {
            data: v,
            kind: kind,
        }
    }

    pub fn read(&self, system: &System, bank: usize, addr: u16) -> u8 {
        let bank = self.data[bank];
        match self.kind {
            MemKind::Prg =>
                system.cartridge.prg_rom[bank.start + addr as usize],
            MemKind::Chr =>
                system.cartridge.chr_rom[bank.start + addr as usize],
        }
    }
}

#[derive(Copy, Clone)]
enum Mapped {
    Page(usize),
    Bank(usize),
}

pub enum BankKind {
    Ram,
    Rom,
}

pub struct MappedMemory {
    banks: Banks,
    pages: Vec<Page>,
    base_addr: u16,
    mapping: Vec<Mapped>,
}

impl MappedMemory {
    pub fn new(state: &mut SystemState, cart: &Cartridge, base_addr: u16,
               ram_kb: u32, size_kb: u32, kind: MemKind) -> MappedMemory {
        let mut pages = Vec::new();
        let mut mapping = Vec::new();

        for x in 0..ram_kb {
            pages.push(state.mem.alloc_kb(1));
        }
        for x in 0..size_kb {
            mapping.push(Mapped::Bank(x as usize));
        }
        MappedMemory {
            banks: Banks::load(cart, kind),
            pages: pages,
            base_addr: base_addr,
            mapping: mapping,
        }
    }

    pub fn map(&mut self, addr: u16, kb: u32, bank: usize, bank_kind: BankKind) {
        if addr & 0x3ff != 0 { panic!("Must map in 1kb chunks"); }
        let offset = (addr - self.base_addr) / 0x400;
        let bank_start = (bank * kb as usize) ;//% self.mapping.len();

        match bank_kind {
            BankKind::Rom => {
                for b in 0..kb as usize {
                    self.mapping[offset as usize + b] = Mapped::Bank(b + bank_start);
                }
            },
            BankKind::Ram => {
                for b in 0..kb as usize{
                    self.mapping[offset as usize + b] = Mapped::Page(b + bank_start);
                }
            }
        }
    }

    fn get_mapping(&self, addr: u16) -> Mapped {
        let offset = (addr - self.base_addr) / 0x400;
        self.mapping[offset as usize]
    }

    pub fn read(&self, system: &System, state: &SystemState, addr: u16) -> u8 {
        let mapping = self.get_mapping(addr);

        match mapping {
            Mapped::Bank(b) => {
                self.banks.read(system, b, addr & 0x3ff)
            },
            Mapped::Page(p) => {
                let page = self.pages[p];
                state.mem.read(page, addr & 0x3ff)
            }
        }
    }

    pub fn write(&self, system: &System, state: &mut SystemState, addr: u16, val: u8) {
        let mapping = self.get_mapping(addr);

        match mapping {
            Mapped::Bank(b) => {},
            Mapped::Page(p) => {
                let page = self.pages[p];
                state.mem.write(page, addr & 0x3ff, val);
            }
        }
    }
}
<|endoftext|>