<commit_before>// This module implements the snapshot files used by zakopane.
// ATOW a snapshot file is pretty much the output of the ``sha256sum''
// command with three extra lines atop.

use std::collections::HashMap;
use std::result::Result;
use std::str::Lines;
use std::string::String;

use crate::errors::ZakocmpError;

// Defines the number of lines preceding the actual checksum content.
const HEADER_LINES: usize = 3;

// Defines the number of hex characters in a sha256sum.
const CHECKSUM_CHARS: usize = 64;

// Defines a zakopane snapshot, which maps paths to checksums.
#[derive(Debug)]
pub struct Snapshot {
    contents: HashMap<String, String>,
}

// Borrows the string representation of a line in a zakopane snapshot
// and returns sliced str's in a tuple of (checksum, path).
fn parse_snapshot_line(line: &str) -> Result<(&str, &str), ZakocmpError> {
    let bad_line = ZakocmpError::Snapshot(format!("malformed snapshot line: ``{}''", line));
    // A snapshot line should consist of the checksum, two spaces, and a
    // non-empty pathname.
    if line.len() < CHECKSUM_CHARS + 3
        || !line.is_char_boundary(CHECKSUM_CHARS)
        || !line.is_char_boundary(CHECKSUM_CHARS + 1)
        || !line.is_char_boundary(CHECKSUM_CHARS + 2)
    {
        return Err(bad_line);
    }

    let (checksum, path_with_leading_space) = line.split_at(CHECKSUM_CHARS);
    if !path_with_leading_space.starts_with("  ") {
        return Err(bad_line);
    }
    Ok((checksum, &path_with_leading_space[2..]))
}

impl Snapshot {
    // Borrows the string representation of a zakopane snapshot and
    // returns the corresponding Snapshot struct.
    pub fn new(snapshot: &str) -> Result<Snapshot, ZakocmpError> {
        let mut lines: Lines = snapshot.lines();

        // A zakopane snapshot starts with three extra lines intended
        // for human readers. zakocmp doesn't care about this header.
        let mut header_drain: usize = HEADER_LINES;
        while header_drain > 0 {
            match lines.next() {
                Some(_) => (),
                None => {
                    return Err(ZakocmpError::Snapshot(
                        "truncated zakopane snapshot".to_string(),
                    ))
                }
            };
            header_drain -= 1;
        }

        // Ingests the rest of the snapshot representation.
        let mut contents: HashMap<String, String> = HashMap::new();
        for line in lines {
            let (checksum, path) = parse_snapshot_line(line)?;
            match contents.insert(path.to_string(), checksum.to_string()) {
                None => (),
                Some(old_checksum) => {
                    return Err(ZakocmpError::Snapshot(format!(
                        "path collision: {} (was already {}, is now {})",
                        path, old_checksum, checksum
                    )))
                }
            };
        }
        Ok(Snapshot { contents: contents })
    }

    // Passes the inner struct's iterator straight out.
    pub fn iter(&self) -> std::collections::hash_map::Iter<String, String> {
        self.contents.iter()
    }

    // Returns a reference to the checksum of the file (if present).
    pub fn get(&self, key: &str) -> std::option::Option<&String> {
        self.contents.get(key)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    // Defines a valid zakopane snapshot header.
    const SNAPSHOT_HEADER_FOR_TESTING: &'static str = r#"simple-zakopane.sh: 2019-07-27-090032
simple-zakopane.sh: /home/kalvin
# this line is typically empty but must be present
"#;

    // Accepts a borrowed string representation of some zakopane
    // checksums, prepends the standard zakopane snapshot header to the
    // same, and returns the owned result.
    fn snapshot_string_for_testing(checksums: &str) -> String {
        let mut snapshot: String = SNAPSHOT_HEADER_FOR_TESTING.to_string();
        snapshot.push_str(checksums);
        snapshot
    }

    // Consumes a ZakocmpError and borrows a string slice. Asserts that
    // the error is of the Snapshot variant and starts with the string
    // slice.
    fn assert_snapshot_error(error: ZakocmpError, prefix: &str) {
        match error {
            ZakocmpError::Snapshot(message) => assert!(message.starts_with(prefix)),
            _ => panic!("expected ZakocmpError::Snapshot"),
        };
    }

    #[test]
    fn snapshot_must_have_proper_header() {
        let snapshot = Snapshot::new(SNAPSHOT_HEADER_FOR_TESTING).unwrap();
        assert_eq!(snapshot.contents.len(), 0);

        let snapshot_without_proper_header = r#"simple-zakopane.sh: 2019-07-27-090032
simple-zakopane.sh: /home/kalvin
"#;
        assert_snapshot_error(
            Snapshot::new(snapshot_without_proper_header).unwrap_err(),
            "truncated",
        );
    }

    #[test]
    fn snapshot_checksum_is_hex() {
        let checksum_ok =
            "4e8401b759a877c0d215ba95bb75bd7d08318cbdc395b3fae9763337ee3614a5  ./hello/there.txt";
        let snapshot = Snapshot::new(&snapshot_string_for_testing(checksum_ok)).unwrap();
        assert_eq!(snapshot.contents.len(), 1);

        // Defines the literal string "malformed" that appears
        // repeatedly in the following tests.
        let malformed: &str = "malformed";

        // Oh no! This checksum dropped a character off the end.
        let checksum_short =
            "4e8401b759a877c0d215ba95bb75bd7d08318cbdc395b3fae9763337ee3614a  ./hello/there.txt";
        assert_snapshot_error(
            Snapshot::new(&snapshot_string_for_testing(checksum_short)).unwrap_err(),
            malformed,
        );

        // Oh no! This checksum line does not refer to a path.
        let checksum_without_path =
            "4e8401b759a877c0d215ba95bb75bd7d08318cbdc395b3fae9763337ee3614a5  ";
        assert_snapshot_error(
            Snapshot::new(&snapshot_string_for_testing(checksum_without_path)).unwrap_err(),
            malformed,
        );

        // Checksum lines may not be empty or too short.
        assert_snapshot_error(
            Snapshot::new(&snapshot_string_for_testing("\n")).unwrap_err(),
            malformed,
        );
        assert_snapshot_error(
            Snapshot::new(&snapshot_string_for_testing("Hello there!")).unwrap_err(),
            malformed,
        );
    }

    #[test]
    fn snapshot_paths_may_not_repeat() {
        let checksums =
            r#"4e8401b759a877c0d215ba95bb75bd7d08318cbdc395b3fae9763337ee3614a5  ./hello/there.txt
0000000000000000000000000000000000000000000000000000000000000000  ./hello/there.txt"#;
        // The point of this test is not to catch identical checksums
        // (which occur naturally if you ever accidentally duplicate
        // files), but to catch repeated paths (which should not be
        // possible in a well-formed snapshot file).
        assert_snapshot_error(
            Snapshot::new(&snapshot_string_for_testing(checksums)).unwrap_err(),
            "path collision",
        );
    }

    #[test]
    fn snapshot_get() {
        // Creates a snapshot describing two files, each with contrived
        // but valid-looking checksums.
        let snapshot = Snapshot::new(&snapshot_string_for_testing(
            r#"0000000000000000000000000000000000000000000000000000000000000001  ./hello/there.txt
0000000000000000000000000000000000000000000000000000000000000002  ./general/kenobi.txt
00000000000000000000000000000000000000000000000000000000000000ff  ./you/are.txt
00000000000000000000000000000000000000000000000000000000000001ff  ./a/bold-one.txt
"#,
        ))
        .unwrap();
        assert_eq!(
            snapshot.get("./hello/there.txt").unwrap(),
            "0000000000000000000000000000000000000000000000000000000000000001"
        );
        assert_eq!(
            snapshot.get("./general/kenobi.txt").unwrap(),
            "0000000000000000000000000000000000000000000000000000000000000002"
        );
        assert_eq!(
            snapshot.get("./you/are.txt").unwrap(),
            "00000000000000000000000000000000000000000000000000000000000000ff"
        );
        assert_eq!(
            snapshot.get("./a/bold-one.txt").unwrap(),
            "00000000000000000000000000000000000000000000000000000000000001ff"
        );

        assert!(snapshot.get("blah-blah-nonexistent-key").is_none());
        // Note that Snapshots don't perform any path comprehension - as
        // far as a Snapshot key is concerned, it's an arbitrary
        // sequence of bytes.
        assert!(snapshot.get("a/bold-one.txt").is_none());
    }
}
<commit_msg>snapshot: publicize snapshot_string_for_testing()<commit_after>// This module implements the snapshot files used by zakopane.
// ATOW a snapshot file is pretty much the output of the ``sha256sum''
// command with three extra lines atop.

use std::collections::HashMap;
use std::result::Result;
use std::str::Lines;
use std::string::String;

use crate::errors::ZakocmpError;

// Defines the number of lines preceding the actual checksum content.
const HEADER_LINES: usize = 3;

// Defines the number of hex characters in a sha256sum.
const CHECKSUM_CHARS: usize = 64;

// Defines a valid zakopane snapshot header.
const SNAPSHOT_HEADER_FOR_TESTING: &'static str = r#"simple-zakopane.sh: 2019-07-27-090032
simple-zakopane.sh: /home/kalvin
# this line is typically empty but must be present
"#;

// Accepts a borrowed string representation of some zakopane
// checksums, prepends the standard zakopane snapshot header to the
// same, and returns the owned result.
pub fn snapshot_string_for_testing(checksums: &str) -> String {
    let mut snapshot: String = SNAPSHOT_HEADER_FOR_TESTING.to_string();
    snapshot.push_str(checksums);
    snapshot
}

// Defines a zakopane snapshot, which maps paths to checksums.
#[derive(Debug)]
pub struct Snapshot {
    contents: HashMap<String, String>,
}

// Borrows the string representation of a line in a zakopane snapshot
// and returns sliced str's in a tuple of (checksum, path).
fn parse_snapshot_line(line: &str) -> Result<(&str, &str), ZakocmpError> {
    let bad_line = ZakocmpError::Snapshot(format!("malformed snapshot line: ``{}''", line));
    // A snapshot line should consist of the checksum, two spaces, and a
    // non-empty pathname.
    if line.len() < CHECKSUM_CHARS + 3
        || !line.is_char_boundary(CHECKSUM_CHARS)
        || !line.is_char_boundary(CHECKSUM_CHARS + 1)
        || !line.is_char_boundary(CHECKSUM_CHARS + 2)
    {
        return Err(bad_line);
    }

    let (checksum, path_with_leading_space) = line.split_at(CHECKSUM_CHARS);
    if !path_with_leading_space.starts_with("  ") {
        return Err(bad_line);
    }
    Ok((checksum, &path_with_leading_space[2..]))
}

impl Snapshot {
    // Borrows the string representation of a zakopane snapshot and
    // returns the corresponding Snapshot struct.
    pub fn new(snapshot: &str) -> Result<Snapshot, ZakocmpError> {
        let mut lines: Lines = snapshot.lines();

        // A zakopane snapshot starts with three extra lines intended
        // for human readers. zakocmp doesn't care about this header.
        let mut header_drain: usize = HEADER_LINES;
        while header_drain > 0 {
            match lines.next() {
                Some(_) => (),
                None => {
                    return Err(ZakocmpError::Snapshot(
                        "truncated zakopane snapshot".to_string(),
                    ))
                }
            };
            header_drain -= 1;
        }

        // Ingests the rest of the snapshot representation.
        let mut contents: HashMap<String, String> = HashMap::new();
        for line in lines {
            let (checksum, path) = parse_snapshot_line(line)?;
            match contents.insert(path.to_string(), checksum.to_string()) {
                None => (),
                Some(old_checksum) => {
                    return Err(ZakocmpError::Snapshot(format!(
                        "path collision: {} (was already {}, is now {})",
                        path, old_checksum, checksum
                    )))
                }
            };
        }
        Ok(Snapshot { contents: contents })
    }

    // Passes the inner struct's iterator straight out.
    pub fn iter(&self) -> std::collections::hash_map::Iter<String, String> {
        self.contents.iter()
    }

    // Returns a reference to the checksum of the file (if present).
    pub fn get(&self, key: &str) -> std::option::Option<&String> {
        self.contents.get(key)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    // Consumes a ZakocmpError and borrows a string slice. Asserts that
    // the error is of the Snapshot variant and starts with the string
    // slice.
    fn assert_snapshot_error(error: ZakocmpError, prefix: &str) {
        match error {
            ZakocmpError::Snapshot(message) => assert!(message.starts_with(prefix)),
            _ => panic!("expected ZakocmpError::Snapshot"),
        };
    }

    #[test]
    fn snapshot_must_have_proper_header() {
        let snapshot = Snapshot::new(SNAPSHOT_HEADER_FOR_TESTING).unwrap();
        assert_eq!(snapshot.contents.len(), 0);

        let snapshot_without_proper_header = r#"simple-zakopane.sh: 2019-07-27-090032
simple-zakopane.sh: /home/kalvin
"#;
        assert_snapshot_error(
            Snapshot::new(snapshot_without_proper_header).unwrap_err(),
            "truncated",
        );
    }

    #[test]
    fn snapshot_checksum_is_hex() {
        let checksum_ok =
            "4e8401b759a877c0d215ba95bb75bd7d08318cbdc395b3fae9763337ee3614a5  ./hello/there.txt";
        let snapshot = Snapshot::new(&snapshot_string_for_testing(checksum_ok)).unwrap();
        assert_eq!(snapshot.contents.len(), 1);

        // Defines the literal string "malformed" that appears
        // repeatedly in the following tests.
        let malformed: &str = "malformed";

        // Oh no! This checksum dropped a character off the end.
        let checksum_short =
            "4e8401b759a877c0d215ba95bb75bd7d08318cbdc395b3fae9763337ee3614a  ./hello/there.txt";
        assert_snapshot_error(
            Snapshot::new(&snapshot_string_for_testing(checksum_short)).unwrap_err(),
            malformed,
        );

        // Oh no! This checksum line does not refer to a path.
        let checksum_without_path =
            "4e8401b759a877c0d215ba95bb75bd7d08318cbdc395b3fae9763337ee3614a5  ";
        assert_snapshot_error(
            Snapshot::new(&snapshot_string_for_testing(checksum_without_path)).unwrap_err(),
            malformed,
        );

        // Checksum lines may not be empty or too short.
        assert_snapshot_error(
            Snapshot::new(&snapshot_string_for_testing("\n")).unwrap_err(),
            malformed,
        );
        assert_snapshot_error(
            Snapshot::new(&snapshot_string_for_testing("Hello there!")).unwrap_err(),
            malformed,
        );
    }

    #[test]
    fn snapshot_paths_may_not_repeat() {
        let checksums =
            r#"4e8401b759a877c0d215ba95bb75bd7d08318cbdc395b3fae9763337ee3614a5  ./hello/there.txt
0000000000000000000000000000000000000000000000000000000000000000  ./hello/there.txt"#;
        // The point of this test is not to catch identical checksums
        // (which occur naturally if you ever accidentally duplicate
        // files), but to catch repeated paths (which should not be
        // possible in a well-formed snapshot file).
        assert_snapshot_error(
            Snapshot::new(&snapshot_string_for_testing(checksums)).unwrap_err(),
            "path collision",
        );
    }

    #[test]
    fn snapshot_get() {
        // Creates a snapshot describing two files, each with contrived
        // but valid-looking checksums.
        let snapshot = Snapshot::new(&snapshot_string_for_testing(
            r#"0000000000000000000000000000000000000000000000000000000000000001  ./hello/there.txt
0000000000000000000000000000000000000000000000000000000000000002  ./general/kenobi.txt
00000000000000000000000000000000000000000000000000000000000000ff  ./you/are.txt
00000000000000000000000000000000000000000000000000000000000001ff  ./a/bold-one.txt
"#,
        ))
        .unwrap();
        assert_eq!(
            snapshot.get("./hello/there.txt").unwrap(),
            "0000000000000000000000000000000000000000000000000000000000000001"
        );
        assert_eq!(
            snapshot.get("./general/kenobi.txt").unwrap(),
            "0000000000000000000000000000000000000000000000000000000000000002"
        );
        assert_eq!(
            snapshot.get("./you/are.txt").unwrap(),
            "00000000000000000000000000000000000000000000000000000000000000ff"
        );
        assert_eq!(
            snapshot.get("./a/bold-one.txt").unwrap(),
            "00000000000000000000000000000000000000000000000000000000000001ff"
        );

        assert!(snapshot.get("blah-blah-nonexistent-key").is_none());
        // Note that Snapshots don't perform any path comprehension - as
        // far as a Snapshot key is concerned, it's an arbitrary
        // sequence of bytes.
        assert!(snapshot.get("a/bold-one.txt").is_none());
    }
}
<|endoftext|>


<commit_before>use geometry::Point;
use geometry::Rect;
use geometry::Size;

/**
 Elements that may be contained by a quadtree node.
 Either child nodes, a single rect, or nothing.
 */
pub enum Elements {
    /// Children are top left, top right, bottom right, and bottom left, respectively.
    Children(~QuadTree, ~QuadTree, ~QuadTree, ~QuadTree),
    /// A single rectangle.
    Member(Rect),
    /// Nothing.
    NoElements,
}

/**
 A quadtree node that can contain either one rectangle,
 or exactly four child nodes.
 */
pub struct QuadTree {
    rect: Rect,
    elements: Elements,
}

impl QuadTree {
    /**
     Create a quadtree with a root node with the given origin and size.
     */
    pub fn new(origin: Point, size: Size, elems: Elements) -> QuadTree {
        let tree =
        match elems {
            Children(tl, tr, br, bl) => QuadTree::newWithChildren(origin, size, tl, tr, br, bl),
            Member(rect) => QuadTree::newWithMember(origin, size, rect),
            NoElements => QuadTree::newWithSize(origin, size),
        };

        tree
    }

    /**
     Create a quadtree with a root node with the same origin and a square
     size with side length matching the longer dimension of `rect`.
     */
    pub fn newAutosized(rect: Rect) -> QuadTree {
        let largerDimen = if rect.size.width > rect.size.height {
            rect.size.width
        } else {
            rect.size.height
        };

        let size = Size::new(largerDimen, largerDimen);

        QuadTree::newWithMember(rect.origin, size, rect)
    }

    /**
     Create an empty quadtree with a zero-sized root node.
     */
    pub fn newEmpty() -> QuadTree {
        let origin = Point::new(0., 0.);
        let size = Size::new(0., 0.);
        let tree = QuadTree::new(origin, size, NoElements);

        tree
    }

    /**
     Create a quadtree with a root node with the given origin, size, and child rectangles.
     Child nodes `tl`, `tr`, `br`, and `bl` should form the rect specified by `origin` and `size`.
     */
    fn newWithChildren(origin: Point, size: Size, tl: ~QuadTree, tr: ~QuadTree, br: ~QuadTree, bl: ~QuadTree) -> QuadTree {
        let nodeRect = Rect::new(origin, size);

        // Assert that our rect and our childrens' rects match up.
        assert!(tl.rect.minX() == bl.rect.minX(), "Top left and bottom left children should have same minX coordinate.");
        assert!(tl.rect.maxX() == bl.rect.maxX(), "Top left and bottom left children should have same maxX coordinate.");
        assert!(tl.rect.maxY() == bl.rect.minY(), "Top left and bottom left children should have same maxY and minY coordinates, respectively.");

        assert!(tl.rect.maxX() == tr.rect.minX(), "Top left and top right children should share their maxX and minX coordinates, respectively.");
        assert!(tl.rect.minY() == tr.rect.minY(), "Top left and top right children should have same minY coordinate.");
        assert!(tl.rect.maxY() == tr.rect.maxY(), "Top left and top right children should have same maxY coordinate.");

        assert!(bl.rect.maxX() == br.rect.minX(), "Bottom left and bottom right children should share their maxX and minX coordinates, respectively.");
        assert!(bl.rect.minY() == br.rect.minY(), "Bottom left and bottom right children should have same minY coordinate.");
        assert!(bl.rect.maxY() == br.rect.maxY(), "Bottom left and bottom right children should have same maxY coordinate.");

        assert!(tr.rect.minX() == br.rect.minX(), "Top right and bottom right children should have same minX coordinate.");
        assert!(tr.rect.maxX() == br.rect.maxX(), "Top right and bottom right children should have same maxX coordinate.");
        assert!(tr.rect.maxY() == br.rect.minY(), "Top right and bottom right children should have same maxY and minY coordinates, respectively.");

        assert!(nodeRect.minX() == tl.rect.minX(), "Tree node's minX should match its top left child's minX coordinate.");
        assert!(nodeRect.maxX() == tr.rect.maxX(), "Tree node's maxX should match its top right child's maxX coordinate.");
        assert!(nodeRect.minY() == tl.rect.minY(), "Tree node's minY should match its top left child's minY coordinate.");
        assert!(nodeRect.maxY() == bl.rect.maxY(), "Tree node's maxY should match its top left child's maxY coordinate.");

        let tree = QuadTree { rect: nodeRect, elements: Children(tl, tr, br, bl) };

        tree
    }

    /**
     Create a quadtree with a root node with the given origin, size, and member rectangle.
     */
    fn newWithMember(origin: Point, size: Size, insertRect: Rect) -> QuadTree {
        let node = QuadTree::newWithSize(origin, size);

        let (success, tree) = node.insertRect(insertRect);
        assert!(success, "Failed inserting a node into an empty quadtree.");

        tree
    }

    /**
     Create a quadtree with only a specified size and position.
     */
    fn newWithSize(origin: Point, size: Size) -> QuadTree {
        let nodeRect = Rect::new(origin, size);
        let tree = QuadTree { rect: nodeRect, elements: NoElements };

        tree
    }

    /**
     Insert a rectangle into the quadtree. If `toInsert` overlaps another rectangle
     already in the tree, the return value will be (false, self).
     If the root node is zero-sized, the resulting tree will have a square root node
     large enough to hold `toInsert`.
     */
    pub fn insertRect(self, toInsert: Rect) -> (bool, QuadTree) {
        if self.rect.width() == 0.0 {
            return (true, QuadTree::newAutosized(toInsert))
        }

        let rectsInChildren = self.rectsInChildNodesIntersectedByRect(&toInsert);
        if rectsInChildren.len() > 0 {
            return (false, self)
        }

        let mut node = self;
        let mut needBigger = node.rect.contains(&toInsert);

        while needBigger {
            let width = node.rect.width();
            let height = node.rect.height();
            let origin = node.rect.origin;
            let size = node.rect.size;

            let wPoint = Point::new(width, 0.);
            let hPoint = Point::new(0., height);

            // Check if the rect to insert extends to the left or "above" our origin,
            // i.e. has a lower x or y coordinate in its origin.
            // Use this information to determine if we must grow the tree left, up, right, or down.
            let left = node.rect.minX() < toInsert.minX();
            let top = node.rect.minX() < toInsert.minY();

            let (tl, tr, bl, br) =
            match (left, top) {
                (true, true) => (QuadTree::newWithSize(origin.subtract(wPoint).add(hPoint), size),
                                 QuadTree::newWithSize(origin.subtract(hPoint), size),
                                 QuadTree::newWithSize(origin.subtract(wPoint), size),
                                 node),
                (true, false) => (QuadTree::newWithSize(origin.subtract(wPoint), size),
                                  node,
                                  QuadTree::newWithSize(origin.subtract(wPoint).subtract(hPoint), size),
                                  QuadTree::newWithSize(origin.subtract(hPoint), size)),
                (false, true) => (QuadTree::newWithSize(origin.add(hPoint), size),
                                  QuadTree::newWithSize(origin.add(hPoint).add(wPoint), size),
                                  node,
                                  QuadTree::newWithSize(origin.add(wPoint), size)),
                (false, false) => (node,
                                   QuadTree::newWithSize(origin.add(wPoint), size),
                                   QuadTree::newWithSize(origin.add(hPoint), size),
                                   QuadTree::newWithSize(origin.add(wPoint).add(hPoint), size)),
            };

            let (tl, tr, bl, br) = (tl.insertRectIfIntersects(toInsert),
                                    tr.insertRectIfIntersects(toInsert),
                                    bl.insertRectIfIntersects(toInsert),
                                    br.insertRectIfIntersects(toInsert),);

            node = QuadTree::newWithChildren(tl.rect.origin,
                Size::new(width * 2., height * 2.),
                ~tl, ~tr, ~br, ~bl);

            needBigger = node.rect.contains(&toInsert);
        }

        (true, node)
    }

    /**
     Insert a rectangle into the node IFF the rectangle intersects the node.
     Does nothing if `toInsert` intersects our existing member rect.
     */
    fn insertRectIfIntersects(self, toInsert: Rect) -> QuadTree {
        if self.rect.intersects(&toInsert) {
            let origin = self.rect.origin;
            let size = self.rect.size;
            match self.elements {
                Children(~tl, ~tr, ~br, ~bl) => {
                    let (tl, tr, br, bl) = (tl.insertRectIfIntersects(toInsert),
                                            tr.insertRectIfIntersects(toInsert),
                                            br.insertRectIfIntersects(toInsert),
                                            bl.insertRectIfIntersects(toInsert),);
                    QuadTree::newWithChildren(origin, size, ~tl, ~tr, ~br, ~bl)
                },
                Member(rect) => {
                    if rect.intersects(&toInsert) {
                        self
                    } else {
                        let (tl, tr, br, bl) = QuadTree::makeChildrenForRect(&self.rect);

                        let (tl, tr, br, bl) = (tl.insertRectIfIntersects(rect),
                                                tr.insertRectIfIntersects(rect),
                                                br.insertRectIfIntersects(rect),
                                                bl.insertRectIfIntersects(rect),);
                        let (tl, tr, br, bl) = (tl.insertRectIfIntersects(toInsert),
                                                tr.insertRectIfIntersects(toInsert),
                                                br.insertRectIfIntersects(toInsert),
                                                bl.insertRectIfIntersects(toInsert),);

                        QuadTree::newWithChildren(origin, size, ~tl, ~tr, ~br, ~bl)
                    }
                },
                NoElements => QuadTree::newWithMember(origin, size, toInsert),
            }
        } else {
            self
        }
    }

    /**
     Create four nodes suitable for use as children, covering the passed in rect.
     */
    fn makeChildrenForRect(rect: &Rect) -> (~QuadTree, ~QuadTree, ~QuadTree, ~QuadTree,) {
        let origin = rect.origin;
        let size = rect.size;

        let newSize = Size::new(size.width / 2., size.height / 2.);
        let wPoint = Point::new(newSize.width, 0.);
        let hPoint = Point::new(0., newSize.height);

        let (tl, tr, br, bl) = (QuadTree::newWithSize(origin, newSize),
                                QuadTree::newWithSize(origin.add(wPoint), newSize),
                                QuadTree::newWithSize(origin.add(wPoint).add(hPoint), newSize),
                                QuadTree::newWithSize(origin.add(hPoint), newSize),);

        (~tl, ~tr, ~br, ~bl)
    }

    /**
     Find all of the rects in `self`, or its children, that are in nodes
     intersected by the given rect.
     */
    pub fn rectsInChildNodesIntersectedByRect(&self, testRect: &Rect) -> ~[Rect] {
        // If the test rect doesn't intersect us, then it can't intersect
        // any rects that we have.
        match self.rect.intersect(testRect) {
            Some(intersection) => {
                let mut rects = ~[];
                let mut nodesToCheck = ~[self];

                while nodesToCheck.len() > 0 {
                    let mut newNodesToCheck = ~[];

                    for node in nodesToCheck.iter() {
                        match node.elements {
                            Children(~ref tl, ~ref tr, ~ref br, ~ref bl) => {
                                let mut check = ~[];
                                let intersection = &intersection;
                                if tl.rect.intersects(intersection) {
                                    check.push(tl);
                                }
                                if tr.rect.intersects(intersection) {
                                    check.push(tr);
                                }
                                if br.rect.intersects(intersection) {
                                    check.push(br);
                                }
                                if bl.rect.intersects(intersection) {
                                    check.push(bl);
                                }
                                newNodesToCheck.push_all(check);
                            }
                            Member(memberRect) => rects.push(memberRect),
                            NoElements => ()
                        };
                    }

                    nodesToCheck = newNodesToCheck;
                }

                rects
            },
            None => ~[],
        }
    }
}
<commit_msg>Fix inverted logic for checking if a node is big enough.<commit_after>use geometry::Point;
use geometry::Rect;
use geometry::Size;

/**
 Elements that may be contained by a quadtree node.
 Either child nodes, a single rect, or nothing.
 */
pub enum Elements {
    /// Children are top left, top right, bottom right, and bottom left, respectively.
    Children(~QuadTree, ~QuadTree, ~QuadTree, ~QuadTree),
    /// A single rectangle.
    Member(Rect),
    /// Nothing.
    NoElements,
}

/**
 A quadtree node that can contain either one rectangle,
 or exactly four child nodes.
 */
pub struct QuadTree {
    rect: Rect,
    elements: Elements,
}

impl QuadTree {
    /**
     Create a quadtree with a root node with the given origin and size.
     */
    pub fn new(origin: Point, size: Size, elems: Elements) -> QuadTree {
        let tree =
        match elems {
            Children(tl, tr, br, bl) => QuadTree::newWithChildren(origin, size, tl, tr, br, bl),
            Member(rect) => QuadTree::newWithMember(origin, size, rect),
            NoElements => QuadTree::newWithSize(origin, size),
        };

        tree
    }

    /**
     Create a quadtree with a root node with the same origin and a square
     size with side length matching the longer dimension of `rect`.
     */
    pub fn newAutosized(rect: Rect) -> QuadTree {
        let largerDimen = if rect.size.width > rect.size.height {
            rect.size.width
        } else {
            rect.size.height
        };

        let size = Size::new(largerDimen, largerDimen);

        QuadTree::newWithMember(rect.origin, size, rect)
    }

    /**
     Create an empty quadtree with a zero-sized root node.
     */
    pub fn newEmpty() -> QuadTree {
        let origin = Point::new(0., 0.);
        let size = Size::new(0., 0.);
        let tree = QuadTree::new(origin, size, NoElements);

        tree
    }

    /**
     Create a quadtree with a root node with the given origin, size, and child rectangles.
     Child nodes `tl`, `tr`, `br`, and `bl` should form the rect specified by `origin` and `size`.
     */
    fn newWithChildren(origin: Point, size: Size, tl: ~QuadTree, tr: ~QuadTree, br: ~QuadTree, bl: ~QuadTree) -> QuadTree {
        let nodeRect = Rect::new(origin, size);

        // Assert that our rect and our childrens' rects match up.
        assert!(tl.rect.minX() == bl.rect.minX(), "Top left and bottom left children should have same minX coordinate.");
        assert!(tl.rect.maxX() == bl.rect.maxX(), "Top left and bottom left children should have same maxX coordinate.");
        assert!(tl.rect.maxY() == bl.rect.minY(), "Top left and bottom left children should have same maxY and minY coordinates, respectively.");

        assert!(tl.rect.maxX() == tr.rect.minX(), "Top left and top right children should share their maxX and minX coordinates, respectively.");
        assert!(tl.rect.minY() == tr.rect.minY(), "Top left and top right children should have same minY coordinate.");
        assert!(tl.rect.maxY() == tr.rect.maxY(), "Top left and top right children should have same maxY coordinate.");

        assert!(bl.rect.maxX() == br.rect.minX(), "Bottom left and bottom right children should share their maxX and minX coordinates, respectively.");
        assert!(bl.rect.minY() == br.rect.minY(), "Bottom left and bottom right children should have same minY coordinate.");
        assert!(bl.rect.maxY() == br.rect.maxY(), "Bottom left and bottom right children should have same maxY coordinate.");

        assert!(tr.rect.minX() == br.rect.minX(), "Top right and bottom right children should have same minX coordinate.");
        assert!(tr.rect.maxX() == br.rect.maxX(), "Top right and bottom right children should have same maxX coordinate.");
        assert!(tr.rect.maxY() == br.rect.minY(), "Top right and bottom right children should have same maxY and minY coordinates, respectively.");

        assert!(nodeRect.minX() == tl.rect.minX(), "Tree node's minX should match its top left child's minX coordinate.");
        assert!(nodeRect.maxX() == tr.rect.maxX(), "Tree node's maxX should match its top right child's maxX coordinate.");
        assert!(nodeRect.minY() == tl.rect.minY(), "Tree node's minY should match its top left child's minY coordinate.");
        assert!(nodeRect.maxY() == bl.rect.maxY(), "Tree node's maxY should match its top left child's maxY coordinate.");

        let tree = QuadTree { rect: nodeRect, elements: Children(tl, tr, br, bl) };

        tree
    }

    /**
     Create a quadtree with a root node with the given origin, size, and member rectangle.
     */
    fn newWithMember(origin: Point, size: Size, insertRect: Rect) -> QuadTree {
        let node = QuadTree::newWithSize(origin, size);

        let (success, tree) = node.insertRect(insertRect);
        assert!(success, "Failed inserting a node into an empty quadtree.");

        tree
    }

    /**
     Create a quadtree with only a specified size and position.
     */
    fn newWithSize(origin: Point, size: Size) -> QuadTree {
        let nodeRect = Rect::new(origin, size);
        let tree = QuadTree { rect: nodeRect, elements: NoElements };

        tree
    }

    /**
     Insert a rectangle into the quadtree. If `toInsert` overlaps another rectangle
     already in the tree, the return value will be (false, self).
     If the root node is zero-sized, the resulting tree will have a square root node
     large enough to hold `toInsert`.
     */
    pub fn insertRect(self, toInsert: Rect) -> (bool, QuadTree) {
        if self.rect.width() == 0.0 {
            return (true, QuadTree::newAutosized(toInsert))
        }

        let rectsInChildren = self.rectsInChildNodesIntersectedByRect(&toInsert);
        if rectsInChildren.len() > 0 {
            return (false, self)
        }

        let mut node = self;
        let mut bigEnough = node.rect.contains(&toInsert);

        while !bigEnough {
            let width = node.rect.width();
            let height = node.rect.height();
            let origin = node.rect.origin;
            let size = node.rect.size;

            let wPoint = Point::new(width, 0.);
            let hPoint = Point::new(0., height);

            // Check if the rect to insert extends to the left or "above" our origin,
            // i.e. has a lower x or y coordinate in its origin.
            // Use this information to determine if we must grow the tree left, up, right, or down.
            let left = node.rect.minX() < toInsert.minX();
            let top = node.rect.minX() < toInsert.minY();

            let (tl, tr, bl, br) =
            match (left, top) {
                (true, true) => (QuadTree::newWithSize(origin.subtract(wPoint).add(hPoint), size),
                                 QuadTree::newWithSize(origin.subtract(hPoint), size),
                                 QuadTree::newWithSize(origin.subtract(wPoint), size),
                                 node),
                (true, false) => (QuadTree::newWithSize(origin.subtract(wPoint), size),
                                  node,
                                  QuadTree::newWithSize(origin.subtract(wPoint).subtract(hPoint), size),
                                  QuadTree::newWithSize(origin.subtract(hPoint), size)),
                (false, true) => (QuadTree::newWithSize(origin.add(hPoint), size),
                                  QuadTree::newWithSize(origin.add(hPoint).add(wPoint), size),
                                  node,
                                  QuadTree::newWithSize(origin.add(wPoint), size)),
                (false, false) => (node,
                                   QuadTree::newWithSize(origin.add(wPoint), size),
                                   QuadTree::newWithSize(origin.add(hPoint), size),
                                   QuadTree::newWithSize(origin.add(wPoint).add(hPoint), size)),
            };

            let (tl, tr, bl, br) = (tl.insertRectIfIntersects(toInsert),
                                    tr.insertRectIfIntersects(toInsert),
                                    bl.insertRectIfIntersects(toInsert),
                                    br.insertRectIfIntersects(toInsert),);

            node = QuadTree::newWithChildren(tl.rect.origin,
                Size::new(width * 2., height * 2.),
                ~tl, ~tr, ~br, ~bl);

            bigEnough = node.rect.contains(&toInsert);
        }

        (true, node)
    }

    /**
     Insert a rectangle into the node IFF the rectangle intersects the node.
     Does nothing if `toInsert` intersects our existing member rect.
     */
    fn insertRectIfIntersects(self, toInsert: Rect) -> QuadTree {
        if self.rect.intersects(&toInsert) {
            let origin = self.rect.origin;
            let size = self.rect.size;
            match self.elements {
                Children(~tl, ~tr, ~br, ~bl) => {
                    let (tl, tr, br, bl) = (tl.insertRectIfIntersects(toInsert),
                                            tr.insertRectIfIntersects(toInsert),
                                            br.insertRectIfIntersects(toInsert),
                                            bl.insertRectIfIntersects(toInsert),);
                    QuadTree::newWithChildren(origin, size, ~tl, ~tr, ~br, ~bl)
                },
                Member(rect) => {
                    if rect.intersects(&toInsert) {
                        self
                    } else {
                        let (tl, tr, br, bl) = QuadTree::makeChildrenForRect(&self.rect);

                        let (tl, tr, br, bl) = (tl.insertRectIfIntersects(rect),
                                                tr.insertRectIfIntersects(rect),
                                                br.insertRectIfIntersects(rect),
                                                bl.insertRectIfIntersects(rect),);
                        let (tl, tr, br, bl) = (tl.insertRectIfIntersects(toInsert),
                                                tr.insertRectIfIntersects(toInsert),
                                                br.insertRectIfIntersects(toInsert),
                                                bl.insertRectIfIntersects(toInsert),);

                        QuadTree::newWithChildren(origin, size, ~tl, ~tr, ~br, ~bl)
                    }
                },
                NoElements => QuadTree::newWithMember(origin, size, toInsert),
            }
        } else {
            self
        }
    }

    /**
     Create four nodes suitable for use as children, covering the passed in rect.
     */
    fn makeChildrenForRect(rect: &Rect) -> (~QuadTree, ~QuadTree, ~QuadTree, ~QuadTree,) {
        let origin = rect.origin;
        let size = rect.size;

        let newSize = Size::new(size.width / 2., size.height / 2.);
        let wPoint = Point::new(newSize.width, 0.);
        let hPoint = Point::new(0., newSize.height);

        let (tl, tr, br, bl) = (QuadTree::newWithSize(origin, newSize),
                                QuadTree::newWithSize(origin.add(wPoint), newSize),
                                QuadTree::newWithSize(origin.add(wPoint).add(hPoint), newSize),
                                QuadTree::newWithSize(origin.add(hPoint), newSize),);

        (~tl, ~tr, ~br, ~bl)
    }

    /**
     Find all of the rects in `self`, or its children, that are in nodes
     intersected by the given rect.
     */
    pub fn rectsInChildNodesIntersectedByRect(&self, testRect: &Rect) -> ~[Rect] {
        // If the test rect doesn't intersect us, then it can't intersect
        // any rects that we have.
        match self.rect.intersect(testRect) {
            Some(intersection) => {
                let mut rects = ~[];
                let mut nodesToCheck = ~[self];

                while nodesToCheck.len() > 0 {
                    let mut newNodesToCheck = ~[];

                    for node in nodesToCheck.iter() {
                        match node.elements {
                            Children(~ref tl, ~ref tr, ~ref br, ~ref bl) => {
                                let mut check = ~[];
                                let intersection = &intersection;
                                if tl.rect.intersects(intersection) {
                                    check.push(tl);
                                }
                                if tr.rect.intersects(intersection) {
                                    check.push(tr);
                                }
                                if br.rect.intersects(intersection) {
                                    check.push(br);
                                }
                                if bl.rect.intersects(intersection) {
                                    check.push(bl);
                                }
                                newNodesToCheck.push_all(check);
                            }
                            Member(memberRect) => rects.push(memberRect),
                            NoElements => ()
                        };
                    }

                    nodesToCheck = newNodesToCheck;
                }

                rects
            },
            None => ~[],
        }
    }
}
<|endoftext|>


<commit_before>use prometheus::{Registry, Gauge, Encoder, TextEncoder};

use super::*;
pub struct PrometheusRenderer {
    pub buffer: Vec<u8>,
    registry: prometheus::Registry,
    pub encoder: prometheus::TextEncoder,
    count: prometheus::Gauge,
    max: prometheus::Gauge,
    min: prometheus::Gauge,
    avg: prometheus::Gauge,
    median: prometheus::Gauge,
    percentile90: prometheus::Gauge,
}

impl PrometheusRenderer {
    pub fn new() -> PrometheusRenderer {
        fn make_and_register_gauge(gauge_name: &str,
                                   registry: &prometheus::Registry)
                                   -> prometheus::Gauge {
            let gauge = prometheus::Gauge::new(String::from(gauge_name),
                                               format!("The {} of response times.", gauge_name))
                .expect("Failed to create Prometheus gauge.");

            let _ = registry.register(Box::new(gauge.clone()))
                .expect("Failed to register Prometheus gauge.");
            gauge
        }

        let registry = prometheus::Registry::new();

        PrometheusRenderer {
            buffer: Vec::new(),
            encoder: prometheus::TextEncoder::new(),
            count: make_and_register_gauge("request_count", &registry),
            max: make_and_register_gauge("request_time_max", &registry),
            min: make_and_register_gauge("request_time_min", &registry),
            avg: make_and_register_gauge("request_time_avg", &registry),
            median: make_and_register_gauge("request_time_median", &registry),
            percentile90: make_and_register_gauge("request_time_percentile90", &registry),
            registry: registry,
        }
    }
}

impl Renderer for PrometheusRenderer {
    fn render(&mut self, result: Option<analyzer::RequestLogAnalyzerResult>) {
        match result {
            Some(result) => {
                self.count.set(result.count as f64);
                self.max.set(result.max as f64);
                self.min.set(result.min as f64);
                self.avg.set(result.avg as f64);
                self.median.set(result.median as f64);
                self.percentile90.set(result.percentile90 as f64);

                let metric_familys = self.registry.gather();

                let _ = self.encoder
                    .encode(&metric_familys, &mut self.buffer)
                    .expect("Failed to encode Prometheus metrics.");
            }
            None => warn!("No matching log lines in file."),
        }
    }
}

#[cfg(test)]
mod tests {
    use std::str;
    use super::*;

    #[test]
    fn test_render_1() {
        let result = Some(analyzer::RequestLogAnalyzerResult {
            count: 3,
            max: 100,
            min: 1,
            avg: 37,
            median: 10,
            percentile90: 100,
        });

        let mut renderer = PrometheusRenderer::new();
        renderer.render(result);

        let buffer_text = str::from_utf8(&renderer.buffer).unwrap();
        assert!(buffer_text.contains("request_count 3"));
        assert!(buffer_text.contains("request_time_max 100"));
        assert!(buffer_text.contains("request_time_min 1"));
        assert!(buffer_text.contains("request_time_avg 37"));
        assert!(buffer_text.contains("request_time_median 10"));
        assert!(buffer_text.contains("request_time_percentile90 100"));
    }

    #[test]
    fn test_render_2() {
        let result = Some(analyzer::RequestLogAnalyzerResult {
            count: 300,
            max: 1000,
            min: 10,
            avg: 42,
            median: 75,
            percentile90: 900,
        });

        let mut renderer = PrometheusRenderer::new();
        renderer.render(result);

        let buffer_text = str::from_utf8(&renderer.buffer).unwrap();
        assert!(buffer_text.contains("request_count 300"));
        assert!(buffer_text.contains("request_time_max 1000"));
        assert!(buffer_text.contains("request_time_min 10"));
        assert!(buffer_text.contains("request_time_avg 42"));
        assert!(buffer_text.contains("request_time_median 75"));
        assert!(buffer_text.contains("request_time_percentile90 900"));
    }
}
<commit_msg>Return zero count for empty result (Prometheus)<commit_after>use prometheus::{Registry, Gauge, Encoder, TextEncoder};

use super::*;
pub struct PrometheusRenderer {
    pub buffer: Vec<u8>,
    registry: prometheus::Registry,
    pub encoder: prometheus::TextEncoder,
    count: prometheus::Gauge,
    max: prometheus::Gauge,
    min: prometheus::Gauge,
    avg: prometheus::Gauge,
    median: prometheus::Gauge,
    percentile90: prometheus::Gauge,
}

impl PrometheusRenderer {
    pub fn new() -> PrometheusRenderer {
        fn make_and_register_gauge(gauge_name: &str,
                                   registry: &prometheus::Registry)
                                   -> prometheus::Gauge {
            let gauge = prometheus::Gauge::new(String::from(gauge_name),
                                               format!("The {} of response times.", gauge_name))
                .expect("Failed to create Prometheus gauge.");

            let _ = registry.register(Box::new(gauge.clone()))
                .expect("Failed to register Prometheus gauge.");
            gauge
        }

        let registry = prometheus::Registry::new();

        PrometheusRenderer {
            buffer: Vec::new(),
            encoder: prometheus::TextEncoder::new(),
            count: make_and_register_gauge("request_count", &registry),
            max: make_and_register_gauge("request_time_max", &registry),
            min: make_and_register_gauge("request_time_min", &registry),
            avg: make_and_register_gauge("request_time_avg", &registry),
            median: make_and_register_gauge("request_time_median", &registry),
            percentile90: make_and_register_gauge("request_time_percentile90", &registry),
            registry: registry,
        }
    }
}

impl Renderer for PrometheusRenderer {
    fn render(&mut self, result: Option<analyzer::RequestLogAnalyzerResult>) {
        match result {
            Some(result) => {
                self.count.set(result.count as f64);
                self.max.set(result.max as f64);
                self.min.set(result.min as f64);
                self.avg.set(result.avg as f64);
                self.median.set(result.median as f64);
                self.percentile90.set(result.percentile90 as f64);
            }
            None => {
                warn!("No matching log lines in file.");
            }
        }
        let metric_familys = self.registry.gather();

        let _ = self.encoder
            .encode(&metric_familys, &mut self.buffer)
            .expect("Failed to encode Prometheus metrics.");
    }
}

#[cfg(test)]
mod tests {
    use std::str;
    use super::*;

    #[test]
    fn test_render_1() {
        let result = Some(analyzer::RequestLogAnalyzerResult {
            count: 3,
            max: 100,
            min: 1,
            avg: 37,
            median: 10,
            percentile90: 100,
        });

        let mut renderer = PrometheusRenderer::new();
        renderer.render(result);

        let buffer_text = str::from_utf8(&renderer.buffer).unwrap();
        assert!(buffer_text.contains("request_count 3"));
        assert!(buffer_text.contains("request_time_max 100"));
        assert!(buffer_text.contains("request_time_min 1"));
        assert!(buffer_text.contains("request_time_avg 37"));
        assert!(buffer_text.contains("request_time_median 10"));
        assert!(buffer_text.contains("request_time_percentile90 100"));
    }

    #[test]
    fn test_render_2() {
        let result = Some(analyzer::RequestLogAnalyzerResult {
            count: 300,
            max: 1000,
            min: 10,
            avg: 42,
            median: 75,
            percentile90: 900,
        });

        let mut renderer = PrometheusRenderer::new();
        renderer.render(result);

        let buffer_text = str::from_utf8(&renderer.buffer).unwrap();
        assert!(buffer_text.contains("request_count 300"));
        assert!(buffer_text.contains("request_time_max 1000"));
        assert!(buffer_text.contains("request_time_min 10"));
        assert!(buffer_text.contains("request_time_avg 42"));
        assert!(buffer_text.contains("request_time_median 75"));
        assert!(buffer_text.contains("request_time_percentile90 900"));
    }

    #[test]
    fn test_render_empty() {
        let result = None;

        let mut renderer = PrometheusRenderer::new();
        renderer.render(result);

        let buffer_text = str::from_utf8(&renderer.buffer).unwrap();
        assert!(buffer_text.contains("request_count 0"));
        // @TODO: Does NaN really exist with Prometheus?
        // https://prometheus.io/docs/practices/instrumentation/#avoid-missing-metrics
        // assert!(buffer_text.contains("request_time_max NaN"));
        // assert!(buffer_text.contains("request_time_min NaN"));
        // assert!(buffer_text.contains("request_time_avg NaN"));
        // assert!(buffer_text.contains("request_time_median NaN"));
        // assert!(buffer_text.contains("request_time_percentile90 NaN"));
    }
}
<|endoftext|>


<commit_before>use rustc::{declare_lint, hir, lint, lint_array};
use crate::utils;
use std::fmt;

/// **What it does:** Checks for usage of the `offset` pointer method with a `usize` casted to an
/// `isize`.
///
/// **Why is this bad?** If we’re always increasing the pointer address, we can avoid the numeric
/// cast by using the `add` method instead.
///
/// **Known problems:** None
///
/// **Example:**
/// ```rust
/// let vec = vec![b'a', b'b', b'c'];
/// let ptr = vec.as_ptr();
/// let offset = 1_usize;
///
/// unsafe { ptr.offset(offset as isize); }
/// ```
///
/// Could be written:
///
/// ```rust
/// let vec = vec![b'a', b'b', b'c'];
/// let ptr = vec.as_ptr();
/// let offset = 1_usize;
///
/// unsafe { ptr.add(offset); }
/// ```
declare_clippy_lint! {
    pub PTR_OFFSET_WITH_CAST,
    complexity,
    "uneeded pointer offset cast"
}

#[derive(Copy, Clone, Debug)]
pub struct Pass;

impl lint::LintPass for Pass {
    fn get_lints(&self) -> lint::LintArray {
        lint_array!(PTR_OFFSET_WITH_CAST)
    }
}

impl<'a, 'tcx> lint::LateLintPass<'a, 'tcx> for Pass {
    fn check_expr(&mut self, cx: &lint::LateContext<'a, 'tcx>, expr: &'tcx hir::Expr) {
        // Check if the expressions is a ptr.offset or ptr.wrapping_offset method call
        let (receiver_expr, arg_expr, method) = match expr_as_ptr_offset_call(cx, expr) {
            Some(call_arg) => call_arg,
            None => return,
        };

        // Check if the argument to the method call is a cast from usize
        let cast_lhs_expr = match expr_as_cast_from_usize(cx, arg_expr) {
            Some(cast_lhs_expr) => cast_lhs_expr,
            None => return,
        };

        let msg = format!("use of `{}` with a `usize` casted to an `isize`", method);
        if let Some(sugg) = build_suggestion(cx, method, receiver_expr, cast_lhs_expr) {
            utils::span_lint_and_sugg(cx, PTR_OFFSET_WITH_CAST, expr.span, &msg, "try", sugg);
        } else {
            utils::span_lint(cx, PTR_OFFSET_WITH_CAST, expr.span, &msg);
        }

    }
}

// If the given expression is a cast from a usize, return the lhs of the cast
fn expr_as_cast_from_usize<'a, 'tcx>(
    cx: &lint::LateContext<'a, 'tcx>,
    expr: &'tcx hir::Expr,
) -> Option<&'tcx hir::Expr> {
    if let hir::ExprKind::Cast(ref cast_lhs_expr, _) = expr.node {
        if is_expr_ty_usize(cx, &cast_lhs_expr) {
            return Some(cast_lhs_expr);
        }
    }
    None
}

// If the given expression is a ptr::offset  or ptr::wrapping_offset method call, return the
// receiver, the arg of the method call, and the method.
fn expr_as_ptr_offset_call<'a, 'tcx>(
    cx: &lint::LateContext<'a, 'tcx>,
    expr: &'tcx hir::Expr,
) -> Option<(&'tcx hir::Expr, &'tcx hir::Expr, Method)> {
    if let hir::ExprKind::MethodCall(ref path_segment, _, ref args) = expr.node {
        if is_expr_ty_raw_ptr(cx, &args[0]) {
            if path_segment.ident.name == "offset" {
                return Some((&args[0], &args[1], Method::Offset));
            }
            if path_segment.ident.name == "wrapping_offset" {
                return Some((&args[0], &args[1], Method::WrappingOffset));
            }
        }
    }
    None
}

// Is the type of the expression a usize?
fn is_expr_ty_usize<'a, 'tcx>(
    cx: &lint::LateContext<'a, 'tcx>,
    expr: &hir::Expr,
) -> bool {
    cx.tables.expr_ty(expr) == cx.tcx.types.usize
}

// Is the type of the expression a raw pointer?
fn is_expr_ty_raw_ptr<'a, 'tcx>(
    cx: &lint::LateContext<'a, 'tcx>,
    expr: &hir::Expr,
) -> bool {
    cx.tables.expr_ty(expr).is_unsafe_ptr()
}

fn build_suggestion<'a, 'tcx>(
    cx: &lint::LateContext<'a, 'tcx>,
    method: Method,
    receiver_expr: &hir::Expr,
    cast_lhs_expr: &hir::Expr,
) -> Option<String> {
    let receiver = utils::snippet_opt(cx, receiver_expr.span)?;
    let cast_lhs = utils::snippet_opt(cx, cast_lhs_expr.span)?;
    Some(format!("{}.{}({})", receiver, method.suggestion(), cast_lhs))
}

#[derive(Copy, Clone)]
enum Method {
    Offset,
    WrappingOffset,
}

impl Method {
    fn suggestion(self) -> &'static str {
        match self {
            Method::Offset => "add",
            Method::WrappingOffset => "wrapping_add",
        }
    }
}

impl fmt::Display for Method {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Method::Offset => write!(f, "offset"),
            Method::WrappingOffset => write!(f, "wrapping_offset"),
        }
    }
}
<commit_msg>declare_lint -> declare_tool_lint<commit_after>use rustc::{declare_tool_lint, hir, lint, lint_array};
use crate::utils;
use std::fmt;

/// **What it does:** Checks for usage of the `offset` pointer method with a `usize` casted to an
/// `isize`.
///
/// **Why is this bad?** If we’re always increasing the pointer address, we can avoid the numeric
/// cast by using the `add` method instead.
///
/// **Known problems:** None
///
/// **Example:**
/// ```rust
/// let vec = vec![b'a', b'b', b'c'];
/// let ptr = vec.as_ptr();
/// let offset = 1_usize;
///
/// unsafe { ptr.offset(offset as isize); }
/// ```
///
/// Could be written:
///
/// ```rust
/// let vec = vec![b'a', b'b', b'c'];
/// let ptr = vec.as_ptr();
/// let offset = 1_usize;
///
/// unsafe { ptr.add(offset); }
/// ```
declare_clippy_lint! {
    pub PTR_OFFSET_WITH_CAST,
    complexity,
    "uneeded pointer offset cast"
}

#[derive(Copy, Clone, Debug)]
pub struct Pass;

impl lint::LintPass for Pass {
    fn get_lints(&self) -> lint::LintArray {
        lint_array!(PTR_OFFSET_WITH_CAST)
    }
}

impl<'a, 'tcx> lint::LateLintPass<'a, 'tcx> for Pass {
    fn check_expr(&mut self, cx: &lint::LateContext<'a, 'tcx>, expr: &'tcx hir::Expr) {
        // Check if the expressions is a ptr.offset or ptr.wrapping_offset method call
        let (receiver_expr, arg_expr, method) = match expr_as_ptr_offset_call(cx, expr) {
            Some(call_arg) => call_arg,
            None => return,
        };

        // Check if the argument to the method call is a cast from usize
        let cast_lhs_expr = match expr_as_cast_from_usize(cx, arg_expr) {
            Some(cast_lhs_expr) => cast_lhs_expr,
            None => return,
        };

        let msg = format!("use of `{}` with a `usize` casted to an `isize`", method);
        if let Some(sugg) = build_suggestion(cx, method, receiver_expr, cast_lhs_expr) {
            utils::span_lint_and_sugg(cx, PTR_OFFSET_WITH_CAST, expr.span, &msg, "try", sugg);
        } else {
            utils::span_lint(cx, PTR_OFFSET_WITH_CAST, expr.span, &msg);
        }

    }
}

// If the given expression is a cast from a usize, return the lhs of the cast
fn expr_as_cast_from_usize<'a, 'tcx>(
    cx: &lint::LateContext<'a, 'tcx>,
    expr: &'tcx hir::Expr,
) -> Option<&'tcx hir::Expr> {
    if let hir::ExprKind::Cast(ref cast_lhs_expr, _) = expr.node {
        if is_expr_ty_usize(cx, &cast_lhs_expr) {
            return Some(cast_lhs_expr);
        }
    }
    None
}

// If the given expression is a ptr::offset  or ptr::wrapping_offset method call, return the
// receiver, the arg of the method call, and the method.
fn expr_as_ptr_offset_call<'a, 'tcx>(
    cx: &lint::LateContext<'a, 'tcx>,
    expr: &'tcx hir::Expr,
) -> Option<(&'tcx hir::Expr, &'tcx hir::Expr, Method)> {
    if let hir::ExprKind::MethodCall(ref path_segment, _, ref args) = expr.node {
        if is_expr_ty_raw_ptr(cx, &args[0]) {
            if path_segment.ident.name == "offset" {
                return Some((&args[0], &args[1], Method::Offset));
            }
            if path_segment.ident.name == "wrapping_offset" {
                return Some((&args[0], &args[1], Method::WrappingOffset));
            }
        }
    }
    None
}

// Is the type of the expression a usize?
fn is_expr_ty_usize<'a, 'tcx>(
    cx: &lint::LateContext<'a, 'tcx>,
    expr: &hir::Expr,
) -> bool {
    cx.tables.expr_ty(expr) == cx.tcx.types.usize
}

// Is the type of the expression a raw pointer?
fn is_expr_ty_raw_ptr<'a, 'tcx>(
    cx: &lint::LateContext<'a, 'tcx>,
    expr: &hir::Expr,
) -> bool {
    cx.tables.expr_ty(expr).is_unsafe_ptr()
}

fn build_suggestion<'a, 'tcx>(
    cx: &lint::LateContext<'a, 'tcx>,
    method: Method,
    receiver_expr: &hir::Expr,
    cast_lhs_expr: &hir::Expr,
) -> Option<String> {
    let receiver = utils::snippet_opt(cx, receiver_expr.span)?;
    let cast_lhs = utils::snippet_opt(cx, cast_lhs_expr.span)?;
    Some(format!("{}.{}({})", receiver, method.suggestion(), cast_lhs))
}

#[derive(Copy, Clone)]
enum Method {
    Offset,
    WrappingOffset,
}

impl Method {
    fn suggestion(self) -> &'static str {
        match self {
            Method::Offset => "add",
            Method::WrappingOffset => "wrapping_add",
        }
    }
}

impl fmt::Display for Method {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Method::Offset => write!(f, "offset"),
            Method::WrappingOffset => write!(f, "wrapping_offset"),
        }
    }
}
<|endoftext|>


<commit_before>use super::*;
use op_code::OpCode;
use rle_ast;

pub struct Compiler {
    instructions: Vec<Instruction>,
}

impl Compiler {
    pub fn new() -> Self {
        Compiler {
            instructions: Vec::new(),
        }
    }

    pub fn compile(&mut self, src: &[rle_ast::Instruction]) {
        use rle_ast::Instruction::*;

        for instruction in src {
            match *instruction {
                Op((OpCode::Right, count)) =>
                    self.push(Instruction::Right(count)),
                Op((OpCode::Left, count)) =>
                    self.push(Instruction::Left(count)),
                Op((OpCode::Up, count)) => {
                    let amount = (count % 256) as u8;
                    self.push(Instruction::Change(amount));
                }
                Op((OpCode::Down, count)) => {
                    let amount = (256 - count % 256) as u8;
                    self.push(Instruction::Change(amount));
                }
                Op((OpCode::In, count)) => {
                    for _ in 0 .. count {
                        self.push(Instruction::In);
                    }
                }
                Op((OpCode::Out, count)) => {
                    for _ in 0 .. count {
                        self.push(Instruction::Out);
                    }
                }
                Op((OpCode::Begin, _)) | Op((OpCode::End, _)) =>
                    panic!("bad opcode"),

                Loop(ref body) => {
                    let peephole = set_zero_peephole(&body)
                        .or_else(|| find_zero_peephole(&body))
                        .or_else(|| find_move_peephole(&body))
                    ;

                    if let Some(instr) = peephole {
                        self.push(instr);
                    } else {
                        let begin_pc = self.instructions.len();
                        self.push(Instruction::Begin(0));
                        self.compile(&body);
                        let end_pc = self.instructions.len();
                        self.push(Instruction::End(begin_pc));
                        self.instructions[begin_pc] = Instruction::Begin(end_pc);
                    }
                }
            }
        }
    }

    pub fn into_program(self) -> Box<Program> {
        self.instructions.into_boxed_slice()
    }

    fn push(&mut self, instr: Instruction) {
        self.instructions.push(instr);
    }
}

pub fn set_zero_peephole(body: &[rle_ast::Instruction]) -> Option<Instruction> {
    if body.len() == 1 &&
        (body[0] == rle_ast::Instruction::Op((OpCode::Up, 1)) ||
         body[0] == rle_ast::Instruction::Op((OpCode::Down, 1))) {
        Some(Instruction::SetZero)
    } else {
        None
    }
}

pub fn find_zero_peephole(body: &[rle_ast::Instruction]) -> Option<Instruction> {
    if body.len() == 1 {
        match body[0] {
            rle_ast::Instruction::Op((OpCode::Right, count)) =>
                Some(Instruction::FindZeroRight(count)),
            rle_ast::Instruction::Op((OpCode::Left, count)) =>
                Some(Instruction::FindZeroLeft(count)),
            _ => None,
        }
    } else {
        None
    }
}

pub fn find_move_peephole(body: &[rle_ast::Instruction]) -> Option<Instruction> {
    use rle_ast::Instruction::*;

    if body.len() == 4 {
        match (&body[0], &body[1], &body[2], &body[3]) {
            (&Op((OpCode::Down, 1)),
             &Op((OpCode::Right, count_l)),
             &Op((OpCode::Up, 1)),
             &Op((OpCode::Left, count_r))) if count_l == count_r => {
                Some(Instruction::MoveAddRight(count_l))
            }
            (&Op((OpCode::Down, 1)),
             &Op((OpCode::Left, count_l)),
             &Op((OpCode::Up, 1)),
             &Op((OpCode::Right, count_r))) if count_l == count_r => {
                Some(Instruction::MoveAddLeft(count_l))
            }
            _ => None,
        }
    } else {
        None
    }
}

pub fn compile(src: &[rle_ast::Instruction]) -> Box<Program> {
    let mut compiler = Compiler::new();
    compiler.compile(src);
    compiler.into_program()
}
<commit_msg>More appropriate name for move_add_peephole.<commit_after>use super::*;
use op_code::OpCode;
use rle_ast;

pub struct Compiler {
    instructions: Vec<Instruction>,
}

impl Compiler {
    pub fn new() -> Self {
        Compiler {
            instructions: Vec::new(),
        }
    }

    pub fn compile(&mut self, src: &[rle_ast::Instruction]) {
        use rle_ast::Instruction::*;

        for instruction in src {
            match *instruction {
                Op((OpCode::Right, count)) =>
                    self.push(Instruction::Right(count)),
                Op((OpCode::Left, count)) =>
                    self.push(Instruction::Left(count)),
                Op((OpCode::Up, count)) => {
                    let amount = (count % 256) as u8;
                    self.push(Instruction::Change(amount));
                }
                Op((OpCode::Down, count)) => {
                    let amount = (256 - count % 256) as u8;
                    self.push(Instruction::Change(amount));
                }
                Op((OpCode::In, count)) => {
                    for _ in 0 .. count {
                        self.push(Instruction::In);
                    }
                }
                Op((OpCode::Out, count)) => {
                    for _ in 0 .. count {
                        self.push(Instruction::Out);
                    }
                }
                Op((OpCode::Begin, _)) | Op((OpCode::End, _)) =>
                    panic!("bad opcode"),

                Loop(ref body) => {
                    let peephole = set_zero_peephole(&body)
                        .or_else(|| find_zero_peephole(&body))
                        .or_else(|| move_add_peephole(&body));

                    if let Some(instr) = peephole {
                        self.push(instr);
                    } else {
                        let begin_pc = self.instructions.len();
                        self.push(Instruction::Begin(0));
                        self.compile(&body);
                        let end_pc = self.instructions.len();
                        self.push(Instruction::End(begin_pc));
                        self.instructions[begin_pc] = Instruction::Begin(end_pc);
                    }
                }
            }
        }
    }

    pub fn into_program(self) -> Box<Program> {
        self.instructions.into_boxed_slice()
    }

    fn push(&mut self, instr: Instruction) {
        self.instructions.push(instr);
    }
}

pub fn set_zero_peephole(body: &[rle_ast::Instruction]) -> Option<Instruction> {
    if body.len() == 1 &&
        (body[0] == rle_ast::Instruction::Op((OpCode::Up, 1)) ||
         body[0] == rle_ast::Instruction::Op((OpCode::Down, 1))) {
        Some(Instruction::SetZero)
    } else {
        None
    }
}

pub fn find_zero_peephole(body: &[rle_ast::Instruction]) -> Option<Instruction> {
    if body.len() == 1 {
        match body[0] {
            rle_ast::Instruction::Op((OpCode::Right, count)) =>
                Some(Instruction::FindZeroRight(count)),
            rle_ast::Instruction::Op((OpCode::Left, count)) =>
                Some(Instruction::FindZeroLeft(count)),
            _ => None,
        }
    } else {
        None
    }
}

pub fn move_add_peephole(body: &[rle_ast::Instruction]) -> Option<Instruction> {
    use rle_ast::Instruction::*;

    if body.len() == 4 {
        match (&body[0], &body[1], &body[2], &body[3]) {
            (&Op((OpCode::Down, 1)),
             &Op((OpCode::Right, count_l)),
             &Op((OpCode::Up, 1)),
             &Op((OpCode::Left, count_r))) if count_l == count_r => {
                Some(Instruction::MoveAddRight(count_l))
            }
            (&Op((OpCode::Down, 1)),
             &Op((OpCode::Left, count_l)),
             &Op((OpCode::Up, 1)),
             &Op((OpCode::Right, count_r))) if count_l == count_r => {
                Some(Instruction::MoveAddLeft(count_l))
            }
            _ => None,
        }
    } else {
        None
    }
}

pub fn compile(src: &[rle_ast::Instruction]) -> Box<Program> {
    let mut compiler = Compiler::new();
    compiler.compile(src);
    compiler.into_program()
}
<|endoftext|>


<commit_before>use console::{Console, ConsoleTextKind};
use crossbeam_channel::{Receiver, Sender};
use ctrlc;
use memmap::Mmap;
use pipeline::{Pipeline, PipelineInfo};
use pipeline_matcher::PathMatch;
use std::fs::{self, File};
use std::io;
use std::io::{Error, Write};
use std::ops::Deref;
use std::time::{Duration, Instant};
use tempfile::NamedTempFile;
use util::{catch, decode_error, exit};

// ---------------------------------------------------------------------------------------------------------------------
// PipelineReplacer
// ---------------------------------------------------------------------------------------------------------------------

pub struct PipelineReplacer {
    pub is_color: bool,
    pub is_interactive: bool,
    pub print_file: bool,
    pub print_column: bool,
    pub print_row: bool,
    pub infos: Vec<String>,
    pub errors: Vec<String>,
    console: Console,
    all_replace: bool,
    replacement: Vec<u8>,
    time_beg: Instant,
    time_bsy: Duration,
}

impl PipelineReplacer {
    pub fn new(replacement: &[u8]) -> Self {
        PipelineReplacer {
            is_color: true,
            is_interactive: true,
            print_file: true,
            print_column: false,
            print_row: false,
            infos: Vec::new(),
            errors: Vec::new(),
            console: Console::new(),
            all_replace: false,
            replacement: Vec::from(replacement),
            time_beg: Instant::now(),
            time_bsy: Duration::new(0, 0),
        }
    }

    fn replace_match(&mut self, pm: PathMatch) {
        if pm.matches.is_empty() {
            return;
        }
        self.console.is_color = self.is_color;

        let result = catch::<_, (), Error>(|| {
            let mut tmpfile = try!(NamedTempFile::new_in(pm.path.parent().unwrap_or(&pm.path)));

            let tmpfile_path = tmpfile.path().to_path_buf();
            let _ = ctrlc::set_handler(move || {
                let path = tmpfile_path.clone();
                let mut console = Console::new();
                console.write(
                    ConsoleTextKind::Info,
                    &format!("\nCleanup temporary file: {:?}\n", path),
                );
                let _ = fs::remove_file(path);
                exit(0, &mut console);
            });

            {
                let file = try!(File::open(&pm.path));
                let mmap = try!(unsafe { Mmap::map(&file) });
                let src = mmap.deref();

                let mut i = 0;
                let mut pos = 0;
                let mut column = 0;
                let mut last_lf = 0;
                for m in &pm.matches {
                    try!(tmpfile.write_all(&src[i..m.beg]));

                    let mut do_replace = true;
                    if self.is_interactive & !self.all_replace {
                        if self.print_file {
                            self.console.write(ConsoleTextKind::Filename, pm.path.to_str().unwrap());
                            self.console.write(ConsoleTextKind::Other, ": ");
                        }
                        if self.print_column | self.print_row {
                            while pos < m.beg {
                                if src[pos] == 0x0a {
                                    column += 1;
                                    last_lf = pos;
                                }
                                pos += 1;
                            }
                            if self.print_column {
                                self.console.write(ConsoleTextKind::Other, &format!("{}:", column + 1));
                            }
                            if self.print_row {
                                self.console
                                    .write(ConsoleTextKind::Other, &format!("{}:", m.beg - last_lf));
                            }
                        }

                        self.console.write_match_line(src, m);

                        loop {
                            self.console.write(
                                ConsoleTextKind::Other,
                                "Replace keyword? ( Yes[Y], No[N], All[A], Quit[Q] ):",
                            );
                            self.console.flush();
                            let mut buf = String::new();
                            io::stdin().read_line(&mut buf).unwrap();
                            match buf.trim().to_lowercase().as_ref() {
                                "y" => {
                                    do_replace = true;
                                    break;
                                }
                                "yes" => {
                                    do_replace = true;
                                    break;
                                }
                                "n" => {
                                    do_replace = false;
                                    break;
                                }
                                "no" => {
                                    do_replace = false;
                                    break;
                                }
                                "a" => {
                                    self.all_replace = true;
                                    break;
                                }
                                "all" => {
                                    self.all_replace = true;
                                    break;
                                }
                                "q" => {
                                    let _ = tmpfile.close();
                                    exit(0, &mut self.console);
                                }
                                "quit" => {
                                    let _ = tmpfile.close();
                                    exit(0, &mut self.console);
                                }
                                _ => continue,
                            }
                        }
                    }

                    if do_replace {
                        try!(tmpfile.write_all(&self.replacement));
                    } else {
                        try!(tmpfile.write_all(&src[m.beg..m.end]));
                    }
                    i = m.end;
                }

                if i < src.len() {
                    try!(tmpfile.write_all(&src[i..src.len()]));
                }
                try!(tmpfile.flush());
            }

            let metadata = try!(fs::metadata(&pm.path));

            try!(fs::set_permissions(tmpfile.path(), metadata.permissions()));
            try!(tmpfile.persist(&pm.path));

            Ok(())
        });
        match result {
            Ok(_) => (),
            Err(e) => self.console.write(
                ConsoleTextKind::Error,
                &format!("Error: {} @ {:?}\n", decode_error(e.kind()), pm.path),
            ),
        }
    }
}

impl Pipeline<PathMatch, ()> for PipelineReplacer {
    fn setup(&mut self, id: usize, rx: Receiver<PipelineInfo<PathMatch>>, tx: Sender<PipelineInfo<()>>) {
        self.infos = Vec::new();
        self.errors = Vec::new();
        let mut seq_beg_arrived = false;

        loop {
            match rx.recv() {
                Ok(PipelineInfo::SeqDat(x, pm)) => {
                    watch_time!(self.time_bsy, {
                        self.replace_match(pm);
                        let _ = tx.send(PipelineInfo::SeqDat(x, ()));
                    });
                }

                Ok(PipelineInfo::SeqBeg(x)) => {
                    if !seq_beg_arrived {
                        self.time_beg = Instant::now();
                        let _ = tx.send(PipelineInfo::SeqBeg(x));
                        seq_beg_arrived = true;
                    }
                }

                Ok(PipelineInfo::SeqEnd(x)) => {
                    for i in &self.infos {
                        let _ = tx.send(PipelineInfo::MsgInfo(id, i.clone()));
                    }
                    for e in &self.errors {
                        let _ = tx.send(PipelineInfo::MsgErr(id, e.clone()));
                    }

                    let _ = tx.send(PipelineInfo::MsgTime(id, self.time_bsy, self.time_beg.elapsed()));
                    let _ = tx.send(PipelineInfo::SeqEnd(x));
                    break;
                }

                Ok(PipelineInfo::MsgInfo(i, e)) => {
                    let _ = tx.send(PipelineInfo::MsgInfo(i, e));
                }
                Ok(PipelineInfo::MsgErr(i, e)) => {
                    let _ = tx.send(PipelineInfo::MsgErr(i, e));
                }
                Ok(PipelineInfo::MsgTime(i, t0, t1)) => {
                    let _ = tx.send(PipelineInfo::MsgTime(i, t0, t1));
                }
                Err(_) => break,
            }
        }
    }
}

// ---------------------------------------------------------------------------------------------------------------------
// Test
// ---------------------------------------------------------------------------------------------------------------------
<commit_msg>Fix breaking symlink by ambr<commit_after>use console::{Console, ConsoleTextKind};
use crossbeam_channel::{Receiver, Sender};
use ctrlc;
use memmap::Mmap;
use pipeline::{Pipeline, PipelineInfo};
use pipeline_matcher::PathMatch;
use std::fs::{self, File};
use std::io;
use std::io::{Error, Write};
use std::ops::Deref;
use std::time::{Duration, Instant};
use tempfile::NamedTempFile;
use util::{catch, decode_error, exit};

// ---------------------------------------------------------------------------------------------------------------------
// PipelineReplacer
// ---------------------------------------------------------------------------------------------------------------------

pub struct PipelineReplacer {
    pub is_color: bool,
    pub is_interactive: bool,
    pub print_file: bool,
    pub print_column: bool,
    pub print_row: bool,
    pub infos: Vec<String>,
    pub errors: Vec<String>,
    console: Console,
    all_replace: bool,
    replacement: Vec<u8>,
    time_beg: Instant,
    time_bsy: Duration,
}

impl PipelineReplacer {
    pub fn new(replacement: &[u8]) -> Self {
        PipelineReplacer {
            is_color: true,
            is_interactive: true,
            print_file: true,
            print_column: false,
            print_row: false,
            infos: Vec::new(),
            errors: Vec::new(),
            console: Console::new(),
            all_replace: false,
            replacement: Vec::from(replacement),
            time_beg: Instant::now(),
            time_bsy: Duration::new(0, 0),
        }
    }

    fn replace_match(&mut self, pm: PathMatch) {
        if pm.matches.is_empty() {
            return;
        }
        self.console.is_color = self.is_color;

        let result = catch::<_, (), Error>(|| {
            let mut tmpfile = try!(NamedTempFile::new_in(pm.path.parent().unwrap_or(&pm.path)));

            let tmpfile_path = tmpfile.path().to_path_buf();
            let _ = ctrlc::set_handler(move || {
                let path = tmpfile_path.clone();
                let mut console = Console::new();
                console.write(
                    ConsoleTextKind::Info,
                    &format!("\nCleanup temporary file: {:?}\n", path),
                );
                let _ = fs::remove_file(path);
                exit(0, &mut console);
            });

            {
                let file = try!(File::open(&pm.path));
                let mmap = try!(unsafe { Mmap::map(&file) });
                let src = mmap.deref();

                let mut i = 0;
                let mut pos = 0;
                let mut column = 0;
                let mut last_lf = 0;
                for m in &pm.matches {
                    try!(tmpfile.write_all(&src[i..m.beg]));

                    let mut do_replace = true;
                    if self.is_interactive & !self.all_replace {
                        if self.print_file {
                            self.console.write(ConsoleTextKind::Filename, pm.path.to_str().unwrap());
                            self.console.write(ConsoleTextKind::Other, ": ");
                        }
                        if self.print_column | self.print_row {
                            while pos < m.beg {
                                if src[pos] == 0x0a {
                                    column += 1;
                                    last_lf = pos;
                                }
                                pos += 1;
                            }
                            if self.print_column {
                                self.console.write(ConsoleTextKind::Other, &format!("{}:", column + 1));
                            }
                            if self.print_row {
                                self.console
                                    .write(ConsoleTextKind::Other, &format!("{}:", m.beg - last_lf));
                            }
                        }

                        self.console.write_match_line(src, m);

                        loop {
                            self.console.write(
                                ConsoleTextKind::Other,
                                "Replace keyword? ( Yes[Y], No[N], All[A], Quit[Q] ):",
                            );
                            self.console.flush();
                            let mut buf = String::new();
                            io::stdin().read_line(&mut buf).unwrap();
                            match buf.trim().to_lowercase().as_ref() {
                                "y" => {
                                    do_replace = true;
                                    break;
                                }
                                "yes" => {
                                    do_replace = true;
                                    break;
                                }
                                "n" => {
                                    do_replace = false;
                                    break;
                                }
                                "no" => {
                                    do_replace = false;
                                    break;
                                }
                                "a" => {
                                    self.all_replace = true;
                                    break;
                                }
                                "all" => {
                                    self.all_replace = true;
                                    break;
                                }
                                "q" => {
                                    let _ = tmpfile.close();
                                    exit(0, &mut self.console);
                                }
                                "quit" => {
                                    let _ = tmpfile.close();
                                    exit(0, &mut self.console);
                                }
                                _ => continue,
                            }
                        }
                    }

                    if do_replace {
                        try!(tmpfile.write_all(&self.replacement));
                    } else {
                        try!(tmpfile.write_all(&src[m.beg..m.end]));
                    }
                    i = m.end;
                }

                if i < src.len() {
                    try!(tmpfile.write_all(&src[i..src.len()]));
                }
                try!(tmpfile.flush());
            }

            let real_path = try!(fs::canonicalize(&pm.path));

            let metadata = try!(fs::metadata(&real_path));

            try!(fs::set_permissions(tmpfile.path(), metadata.permissions()));
            try!(tmpfile.persist(&real_path));

            Ok(())
        });
        match result {
            Ok(_) => (),
            Err(e) => self.console.write(
                ConsoleTextKind::Error,
                &format!("Error: {} @ {:?}\n", decode_error(e.kind()), pm.path),
            ),
        }
    }
}

impl Pipeline<PathMatch, ()> for PipelineReplacer {
    fn setup(&mut self, id: usize, rx: Receiver<PipelineInfo<PathMatch>>, tx: Sender<PipelineInfo<()>>) {
        self.infos = Vec::new();
        self.errors = Vec::new();
        let mut seq_beg_arrived = false;

        loop {
            match rx.recv() {
                Ok(PipelineInfo::SeqDat(x, pm)) => {
                    watch_time!(self.time_bsy, {
                        self.replace_match(pm);
                        let _ = tx.send(PipelineInfo::SeqDat(x, ()));
                    });
                }

                Ok(PipelineInfo::SeqBeg(x)) => {
                    if !seq_beg_arrived {
                        self.time_beg = Instant::now();
                        let _ = tx.send(PipelineInfo::SeqBeg(x));
                        seq_beg_arrived = true;
                    }
                }

                Ok(PipelineInfo::SeqEnd(x)) => {
                    for i in &self.infos {
                        let _ = tx.send(PipelineInfo::MsgInfo(id, i.clone()));
                    }
                    for e in &self.errors {
                        let _ = tx.send(PipelineInfo::MsgErr(id, e.clone()));
                    }

                    let _ = tx.send(PipelineInfo::MsgTime(id, self.time_bsy, self.time_beg.elapsed()));
                    let _ = tx.send(PipelineInfo::SeqEnd(x));
                    break;
                }

                Ok(PipelineInfo::MsgInfo(i, e)) => {
                    let _ = tx.send(PipelineInfo::MsgInfo(i, e));
                }
                Ok(PipelineInfo::MsgErr(i, e)) => {
                    let _ = tx.send(PipelineInfo::MsgErr(i, e));
                }
                Ok(PipelineInfo::MsgTime(i, t0, t1)) => {
                    let _ = tx.send(PipelineInfo::MsgTime(i, t0, t1));
                }
                Err(_) => break,
            }
        }
    }
}

// ---------------------------------------------------------------------------------------------------------------------
// Test
// ---------------------------------------------------------------------------------------------------------------------
<|endoftext|>


<commit_before>//! Checks that all error codes have at least one test to prevent having error
//! codes that are silently not thrown by the compiler anymore.

use std::collections::HashMap;
use std::ffi::OsStr;
use std::path::Path;

// A few of those error codes can't be tested but all the others can and *should* be tested!
const WHITELIST: &[&str] = &[
    "E0183",
    "E0227",
    "E0279",
    "E0280",
    "E0311",
    "E0313",
    "E0314",
    "E0315",
    "E0377",
    "E0456",
    "E0461",
    "E0462",
    "E0464",
    "E0465",
    "E0472",
    "E0473",
    "E0474",
    "E0475",
    "E0476",
    "E0479",
    "E0480",
    "E0481",
    "E0482",
    "E0483",
    "E0484",
    "E0485",
    "E0486",
    "E0487",
    "E0488",
    "E0489",
    "E0514",
    "E0519",
    "E0523",
    "E0554",
    "E0570",
    "E0629",
    "E0630",
    "E0640",
    "E0717",
    "E0727",
    "E0729",
];

fn extract_error_codes(f: &str, error_codes: &mut HashMap<String, bool>) {
    let mut reached_no_explanation = false;
    let mut last_error_code = None;

    for line in f.lines() {
        let s = line.trim();
        if s.starts_with('E') && s.ends_with(": r##\"") {
            if let Some(err_code) = s.splitn(2, ':').next() {
                let err_code = err_code.to_owned();
                last_error_code = Some(err_code.clone());
                if !error_codes.contains_key(&err_code) {
                    error_codes.insert(err_code, false);
                }
            }
        } else if s.starts_with("```") && s.contains("compile_fail") && s.contains('E') {
            if let Some(err_code) = s.splitn(2, 'E').skip(1).next() {
                if let Some(err_code) = err_code.splitn(2, ',').next() {
                    let nb = error_codes.entry(format!("E{}", err_code)).or_insert(false);
                    *nb = true;
                }
            }
        } else if s == ";" {
            reached_no_explanation = true;
        } else if reached_no_explanation && s.starts_with('E') {
            if let Some(err_code) = s.splitn(2, ',').next() {
                let err_code = err_code.to_owned();
                if !error_codes.contains_key(&err_code) { // this check should *never* fail!
                    error_codes.insert(err_code, false);
                }
            }
        } else if s.starts_with("#### Note: this error code is no longer emitted by the compiler") {
            if let Some(last) = last_error_code {
                error_codes.get_mut(&last).map(|x| *x = true);
            }
            last_error_code = None;
        }
    }
}

fn extract_error_codes_from_tests(f: &str, error_codes: &mut HashMap<String, bool>) {
    for line in f.lines() {
        let s = line.trim();
        if s.starts_with("error[E") || s.starts_with("warning[E") {
            if let Some(err_code) = s.splitn(2, ']').next() {
                if let Some(err_code) = err_code.splitn(2, '[').skip(1).next() {
                    let nb = error_codes.entry(err_code.to_owned()).or_insert(false);
                    *nb = true;
                }
            }
        }
    }
}

pub fn check(path: &Path, bad: &mut bool) {
    println!("Checking which error codes lack tests...");
    let mut error_codes: HashMap<String, bool> = HashMap::new();
    super::walk(path,
                &mut |path| super::filter_dirs(path),
                &mut |entry, contents| {
        let file_name = entry.file_name();
        if file_name == "error_codes.rs" {
            extract_error_codes(contents, &mut error_codes);
        } else if entry.path().extension() == Some(OsStr::new("stderr")) {
            extract_error_codes_from_tests(contents, &mut error_codes);
        }
    });
    println!("Found {} error codes", error_codes.len());

    let mut errors = Vec::new();
    for (err_code, nb) in &error_codes {
        if !*nb && !WHITELIST.contains(&err_code.as_str()) {
            errors.push(format!("Error code {} needs to have at least one UI test!", err_code));
        }
    }
    errors.sort();
    for err in &errors {
        eprintln!("{}", err);
    }
    println!("Found {} error codes with no tests", errors.len());
    if !errors.is_empty() {
        *bad = true;
    }
    println!("Done!");
}
<commit_msg>Rollup merge of #66574 - GuillaumeGomez:update-tidy-err-code-check, r=Mark-Simulacrum<commit_after>//! Checks that all error codes have at least one test to prevent having error
//! codes that are silently not thrown by the compiler anymore.

use std::collections::HashMap;
use std::ffi::OsStr;
use std::fs::read_to_string;
use std::io::Read;
use std::path::Path;

// A few of those error codes can't be tested but all the others can and *should* be tested!
const WHITELIST: &[&str] = &[
    "E0183",
    "E0227",
    "E0279",
    "E0280",
    "E0311",
    "E0313",
    "E0314",
    "E0315",
    "E0377",
    "E0456",
    "E0461",
    "E0462",
    "E0464",
    "E0465",
    "E0472",
    "E0473",
    "E0474",
    "E0475",
    "E0476",
    "E0479",
    "E0480",
    "E0481",
    "E0482",
    "E0483",
    "E0484",
    "E0485",
    "E0486",
    "E0487",
    "E0488",
    "E0489",
    "E0514",
    "E0519",
    "E0523",
    "E0554",
    "E0570",
    "E0629",
    "E0630",
    "E0640",
    "E0717",
    "E0727",
    "E0729",
];

fn check_error_code_explanation(
    f: &str,
    error_codes: &mut HashMap<String, bool>,
    err_code: String,
) {
    for line in f.lines() {
        let s = line.trim();
        if s.starts_with("```") && s.contains("compile_fail") && s.contains('E') {
            error_codes.insert(err_code, true);
            return;
        } else if s.starts_with("#### Note: this error code is no longer emitted by the compiler") {
            error_codes.get_mut(&err_code).map(|x| *x = true);
            return;
        }
    }
}

macro_rules! some_or_continue {
    ($e:expr) => (
        match $e {
            Some(e) => e,
            None => continue,
        }
    );
}

fn extract_error_codes(f: &str, error_codes: &mut HashMap<String, bool>, path: &Path) {
    let mut reached_no_explanation = false;

    for line in f.lines() {
        let s = line.trim();
        if !reached_no_explanation && s.starts_with('E') && s.contains("include_str!(\"") {
            if let Some(err_code) = s.splitn(2, ':').next() {
                let err_code = err_code.to_owned();
                if !error_codes.contains_key(&err_code) {
                    error_codes.insert(err_code.clone(), false);
                }
                // Now we extract the tests from the markdown file!
                let md = some_or_continue!(s.splitn(2, "include_str!(\"").skip(1).next());
                let md_file_name = some_or_continue!(md.splitn(2, "\")").next());
                let path = some_or_continue!(path.parent()).join(md_file_name);
                match read_to_string(&path) {
                    Ok(content) => {
                        check_error_code_explanation(
                            &content,
                            error_codes,
                            err_code,
                        );
                    }
                    Err(e) => {
                        eprintln!("Couldn't read `{}`: {}", path.display(), e);
                    }
                }
            }
        } else if reached_no_explanation && s.starts_with('E') {
            if let Some(err_code) = s.splitn(2, ',').next() {
                let err_code = err_code.to_owned();
                if !error_codes.contains_key(&err_code) { // this check should *never* fail!
                    error_codes.insert(err_code, false);
                }
            }
        } else if s == ";" {
            reached_no_explanation = true;
        }
    }
}

fn extract_error_codes_from_tests(f: &str, error_codes: &mut HashMap<String, bool>) {
    for line in f.lines() {
        let s = line.trim();
        if s.starts_with("error[E") || s.starts_with("warning[E") {
            if let Some(err_code) = s.splitn(2, ']').next() {
                if let Some(err_code) = err_code.splitn(2, '[').skip(1).next() {
                    let nb = error_codes.entry(err_code.to_owned()).or_insert(false);
                    *nb = true;
                }
            }
        }
    }
}

pub fn check(path: &Path, bad: &mut bool) {
    println!("Checking which error codes lack tests...");
    let mut error_codes: HashMap<String, bool> = HashMap::new();
    super::walk(path,
                &mut |path| super::filter_dirs(path),
                &mut |entry, contents| {
        let file_name = entry.file_name();
        if file_name == "error_codes.rs" {
            extract_error_codes(contents, &mut error_codes, entry.path());
        } else if entry.path().extension() == Some(OsStr::new("stderr")) {
            extract_error_codes_from_tests(contents, &mut error_codes);
        }
    });
    println!("Found {} error codes", error_codes.len());

    let mut errors = Vec::new();
    for (err_code, nb) in &error_codes {
        if !*nb && !WHITELIST.contains(&err_code.as_str()) {
            errors.push(format!("Error code {} needs to have at least one UI test!", err_code));
        }
    }
    errors.sort();
    for err in &errors {
        eprintln!("{}", err);
    }
    println!("Found {} error codes with no tests", errors.len());
    if !errors.is_empty() {
        *bad = true;
    }
    println!("Done!");
}
<|endoftext|>