<commit_before>//! List group issues
//!
//! https://docs.gitlab.com/ce/api/issues.html#list-group-issues
//!
//! # List group issues
//!
//! Get a list of a group's issues.
//!
//! ```text
//! GET /groups/ID/issues
//! GET /groups/ID/issues?state=opened
//! GET /groups/ID/issues?state=closed
//! GET /groups/ID/issues?labels=foo
//! GET /groups/ID/issues?labels=foo,bar
//! GET /groups/ID/issues?labels=foo,bar&state=opened
//! GET /groups/ID/issues?milestone=1.0.0
//! GET /groups/ID/issues?milestone=1.0.0&state=opened
//! ```
//!
//! | Attribute | Type | Required | Description |
//! | --------- | ---- | -------- | ----------- |
//! | `id`      | integer | yes   | The ID of a group |
//! | `state`   | string  | no    | Return all issues or just those that are `opened` or `closed`|
//! | `labels`  | string  | no    | Comma-separated list of label names, issues must have all labels to be returned |
//! | `milestone` | string| no    | The milestone title |
//! | `order_by`| string  | no    | Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at` |
//! | `sort`    | string  | no    | Return requests sorted in `asc` or `desc` order. Default is `desc`  |
//!

// use serde_urlencoded;

use BuildQuery;
use Issues;
use issues::GroupIssuesListerInternal;

use ::errors::*;

#[derive(Debug, Clone)]
pub struct IssuesLister<'a> {
    gl: &'a ::GitLab,
    /// The ID of a group
    id: i64,
    internal: GroupIssuesListerInternal,
}

impl<'a> IssuesLister<'a> {
    pub fn new(gl: &'a ::GitLab, id: i64) -> IssuesLister {
        IssuesLister {
            gl: gl,
            id: id,
            internal: GroupIssuesListerInternal {
                state: None,
                labels: None,
                milestone: None,
                order_by: None,
                sort: None,
            },
        }
    }

    pub fn state(&'a mut self, state: ::issues::State) -> &'a mut IssuesLister {
        self.internal.state = Some(state);
        self
    }

    pub fn milestone(&'a mut self, milestone: String) -> &'a mut IssuesLister {
        self.internal.milestone = Some(milestone);
        self
    }

    pub fn labels(&'a mut self, labels: Vec<String>) -> &'a mut IssuesLister {
        self.internal.labels = Some(labels);
        self
    }

    pub fn order_by(&'a mut self, order_by: ::issues::ListingOrderBy) -> &'a mut IssuesLister {
        self.internal.order_by = Some(order_by);
        self
    }

    pub fn sort(&'a mut self, sort: ::ListingSort) -> &'a mut IssuesLister {
        self.internal.sort = Some(sort);
        self
    }

    /// Commit the lister: Query GitLab and return a list of issues.
    pub fn list(&self) -> Result<Issues> {
        let query = self.build_query();
        debug!("query: {:?}", query);

        self.gl.get(&query).chain_err(|| format!("cannot get query {}", query))
    }
}

impl<'a> BuildQuery for IssuesLister<'a> {
    fn build_query(&self) -> String {

        // NOTE: Can't use `serde_urlencoded` since it cannot serialize a Vec<T>
        //       See https://github.com/nox/serde_urlencoded/issues/6
        // let encoded = serde_urlencoded::to_string(&self.internal).unwrap();

        let mut query = format!("groups/{}/issues", self.id);
        let amp_char = "&";
        let comma_char = ",";
        let none_char = "";
        let mut split_char = &none_char;

        // Append a "?" only if at least one of the `Option` is `Some(_)` or if
        // strings contain something.
        query.push_str(match (&self.internal.state,
                              &self.internal.labels,
                              &self.internal.milestone,
                              &self.internal.order_by,
                              &self.internal.sort) {
            (&None, &None, &None, &None, &None) => "",
            _ => "?",
        });

        self.internal.state.map(|state| {
            query.push_str(split_char);
            split_char = &amp_char;

            query.push_str("state=");
            query.push_str(match state {
                ::issues::State::Opened => "opened",
                ::issues::State::Closed => "closed",
            });
        });

        self.internal.labels.as_ref().map(|labels| {
            query.push_str(split_char);
            split_char = &amp_char;

            query.push_str("labels=");

            let mut array_split_char = &none_char;
            for label in labels {
                query.push_str(array_split_char);
                query.push_str(&label.to_string());
                array_split_char = &comma_char;
            }
        });

        self.internal.milestone.as_ref().map(|milestone| {
            query.push_str(split_char);
            split_char = &amp_char;

            query.push_str("milestone=");
            query.push_str(milestone);
        });

        self.internal.order_by.map(|order_by| {
            query.push_str(split_char);
            split_char = &amp_char;

            query.push_str("order_by=");
            query.push_str(match order_by {
                ::issues::ListingOrderBy::CreatedAt => "created_at",
                ::issues::ListingOrderBy::UpdatedAt => "updated_at",
            });
        });

        self.internal.sort.map(|sort| {
            query.push_str(split_char);
            split_char = &amp_char;

            query.push_str("sort=");
            query.push_str(match sort {
                ::ListingSort::Asc => "asc",
                ::ListingSort::Desc => "desc",
            });
        });

        query
    }
}

#[cfg(test)]
mod tests {
    use BuildQuery;

    const TEST_PROJECT_ID: i64 = 123;

    #[test]
    fn build_query_default() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap();
        // let gl: ::GitLab = Default::default();

        let expected_string = format!("groups/{}/issues", TEST_PROJECT_ID);

        let lister = gl.issues();
        let lister = lister.group(TEST_PROJECT_ID);
        let query = lister.build_query();
        assert_eq!(query, expected_string);

        let lister = gl.issues().group(TEST_PROJECT_ID);
        let query = lister.build_query();
        assert_eq!(query, expected_string);

        let query = gl.issues().group(TEST_PROJECT_ID).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_state() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap();
        // let gl: ::GitLab = Default::default();

        let expected_string = "groups/123/issues?state=opened";
        let query = gl.issues().group(TEST_PROJECT_ID).state(::issues::State::Opened).build_query();
        assert_eq!(query, expected_string);

        let expected_string = "groups/123/issues?state=closed";
        let query = gl.issues().group(TEST_PROJECT_ID).state(::issues::State::Closed).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_milestone() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap();
        // let gl: ::GitLab = Default::default();

        let expected_string = "groups/123/issues?milestone=TestMilestone";
        let query = gl.issues().group(TEST_PROJECT_ID).milestone("TestMilestone".to_string()).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_skip_groups() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap();
        // let gl: ::GitLab = Default::default();

        let expected_string = "groups/123/issues?labels=label1,label2,label3";
        let query = gl.issues()
            .group(TEST_PROJECT_ID)
            .labels(vec![String::from("label1"), String::from("label2"), String::from("label3")])
            .build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_order_by() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap();
        // let gl: ::GitLab = Default::default();

        let expected_string = "groups/123/issues?order_by=created_at";
        let query = gl.issues()
            .group(TEST_PROJECT_ID)
            .order_by(::issues::ListingOrderBy::CreatedAt)
            .build_query();
        assert_eq!(query, expected_string);

        let expected_string = "groups/123/issues?order_by=updated_at";
        let query = gl.issues()
            .group(TEST_PROJECT_ID)
            .order_by(::issues::ListingOrderBy::UpdatedAt)
            .build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_sort() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap();
        // let gl: ::GitLab = Default::default();

        let expected_string = "groups/123/issues?sort=asc";
        let query = gl.issues().group(TEST_PROJECT_ID).sort(::ListingSort::Asc).build_query();
        assert_eq!(query, expected_string);

        let expected_string = "groups/123/issues?sort=desc";
        let query = gl.issues().group(TEST_PROJECT_ID).sort(::ListingSort::Desc).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_multiple() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap();
        // let gl: ::GitLab = Default::default();

        let expected_string = "groups/123/issues?order_by=created_at&sort=asc";
        let query = gl.issues()
            .group(TEST_PROJECT_ID)
            .sort(::ListingSort::Asc)
            .order_by(::issues::ListingOrderBy::CreatedAt)
            .build_query();
        assert_eq!(query, expected_string);
    }
}
<commit_msg>Bug fix: Use `serde_urlencoded` to encode the URL part of the milestone<commit_after>//! List group issues
//!
//! https://docs.gitlab.com/ce/api/issues.html#list-group-issues
//!
//! # List group issues
//!
//! Get a list of a group's issues.
//!
//! ```text
//! GET /groups/ID/issues
//! GET /groups/ID/issues?state=opened
//! GET /groups/ID/issues?state=closed
//! GET /groups/ID/issues?labels=foo
//! GET /groups/ID/issues?labels=foo,bar
//! GET /groups/ID/issues?labels=foo,bar&state=opened
//! GET /groups/ID/issues?milestone=1.0.0
//! GET /groups/ID/issues?milestone=1.0.0&state=opened
//! ```
//!
//! | Attribute | Type | Required | Description |
//! | --------- | ---- | -------- | ----------- |
//! | `id`      | integer | yes   | The ID of a group |
//! | `state`   | string  | no    | Return all issues or just those that are `opened` or `closed`|
//! | `labels`  | string  | no    | Comma-separated list of label names, issues must have all labels to be returned |
//! | `milestone` | string| no    | The milestone title |
//! | `order_by`| string  | no    | Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at` |
//! | `sort`    | string  | no    | Return requests sorted in `asc` or `desc` order. Default is `desc`  |
//!

use serde_urlencoded;

use BuildQuery;
use Issues;
use issues::GroupIssuesListerInternal;

use ::errors::*;

#[derive(Debug, Clone)]
pub struct IssuesLister<'a> {
    gl: &'a ::GitLab,
    /// The ID of a group
    id: i64,
    internal: GroupIssuesListerInternal,
}

impl<'a> IssuesLister<'a> {
    pub fn new(gl: &'a ::GitLab, id: i64) -> IssuesLister {
        IssuesLister {
            gl: gl,
            id: id,
            internal: GroupIssuesListerInternal {
                state: None,
                labels: None,
                milestone: None,
                order_by: None,
                sort: None,
            },
        }
    }

    pub fn state(&'a mut self, state: ::issues::State) -> &'a mut IssuesLister {
        self.internal.state = Some(state);
        self
    }

    pub fn milestone(&'a mut self, milestone: String) -> &'a mut IssuesLister {
        self.internal.milestone = Some(milestone);
        self
    }

    pub fn labels(&'a mut self, labels: Vec<String>) -> &'a mut IssuesLister {
        self.internal.labels = Some(labels);
        self
    }

    pub fn order_by(&'a mut self, order_by: ::issues::ListingOrderBy) -> &'a mut IssuesLister {
        self.internal.order_by = Some(order_by);
        self
    }

    pub fn sort(&'a mut self, sort: ::ListingSort) -> &'a mut IssuesLister {
        self.internal.sort = Some(sort);
        self
    }

    /// Commit the lister: Query GitLab and return a list of issues.
    pub fn list(&self) -> Result<Issues> {
        let query = self.build_query();
        debug!("query: {:?}", query);

        self.gl.get(&query).chain_err(|| format!("cannot get query {}", query))
    }
}

impl<'a> BuildQuery for IssuesLister<'a> {
    fn build_query(&self) -> String {

        // NOTE: Can't use `serde_urlencoded` since it cannot serialize a Vec<T>
        //       See https://github.com/nox/serde_urlencoded/issues/6
        // let encoded = serde_urlencoded::to_string(&self.internal).unwrap();

        let mut query = format!("groups/{}/issues", self.id);
        let amp_char = "&";
        let comma_char = ",";
        let none_char = "";
        let mut split_char = &none_char;

        // Append a "?" only if at least one of the `Option` is `Some(_)` or if
        // strings contain something.
        query.push_str(match (&self.internal.state,
                              &self.internal.labels,
                              &self.internal.milestone,
                              &self.internal.order_by,
                              &self.internal.sort) {
            (&None, &None, &None, &None, &None) => "",
            _ => "?",
        });

        self.internal.state.map(|state| {
            query.push_str(split_char);
            split_char = &amp_char;

            query.push_str("state=");
            query.push_str(match state {
                ::issues::State::Opened => "opened",
                ::issues::State::Closed => "closed",
            });
        });

        self.internal.labels.as_ref().map(|labels| {
            query.push_str(split_char);
            split_char = &amp_char;

            query.push_str("labels=");

            let mut array_split_char = &none_char;
            for label in labels {
                query.push_str(array_split_char);
                query.push_str(&label.to_string());
                array_split_char = &comma_char;
            }
        });

        self.internal.milestone.as_ref().map(|milestone| {
            query.push_str(split_char);
            split_char = &amp_char;

            let params = &[("milestone", milestone)];
            query.push_str(&serde_urlencoded::to_string(&params).unwrap());
        });

        self.internal.order_by.map(|order_by| {
            query.push_str(split_char);
            split_char = &amp_char;

            query.push_str("order_by=");
            query.push_str(match order_by {
                ::issues::ListingOrderBy::CreatedAt => "created_at",
                ::issues::ListingOrderBy::UpdatedAt => "updated_at",
            });
        });

        self.internal.sort.map(|sort| {
            query.push_str(split_char);
            split_char = &amp_char;

            query.push_str("sort=");
            query.push_str(match sort {
                ::ListingSort::Asc => "asc",
                ::ListingSort::Desc => "desc",
            });
        });

        query
    }
}

#[cfg(test)]
mod tests {
    use BuildQuery;

    const TEST_PROJECT_ID: i64 = 123;

    #[test]
    fn build_query_default() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap();
        // let gl: ::GitLab = Default::default();

        let expected_string = format!("groups/{}/issues", TEST_PROJECT_ID);

        let lister = gl.issues();
        let lister = lister.group(TEST_PROJECT_ID);
        let query = lister.build_query();
        assert_eq!(query, expected_string);

        let lister = gl.issues().group(TEST_PROJECT_ID);
        let query = lister.build_query();
        assert_eq!(query, expected_string);

        let query = gl.issues().group(TEST_PROJECT_ID).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_state() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap();
        // let gl: ::GitLab = Default::default();

        let expected_string = "groups/123/issues?state=opened";
        let query = gl.issues().group(TEST_PROJECT_ID).state(::issues::State::Opened).build_query();
        assert_eq!(query, expected_string);

        let expected_string = "groups/123/issues?state=closed";
        let query = gl.issues().group(TEST_PROJECT_ID).state(::issues::State::Closed).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_milestone() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap();
        // let gl: ::GitLab = Default::default();

        let expected_string = "groups/123/issues?milestone=TestMilestone";
        let query = gl.issues().group(TEST_PROJECT_ID).milestone("TestMilestone".to_string()).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_skip_groups() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap();
        // let gl: ::GitLab = Default::default();

        let expected_string = "groups/123/issues?labels=label1,label2,label3";
        let query = gl.issues()
            .group(TEST_PROJECT_ID)
            .labels(vec![String::from("label1"), String::from("label2"), String::from("label3")])
            .build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_order_by() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap();
        // let gl: ::GitLab = Default::default();

        let expected_string = "groups/123/issues?order_by=created_at";
        let query = gl.issues()
            .group(TEST_PROJECT_ID)
            .order_by(::issues::ListingOrderBy::CreatedAt)
            .build_query();
        assert_eq!(query, expected_string);

        let expected_string = "groups/123/issues?order_by=updated_at";
        let query = gl.issues()
            .group(TEST_PROJECT_ID)
            .order_by(::issues::ListingOrderBy::UpdatedAt)
            .build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_sort() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap();
        // let gl: ::GitLab = Default::default();

        let expected_string = "groups/123/issues?sort=asc";
        let query = gl.issues().group(TEST_PROJECT_ID).sort(::ListingSort::Asc).build_query();
        assert_eq!(query, expected_string);

        let expected_string = "groups/123/issues?sort=desc";
        let query = gl.issues().group(TEST_PROJECT_ID).sort(::ListingSort::Desc).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_multiple() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap();
        // let gl: ::GitLab = Default::default();

        let expected_string = "groups/123/issues?order_by=created_at&sort=asc";
        let query = gl.issues()
            .group(TEST_PROJECT_ID)
            .sort(::ListingSort::Asc)
            .order_by(::issues::ListingOrderBy::CreatedAt)
            .build_query();
        assert_eq!(query, expected_string);
    }
}
<|endoftext|>


<commit_before>#[doc = "Operations and constants for `uint`"];

const min_value: uint = 0u;
const max_value: uint = 0u - 1u;

pure fn min(x: uint, y: uint) -> uint { if x < y { x } else { y } }
pure fn max(x: uint, y: uint) -> uint { if x > y { x } else { y } }

pure fn add(x: uint, y: uint) -> uint { ret x + y; }
pure fn sub(x: uint, y: uint) -> uint { ret x - y; }
pure fn mul(x: uint, y: uint) -> uint { ret x * y; }
pure fn div(x: uint, y: uint) -> uint { ret x / y; }

pure fn rem(x: uint, y: uint) -> uint { ret x % y; }
pure fn lt(x: uint, y: uint) -> bool { ret x < y; }
pure fn le(x: uint, y: uint) -> bool { ret x <= y; }
pure fn eq(x: uint, y: uint) -> bool { ret x == y; }
pure fn ne(x: uint, y: uint) -> bool { ret x != y; }
pure fn ge(x: uint, y: uint) -> bool { ret x >= y; }
pure fn gt(x: uint, y: uint) -> bool { ret x > y; }

#[doc = "
Divide two numbers, return the result, rounded up.

# Arguments

* x - an integer
* y - an integer distinct from 0u

# Return value

The smallest integer `q` such that `x/y <= q`.
"]
pure fn div_ceil(x: uint, y: uint) -> uint {
    let div = div(x, y);
    if x % y == 0u { ret div;}
    else { ret div + 1u; }
}

#[doc = "
Divide two numbers, return the result, rounded to the closest integer.

# Arguments

* x - an integer
* y - an integer distinct from 0u

# Return value

The integer `q` closest to `x/y`.
"]
pure fn div_round(x: uint, y: uint) -> uint {
    let div = div(x, y);
    if x % y * 2u  < y { ret div;}
    else { ret div + 1u; }
}

#[doc = "
Divide two numbers, return the result, rounded down.

Note: This is the same function as `div`.

# Arguments

* x - an integer
* y - an integer distinct from 0u

# Return value

The smallest integer `q` such that `x/y <= q`. This
is either `x/y` or `x/y + 1`.
"]
pure fn div_floor(x: uint, y: uint) -> uint { ret x / y; }

#[doc = "Produce a uint suitable for use in a hash table"]
fn hash(x: uint) -> uint { ret x; }

#[doc = "Iterate over the range [`lo`..`hi`)"]
#[inline(always)]
fn range(lo: uint, hi: uint, it: fn(uint)) {
    let mut i = lo;
    while i < hi { it(i); i += 1u; }
}

#[doc = "
Iterate over the range [`lo`..`hi`), or stop when requested

# Arguments

* lo - The integer at which to start the loop (included)
* hi - The integer at which to stop the loop (excluded)
* it - A block to execute with each consecutive integer of the range.
       Return `true` to continue, `false` to stop.

# Return value

`true` If execution proceeded correctly, `false` if it was interrupted,
that is if `it` returned `false` at any point.
"]
fn iterate(lo: uint, hi: uint, it: fn(uint) -> bool) -> bool {
    let mut i = lo;
    while i < hi {
        if (!it(i)) { ret false; }
        i += 1u;
    }
    ret true;
}

#[doc = "Returns the smallest power of 2 greater than or equal to `n`"]
fn next_power_of_two(n: uint) -> uint {
    let halfbits: uint = sys::size_of::<uint>() * 4u;
    let mut tmp: uint = n - 1u;
    let mut shift: uint = 1u;
    while shift <= halfbits { tmp |= tmp >> shift; shift <<= 1u; }
    ret tmp + 1u;
}

#[doc = "
Parse a buffer of bytes

# Arguments

* buf - A byte buffer
* radix - The base of the number

# Failure

`buf` must not be empty
"]
fn parse_buf(buf: [u8], radix: uint) -> option<uint> {
    if vec::len(buf) == 0u { ret none; }
    let mut i = vec::len(buf) - 1u;
    let mut power = 1u;
    let mut n = 0u;
    while true {
        alt char::to_digit(buf[i] as char, radix) {
          some(d) { n += d * power; }
          none { ret none; }
        }
        power *= radix;
        if i == 0u { ret some(n); }
        i -= 1u;
    }
    fail;
}

#[doc = "Parse a string to an int"]
fn from_str(s: str) -> option<uint> { parse_buf(str::bytes(s), 10u) }

#[doc = "Convert to a string in a given base"]
fn to_str(num: uint, radix: uint) -> str {
    let mut n = num;
    assert (0u < radix && radix <= 16u);
    fn digit(n: uint) -> char {
        ret alt n {
              0u { '0' }
              1u { '1' }
              2u { '2' }
              3u { '3' }
              4u { '4' }
              5u { '5' }
              6u { '6' }
              7u { '7' }
              8u { '8' }
              9u { '9' }
              10u { 'a' }
              11u { 'b' }
              12u { 'c' }
              13u { 'd' }
              14u { 'e' }
              15u { 'f' }
              _ { fail }
            };
    }
    if n == 0u { ret "0"; }
    let mut s: str = "";
    while n != 0u {
        s += str::from_byte(digit(n % radix) as u8);
        n /= radix;
    }
    let mut s1: str = "";
    let mut len: uint = str::len(s);
    while len != 0u { len -= 1u; s1 += str::from_byte(s[len]); }
    ret s1;
}

#[doc = "Convert to a string"]
fn str(i: uint) -> str { ret to_str(i, 10u); }

#[doc = "Computes the bitwise complement"]
fn compl(i: uint) -> uint {
    max_value ^ i
}

#[cfg(test)]
mod tests {

    #[test]
    fn test_from_str() {
        assert uint::from_str("0") == some(0u);
        assert uint::from_str("3") == some(3u);
        assert uint::from_str("10") == some(10u);
        assert uint::from_str("123456789") == some(123456789u);
        assert uint::from_str("00100") == some(100u);

        assert uint::from_str("") == none;
        assert uint::from_str(" ") == none;
        assert uint::from_str("x") == none;
    }

    #[Test]
    fn test_parse_buf() {
        import str::bytes;
        assert uint::parse_buf(bytes("123"), 10u) == some(123u);
        assert uint::parse_buf(bytes("1001"), 2u) == some(9u);
        assert uint::parse_buf(bytes("123"), 8u) == some(83u);
        assert uint::parse_buf(bytes("123"), 16u) == some(291u);
        assert uint::parse_buf(bytes("ffff"), 16u) == some(65535u);
        assert uint::parse_buf(bytes("z"), 36u) == some(35u);

        assert uint::parse_buf(str::bytes("Z"), 10u) == none;
        assert uint::parse_buf(str::bytes("_"), 2u) == none;
    }

    #[test]
    fn test_next_power_of_two() {
        assert (uint::next_power_of_two(0u) == 0u);
        assert (uint::next_power_of_two(1u) == 1u);
        assert (uint::next_power_of_two(2u) == 2u);
        assert (uint::next_power_of_two(3u) == 4u);
        assert (uint::next_power_of_two(4u) == 4u);
        assert (uint::next_power_of_two(5u) == 8u);
        assert (uint::next_power_of_two(6u) == 8u);
        assert (uint::next_power_of_two(7u) == 8u);
        assert (uint::next_power_of_two(8u) == 8u);
        assert (uint::next_power_of_two(9u) == 16u);
        assert (uint::next_power_of_two(10u) == 16u);
        assert (uint::next_power_of_two(11u) == 16u);
        assert (uint::next_power_of_two(12u) == 16u);
        assert (uint::next_power_of_two(13u) == 16u);
        assert (uint::next_power_of_two(14u) == 16u);
        assert (uint::next_power_of_two(15u) == 16u);
        assert (uint::next_power_of_two(16u) == 16u);
        assert (uint::next_power_of_two(17u) == 32u);
        assert (uint::next_power_of_two(18u) == 32u);
        assert (uint::next_power_of_two(19u) == 32u);
        assert (uint::next_power_of_two(20u) == 32u);
        assert (uint::next_power_of_two(21u) == 32u);
        assert (uint::next_power_of_two(22u) == 32u);
        assert (uint::next_power_of_two(23u) == 32u);
        assert (uint::next_power_of_two(24u) == 32u);
        assert (uint::next_power_of_two(25u) == 32u);
        assert (uint::next_power_of_two(26u) == 32u);
        assert (uint::next_power_of_two(27u) == 32u);
        assert (uint::next_power_of_two(28u) == 32u);
        assert (uint::next_power_of_two(29u) == 32u);
        assert (uint::next_power_of_two(30u) == 32u);
        assert (uint::next_power_of_two(31u) == 32u);
        assert (uint::next_power_of_two(32u) == 32u);
        assert (uint::next_power_of_two(33u) == 64u);
        assert (uint::next_power_of_two(34u) == 64u);
        assert (uint::next_power_of_two(35u) == 64u);
        assert (uint::next_power_of_two(36u) == 64u);
        assert (uint::next_power_of_two(37u) == 64u);
        assert (uint::next_power_of_two(38u) == 64u);
        assert (uint::next_power_of_two(39u) == 64u);
    }

    #[test]
    fn test_overflows() {
        assert (uint::max_value > 0u);
        assert (uint::min_value <= 0u);
        assert (uint::min_value + uint::max_value + 1u == 0u);
    }

    #[test]
    fn test_div() {
        assert(uint::div_floor(3u, 4u) == 0u);
        assert(uint::div_ceil(3u, 4u)  == 1u);
        assert(uint::div_round(3u, 4u) == 1u);
    }
}

// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
<commit_msg>core: Formatting<commit_after>#[doc = "Operations and constants for `uint`"];

const min_value: uint = 0u;
const max_value: uint = 0u - 1u;

pure fn min(x: uint, y: uint) -> uint { if x < y { x } else { y } }
pure fn max(x: uint, y: uint) -> uint { if x > y { x } else { y } }

pure fn add(x: uint, y: uint) -> uint { ret x + y; }
pure fn sub(x: uint, y: uint) -> uint { ret x - y; }
pure fn mul(x: uint, y: uint) -> uint { ret x * y; }
pure fn div(x: uint, y: uint) -> uint { ret x / y; }
pure fn rem(x: uint, y: uint) -> uint { ret x % y; }

pure fn lt(x: uint, y: uint) -> bool { ret x < y; }
pure fn le(x: uint, y: uint) -> bool { ret x <= y; }
pure fn eq(x: uint, y: uint) -> bool { ret x == y; }
pure fn ne(x: uint, y: uint) -> bool { ret x != y; }
pure fn ge(x: uint, y: uint) -> bool { ret x >= y; }
pure fn gt(x: uint, y: uint) -> bool { ret x > y; }

#[doc = "
Divide two numbers, return the result, rounded up.

# Arguments

* x - an integer
* y - an integer distinct from 0u

# Return value

The smallest integer `q` such that `x/y <= q`.
"]
pure fn div_ceil(x: uint, y: uint) -> uint {
    let div = div(x, y);
    if x % y == 0u { ret div;}
    else { ret div + 1u; }
}

#[doc = "
Divide two numbers, return the result, rounded to the closest integer.

# Arguments

* x - an integer
* y - an integer distinct from 0u

# Return value

The integer `q` closest to `x/y`.
"]
pure fn div_round(x: uint, y: uint) -> uint {
    let div = div(x, y);
    if x % y * 2u  < y { ret div;}
    else { ret div + 1u; }
}

#[doc = "
Divide two numbers, return the result, rounded down.

Note: This is the same function as `div`.

# Arguments

* x - an integer
* y - an integer distinct from 0u

# Return value

The smallest integer `q` such that `x/y <= q`. This
is either `x/y` or `x/y + 1`.
"]
pure fn div_floor(x: uint, y: uint) -> uint { ret x / y; }

#[doc = "Produce a uint suitable for use in a hash table"]
fn hash(x: uint) -> uint { ret x; }

#[doc = "Iterate over the range [`lo`..`hi`)"]
#[inline(always)]
fn range(lo: uint, hi: uint, it: fn(uint)) {
    let mut i = lo;
    while i < hi { it(i); i += 1u; }
}

#[doc = "
Iterate over the range [`lo`..`hi`), or stop when requested

# Arguments

* lo - The integer at which to start the loop (included)
* hi - The integer at which to stop the loop (excluded)
* it - A block to execute with each consecutive integer of the range.
       Return `true` to continue, `false` to stop.

# Return value

`true` If execution proceeded correctly, `false` if it was interrupted,
that is if `it` returned `false` at any point.
"]
fn iterate(lo: uint, hi: uint, it: fn(uint) -> bool) -> bool {
    let mut i = lo;
    while i < hi {
        if (!it(i)) { ret false; }
        i += 1u;
    }
    ret true;
}

#[doc = "Returns the smallest power of 2 greater than or equal to `n`"]
fn next_power_of_two(n: uint) -> uint {
    let halfbits: uint = sys::size_of::<uint>() * 4u;
    let mut tmp: uint = n - 1u;
    let mut shift: uint = 1u;
    while shift <= halfbits { tmp |= tmp >> shift; shift <<= 1u; }
    ret tmp + 1u;
}

#[doc = "
Parse a buffer of bytes

# Arguments

* buf - A byte buffer
* radix - The base of the number

# Failure

`buf` must not be empty
"]
fn parse_buf(buf: [u8], radix: uint) -> option<uint> {
    if vec::len(buf) == 0u { ret none; }
    let mut i = vec::len(buf) - 1u;
    let mut power = 1u;
    let mut n = 0u;
    while true {
        alt char::to_digit(buf[i] as char, radix) {
          some(d) { n += d * power; }
          none { ret none; }
        }
        power *= radix;
        if i == 0u { ret some(n); }
        i -= 1u;
    }
    fail;
}

#[doc = "Parse a string to an int"]
fn from_str(s: str) -> option<uint> { parse_buf(str::bytes(s), 10u) }

#[doc = "Convert to a string in a given base"]
fn to_str(num: uint, radix: uint) -> str {
    let mut n = num;
    assert (0u < radix && radix <= 16u);
    fn digit(n: uint) -> char {
        ret alt n {
              0u { '0' }
              1u { '1' }
              2u { '2' }
              3u { '3' }
              4u { '4' }
              5u { '5' }
              6u { '6' }
              7u { '7' }
              8u { '8' }
              9u { '9' }
              10u { 'a' }
              11u { 'b' }
              12u { 'c' }
              13u { 'd' }
              14u { 'e' }
              15u { 'f' }
              _ { fail }
            };
    }
    if n == 0u { ret "0"; }
    let mut s: str = "";
    while n != 0u {
        s += str::from_byte(digit(n % radix) as u8);
        n /= radix;
    }
    let mut s1: str = "";
    let mut len: uint = str::len(s);
    while len != 0u { len -= 1u; s1 += str::from_byte(s[len]); }
    ret s1;
}

#[doc = "Convert to a string"]
fn str(i: uint) -> str { ret to_str(i, 10u); }

#[doc = "Computes the bitwise complement"]
fn compl(i: uint) -> uint {
    max_value ^ i
}

#[cfg(test)]
mod tests {

    #[test]
    fn test_from_str() {
        assert uint::from_str("0") == some(0u);
        assert uint::from_str("3") == some(3u);
        assert uint::from_str("10") == some(10u);
        assert uint::from_str("123456789") == some(123456789u);
        assert uint::from_str("00100") == some(100u);

        assert uint::from_str("") == none;
        assert uint::from_str(" ") == none;
        assert uint::from_str("x") == none;
    }

    #[Test]
    fn test_parse_buf() {
        import str::bytes;
        assert uint::parse_buf(bytes("123"), 10u) == some(123u);
        assert uint::parse_buf(bytes("1001"), 2u) == some(9u);
        assert uint::parse_buf(bytes("123"), 8u) == some(83u);
        assert uint::parse_buf(bytes("123"), 16u) == some(291u);
        assert uint::parse_buf(bytes("ffff"), 16u) == some(65535u);
        assert uint::parse_buf(bytes("z"), 36u) == some(35u);

        assert uint::parse_buf(str::bytes("Z"), 10u) == none;
        assert uint::parse_buf(str::bytes("_"), 2u) == none;
    }

    #[test]
    fn test_next_power_of_two() {
        assert (uint::next_power_of_two(0u) == 0u);
        assert (uint::next_power_of_two(1u) == 1u);
        assert (uint::next_power_of_two(2u) == 2u);
        assert (uint::next_power_of_two(3u) == 4u);
        assert (uint::next_power_of_two(4u) == 4u);
        assert (uint::next_power_of_two(5u) == 8u);
        assert (uint::next_power_of_two(6u) == 8u);
        assert (uint::next_power_of_two(7u) == 8u);
        assert (uint::next_power_of_two(8u) == 8u);
        assert (uint::next_power_of_two(9u) == 16u);
        assert (uint::next_power_of_two(10u) == 16u);
        assert (uint::next_power_of_two(11u) == 16u);
        assert (uint::next_power_of_two(12u) == 16u);
        assert (uint::next_power_of_two(13u) == 16u);
        assert (uint::next_power_of_two(14u) == 16u);
        assert (uint::next_power_of_two(15u) == 16u);
        assert (uint::next_power_of_two(16u) == 16u);
        assert (uint::next_power_of_two(17u) == 32u);
        assert (uint::next_power_of_two(18u) == 32u);
        assert (uint::next_power_of_two(19u) == 32u);
        assert (uint::next_power_of_two(20u) == 32u);
        assert (uint::next_power_of_two(21u) == 32u);
        assert (uint::next_power_of_two(22u) == 32u);
        assert (uint::next_power_of_two(23u) == 32u);
        assert (uint::next_power_of_two(24u) == 32u);
        assert (uint::next_power_of_two(25u) == 32u);
        assert (uint::next_power_of_two(26u) == 32u);
        assert (uint::next_power_of_two(27u) == 32u);
        assert (uint::next_power_of_two(28u) == 32u);
        assert (uint::next_power_of_two(29u) == 32u);
        assert (uint::next_power_of_two(30u) == 32u);
        assert (uint::next_power_of_two(31u) == 32u);
        assert (uint::next_power_of_two(32u) == 32u);
        assert (uint::next_power_of_two(33u) == 64u);
        assert (uint::next_power_of_two(34u) == 64u);
        assert (uint::next_power_of_two(35u) == 64u);
        assert (uint::next_power_of_two(36u) == 64u);
        assert (uint::next_power_of_two(37u) == 64u);
        assert (uint::next_power_of_two(38u) == 64u);
        assert (uint::next_power_of_two(39u) == 64u);
    }

    #[test]
    fn test_overflows() {
        assert (uint::max_value > 0u);
        assert (uint::min_value <= 0u);
        assert (uint::min_value + uint::max_value + 1u == 0u);
    }

    #[test]
    fn test_div() {
        assert(uint::div_floor(3u, 4u) == 0u);
        assert(uint::div_ceil(3u, 4u)  == 1u);
        assert(uint::div_round(3u, 4u) == 1u);
    }
}

// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
<|endoftext|>


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

use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::collections::hash_state::DefaultState;
use rand::Rng;
use std::hash::{Hash, Hasher, SipHasher};
use std::iter::repeat;
use rand;
use std::slice::Iter;
use std::default::Default;

#[cfg(test)]
use std::cell::Cell;

pub struct HashCache<K, V> {
    entries: HashMap<K, V, DefaultState<SipHasher>>,
}

impl<K, V> HashCache<K,V>
    where K: Clone + PartialEq + Eq + Hash,
          V: Clone,
{
    pub fn new() -> HashCache<K,V> {
        HashCache {
          entries: HashMap::with_hash_state(<DefaultState<SipHasher> as Default>::default()),
        }
    }

    pub fn insert(&mut self, key: K, value: V) {
        self.entries.insert(key, value);
    }

    pub fn find(&self, key: &K) -> Option<V> {
        match self.entries.get(key) {
            Some(v) => Some(v.clone()),
            None => None,
        }
    }

    pub fn find_or_create<F>(&mut self, key: &K, blk: F) -> V where F: Fn(&K) -> V {
        match self.entries.entry(key.clone()) {
            Occupied(occupied) => {
                (*occupied.get()).clone()
            }
            Vacant(vacant) => {
                (*vacant.insert(blk(key))).clone()
            }
        }
    }

    pub fn evict_all(&mut self) {
        self.entries.clear();
    }
}

#[test]
fn test_hashcache() {
    let mut cache: HashCache<usize, Cell<&str>> = HashCache::new();

    cache.insert(1, Cell::new("one"));
    assert!(cache.find(&1).is_some());
    assert!(cache.find(&2).is_none());

    cache.find_or_create(&2, |_v| { Cell::new("two") });
    assert!(cache.find(&1).is_some());
    assert!(cache.find(&2).is_some());
}

pub struct LRUCache<K, V> {
    entries: Vec<(K, V)>,
    cache_size: usize,
}

impl<K: Clone + PartialEq, V: Clone> LRUCache<K,V> {
    pub fn new(size: usize) -> LRUCache<K, V> {
        LRUCache {
          entries: vec!(),
          cache_size: size,
        }
    }

    #[inline]
    pub fn touch(&mut self, pos: usize) -> V {
        let last_index = self.entries.len() - 1;
        if pos != last_index {
            let entry = self.entries.remove(pos);
            self.entries.push(entry);
        }
        self.entries[last_index].1.clone()
    }

    pub fn iter<'a>(&'a self) -> Iter<'a,(K,V)> {
        self.entries.iter()
    }

    pub fn insert(&mut self, key: K, val: V) {
        if self.entries.len() == self.cache_size {
            self.entries.remove(0);
        }
        self.entries.push((key, val));
    }

    pub fn find(&mut self, key: &K) -> Option<V> {
        match self.entries.iter().position(|&(ref k, _)| key == k) {
            Some(pos) => Some(self.touch(pos)),
            None      => None,
        }
    }

    pub fn find_or_create<F>(&mut self, key: &K, blk: F) -> V where F: Fn(&K) -> V {
        match self.entries.iter().position(|&(ref k, _)| *k == *key) {
            Some(pos) => self.touch(pos),
            None => {
                let val = blk(key);
                self.insert(key.clone(), val.clone());
                val
            }
        }
    }

    pub fn evict_all(&mut self) {
        self.entries.clear();
    }
}

pub struct SimpleHashCache<K,V> {
    entries: Vec<Option<(K,V)>>,
    k0: u64,
    k1: u64,
}

impl<K:Clone+Eq+Hash,V:Clone> SimpleHashCache<K,V> {
    pub fn new(cache_size: usize) -> SimpleHashCache<K,V> {
        let mut r = rand::thread_rng();
        SimpleHashCache {
            entries: repeat(None).take(cache_size).collect(),
            k0: r.gen(),
            k1: r.gen(),
        }
    }

    #[inline]
    fn to_bucket(&self, h: usize) -> usize {
        h % self.entries.len()
    }

    #[inline]
    fn bucket_for_key<Q:Hash>(&self, key: &Q) -> usize {
        let mut hasher = SipHasher::new_with_keys(self.k0, self.k1);
        key.hash(&mut hasher);
        self.to_bucket(hasher.finish() as usize)
    }

    pub fn insert(&mut self, key: K, value: V) {
        let bucket_index = self.bucket_for_key(&key);
        self.entries[bucket_index] = Some((key, value));
    }

    pub fn find<Q>(&self, key: &Q) -> Option<V> where Q: PartialEq<K> + Hash + Eq {
        let bucket_index = self.bucket_for_key(key);
        match self.entries[bucket_index] {
            Some((ref existing_key, ref value)) if key == existing_key => Some((*value).clone()),
            _ => None,
        }
    }

    pub fn find_or_create<F>(&mut self, key: &K, blk: F) -> V where F: Fn(&K) -> V {
        match self.find(key) {
            Some(value) => return value,
            None => {}
        }
        let value = blk(key);
        self.insert((*key).clone(), value.clone());
        value
    }

    pub fn evict_all(&mut self) {
        for slot in self.entries.iter_mut() {
            *slot = None
        }
    }
}

#[test]
fn test_lru_cache() {
    let one = Cell::new("one");
    let two = Cell::new("two");
    let three = Cell::new("three");
    let four = Cell::new("four");

    // Test normal insertion.
    let mut cache: LRUCache<uint,Cell<&str>> = LRUCache::new(2); // (_, _) (cache is empty)
    cache.insert(1, one);    // (1, _)
    cache.insert(2, two);    // (1, 2)
    cache.insert(3, three);  // (2, 3)

    assert!(cache.find(&1).is_none());  // (2, 3) (no change)
    assert!(cache.find(&3).is_some());  // (2, 3)
    assert!(cache.find(&2).is_some());  // (3, 2)

    // Test that LRU works (this insertion should replace 3, not 2).
    cache.insert(4, four); // (2, 4)

    assert!(cache.find(&1).is_none());  // (2, 4) (no change)
    assert!(cache.find(&2).is_some());  // (4, 2)
    assert!(cache.find(&3).is_none());  // (4, 2) (no change)
    assert!(cache.find(&4).is_some());  // (2, 4) (no change)

    // Test find_or_create.
    cache.find_or_create(&1, |_| { Cell::new("one") }); // (4, 1)

    assert!(cache.find(&1).is_some()); // (4, 1) (no change)
    assert!(cache.find(&2).is_none()); // (4, 1) (no change)
    assert!(cache.find(&3).is_none()); // (4, 1) (no change)
    assert!(cache.find(&4).is_some()); // (1, 4)
}
<commit_msg>Use usize in test_lru_cache.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::collections::hash_state::DefaultState;
use rand::Rng;
use std::hash::{Hash, Hasher, SipHasher};
use std::iter::repeat;
use rand;
use std::slice::Iter;
use std::default::Default;

#[cfg(test)]
use std::cell::Cell;

pub struct HashCache<K, V> {
    entries: HashMap<K, V, DefaultState<SipHasher>>,
}

impl<K, V> HashCache<K,V>
    where K: Clone + PartialEq + Eq + Hash,
          V: Clone,
{
    pub fn new() -> HashCache<K,V> {
        HashCache {
          entries: HashMap::with_hash_state(<DefaultState<SipHasher> as Default>::default()),
        }
    }

    pub fn insert(&mut self, key: K, value: V) {
        self.entries.insert(key, value);
    }

    pub fn find(&self, key: &K) -> Option<V> {
        match self.entries.get(key) {
            Some(v) => Some(v.clone()),
            None => None,
        }
    }

    pub fn find_or_create<F>(&mut self, key: &K, blk: F) -> V where F: Fn(&K) -> V {
        match self.entries.entry(key.clone()) {
            Occupied(occupied) => {
                (*occupied.get()).clone()
            }
            Vacant(vacant) => {
                (*vacant.insert(blk(key))).clone()
            }
        }
    }

    pub fn evict_all(&mut self) {
        self.entries.clear();
    }
}

#[test]
fn test_hashcache() {
    let mut cache: HashCache<usize, Cell<&str>> = HashCache::new();

    cache.insert(1, Cell::new("one"));
    assert!(cache.find(&1).is_some());
    assert!(cache.find(&2).is_none());

    cache.find_or_create(&2, |_v| { Cell::new("two") });
    assert!(cache.find(&1).is_some());
    assert!(cache.find(&2).is_some());
}

pub struct LRUCache<K, V> {
    entries: Vec<(K, V)>,
    cache_size: usize,
}

impl<K: Clone + PartialEq, V: Clone> LRUCache<K,V> {
    pub fn new(size: usize) -> LRUCache<K, V> {
        LRUCache {
          entries: vec!(),
          cache_size: size,
        }
    }

    #[inline]
    pub fn touch(&mut self, pos: usize) -> V {
        let last_index = self.entries.len() - 1;
        if pos != last_index {
            let entry = self.entries.remove(pos);
            self.entries.push(entry);
        }
        self.entries[last_index].1.clone()
    }

    pub fn iter<'a>(&'a self) -> Iter<'a,(K,V)> {
        self.entries.iter()
    }

    pub fn insert(&mut self, key: K, val: V) {
        if self.entries.len() == self.cache_size {
            self.entries.remove(0);
        }
        self.entries.push((key, val));
    }

    pub fn find(&mut self, key: &K) -> Option<V> {
        match self.entries.iter().position(|&(ref k, _)| key == k) {
            Some(pos) => Some(self.touch(pos)),
            None      => None,
        }
    }

    pub fn find_or_create<F>(&mut self, key: &K, blk: F) -> V where F: Fn(&K) -> V {
        match self.entries.iter().position(|&(ref k, _)| *k == *key) {
            Some(pos) => self.touch(pos),
            None => {
                let val = blk(key);
                self.insert(key.clone(), val.clone());
                val
            }
        }
    }

    pub fn evict_all(&mut self) {
        self.entries.clear();
    }
}

pub struct SimpleHashCache<K,V> {
    entries: Vec<Option<(K,V)>>,
    k0: u64,
    k1: u64,
}

impl<K:Clone+Eq+Hash,V:Clone> SimpleHashCache<K,V> {
    pub fn new(cache_size: usize) -> SimpleHashCache<K,V> {
        let mut r = rand::thread_rng();
        SimpleHashCache {
            entries: repeat(None).take(cache_size).collect(),
            k0: r.gen(),
            k1: r.gen(),
        }
    }

    #[inline]
    fn to_bucket(&self, h: usize) -> usize {
        h % self.entries.len()
    }

    #[inline]
    fn bucket_for_key<Q:Hash>(&self, key: &Q) -> usize {
        let mut hasher = SipHasher::new_with_keys(self.k0, self.k1);
        key.hash(&mut hasher);
        self.to_bucket(hasher.finish() as usize)
    }

    pub fn insert(&mut self, key: K, value: V) {
        let bucket_index = self.bucket_for_key(&key);
        self.entries[bucket_index] = Some((key, value));
    }

    pub fn find<Q>(&self, key: &Q) -> Option<V> where Q: PartialEq<K> + Hash + Eq {
        let bucket_index = self.bucket_for_key(key);
        match self.entries[bucket_index] {
            Some((ref existing_key, ref value)) if key == existing_key => Some((*value).clone()),
            _ => None,
        }
    }

    pub fn find_or_create<F>(&mut self, key: &K, blk: F) -> V where F: Fn(&K) -> V {
        match self.find(key) {
            Some(value) => return value,
            None => {}
        }
        let value = blk(key);
        self.insert((*key).clone(), value.clone());
        value
    }

    pub fn evict_all(&mut self) {
        for slot in self.entries.iter_mut() {
            *slot = None
        }
    }
}

#[test]
fn test_lru_cache() {
    let one = Cell::new("one");
    let two = Cell::new("two");
    let three = Cell::new("three");
    let four = Cell::new("four");

    // Test normal insertion.
    let mut cache: LRUCache<usize,Cell<&str>> = LRUCache::new(2); // (_, _) (cache is empty)
    cache.insert(1, one);    // (1, _)
    cache.insert(2, two);    // (1, 2)
    cache.insert(3, three);  // (2, 3)

    assert!(cache.find(&1).is_none());  // (2, 3) (no change)
    assert!(cache.find(&3).is_some());  // (2, 3)
    assert!(cache.find(&2).is_some());  // (3, 2)

    // Test that LRU works (this insertion should replace 3, not 2).
    cache.insert(4, four); // (2, 4)

    assert!(cache.find(&1).is_none());  // (2, 4) (no change)
    assert!(cache.find(&2).is_some());  // (4, 2)
    assert!(cache.find(&3).is_none());  // (4, 2) (no change)
    assert!(cache.find(&4).is_some());  // (2, 4) (no change)

    // Test find_or_create.
    cache.find_or_create(&1, |_| { Cell::new("one") }); // (4, 1)

    assert!(cache.find(&1).is_some()); // (4, 1) (no change)
    assert!(cache.find(&2).is_none()); // (4, 1) (no change)
    assert!(cache.find(&3).is_none()); // (4, 1) (no change)
    assert!(cache.find(&4).is_some()); // (1, 4)
}
<|endoftext|>


<commit_before>//! Treiber stacks.

use std::sync::atomic;
use std::ptr;
use {Atomic, Guard};

/// A Treiber stack.
///
/// Treiber stacks are one way to implement concurrent LIFO stack.
///
/// Treiber stacks builds on linked lists. They are lock-free and non-blocking. It can be compared
/// to transactional memory in that it repeats operations, if another thread changes it while.
///
/// The ABA problem is of course addressed through the API of this crate.
pub struct Treiber<T> {
    /// The head node.
    head: Atomic<Node<T>>,
}

impl<T> Drop for Treiber<T> {
    fn drop(&mut self) {
        // Due to the nature of Treiber stacks, there are no active guards of things within the
        // structure. They're all gone, thus we can safely mess with the inner structure.

        unsafe {
            let ptr = self.head.get_inner_mut().get_mut();

            if !ptr.is_null() {
                // Call destructors on the stack.
                (**ptr).destroy();
                // Finally deallocate the pointer itself.
                drop(Box::from_raw(*ptr));
                // Set it to null to prevent `Atomic`'s destructor from running.
                *ptr = ptr::null_mut();
            }
        }
    }
}

impl<T> Treiber<T> {
    /// Create a new, empty Treiber stack.
    pub fn new() -> Treiber<T> {
        Treiber {
            head: Atomic::default(),
        }
    }

    /// Pop an item from the stack.
    // TODO: Change this return type.
    pub fn pop(&self) -> Option<Guard<T>> {
        // TODO: Use `catch {}` here when it lands.
        // Read the head snapshot.
        let mut snapshot = self.head.load(atomic::Ordering::Acquire);

        // Unless the head snapshot is `None`, try to replace it with the tail.
        while let Some(node) = snapshot {
            // Attempt to replace the head with the tail of the head.
            match unsafe {
                self.head.compare_and_swap_raw(
                    node.as_raw(),
                    node.next as *mut Node<T>,
                    atomic::Ordering::Release,
                )
            } {
                // It succeeded; return the item.
                Ok(_) => return Some(node.map(|x| &x.item)),
                // It failed, update the head snapshot and continue.
                Err(new) => snapshot = new,
            }
        }

        // As the head was empty, there is nothing to pop.
        None
    }

    /// Push an item to the stack.
    pub fn push(&self, item: T)
    where T: 'static {
        // Load the head snapshot.
        let mut snapshot = self.head.load(atomic::Ordering::Relaxed);
        let mut snapshot_ptr: Option<*const Node<T>>;

        // TODO: Use `catch {}` here when it lands.
        // Construct a node, which will be the new head.
        let mut node = Box::new(Node {
            item: item,
            // Placeholder; we will replace it with an actual value in the loop.
            next: ptr::null(),
        });

        loop {
            // Derive the nullable snapshot pointer from the head snapshot.
            snapshot_ptr = snapshot.as_ref().map(Guard::as_raw);
            // Construct the next-pointer of the new node from the head snapshot.
            node.next = snapshot_ptr.unwrap_or(ptr::null());

            // TODO: This should be something that ignores the guard creation when the CAS
            //       succeeds, because it's expensive to do and not used anyway. It should be easy
            //       enough to implement, but I am struggling to come up with a good name for the
            //       method.

            // CAS from the read pointer to the new head.
            match self.head.compare_and_swap(snapshot_ptr, Some(node), atomic::Ordering::Release) {
                // If it succeeds, the item has been pushed.
                Ok(_) => break,
                // If it fails, we will retry the CAS with updated values.
                Err((new_head, Some(node2))) => {
                    // Update the head snapshot.
                    snapshot = new_head;
                    // Put the box we gave back to the variable where it belongs.
                    node = node2;
                },
                // This should never be reached as we gave an argument which was unconditionally
                // `Some`.
                _ => unreachable!(),
            }
        }
    }
}

/// A node in the stack.
struct Node<T> {
    /// The data this node holds.
    item: T,
    /// The next node.
    next: *const Node<T>,
}

impl<T> Node<T> {
    /// Destroy the node and its precessors.
    ///
    /// This doesn't call the destructor on `T`.
    ///
    /// # Safety
    ///
    /// As this can be called multiple times, it is marked unsafe.
    unsafe fn destroy(&mut self) {
        // FIXME: Since this is recursive (and although it is likely optimized out), there might be
        //        cases where this leads to stack overflow, given correct compilation flags and
        //        sufficiently many elements.

        // Recursively drop the next node, if it exists.
        if !self.next.is_null() {
            drop(Box::from_raw(self.next as *mut Node<T>));
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::thread;
    use std::sync::Arc;
    use std::sync::atomic::AtomicUsize;

    #[derive(Clone)]
    struct Dropper {
        d: Arc<AtomicUsize>,
    }

    impl Drop for Dropper {
        fn drop(&mut self) {
            self.d.fetch_add(1, atomic::Ordering::Relaxed);
        }
    }

    #[test]
    fn simple1() {
        let stack = Treiber::new();

        stack.push(1);
        stack.push(200);
        stack.push(44);

        assert_eq!(*stack.pop().unwrap(), 44);
        assert_eq!(*stack.pop().unwrap(), 200);
        assert_eq!(*stack.pop().unwrap(), 1);
        assert!(stack.pop().is_none());

        ::gc();
    }

    #[test]
    fn simple2() {
        let stack = Treiber::new();

        for _ in 0..16 {
            stack.push(1);
            stack.push(200);
            stack.push(44);

            assert_eq!(*stack.pop().unwrap(), 44);
            assert_eq!(*stack.pop().unwrap(), 200);
            stack.push(20000);

            assert_eq!(*stack.pop().unwrap(), 20000);
            assert_eq!(*stack.pop().unwrap(), 1);

            assert!(stack.pop().is_none());
            assert!(stack.pop().is_none());
            assert!(stack.pop().is_none());
            assert!(stack.pop().is_none());
        }

        ::gc();
    }

    #[test]
    fn simple3() {
        let stack = Treiber::new();

        for i in 0..10000 {
            stack.push(i);
        }

        for i in (0..10000).rev() {
            assert_eq!(*stack.pop().unwrap(), i);
        }

        for i in 0..10000 {
            stack.push(i);
        }

        for i in (0..10000).rev() {
            assert_eq!(*stack.pop().unwrap(), i);
        }

        assert!(stack.pop().is_none());
        assert!(stack.pop().is_none());
        assert!(stack.pop().is_none());
        assert!(stack.pop().is_none());
    }

    #[test]
    fn push_pop() {
        let stack = Arc::new(Treiber::new());
        let mut j = Vec::new();
        for _ in 0..16 {
            let s = stack.clone();
            j.push(thread::spawn(move || {
                for _ in 0..10_000_000 {
                    s.push(23);
                    assert_eq!(*s.pop().unwrap(), 23);
                }
            }));
        }

        for i in j {
            i.join().unwrap();
        }
    }

    #[test]
    fn increment() {
        let stack = Arc::new(Treiber::<u64>::new());
        stack.push(0);
        let mut j = Vec::new();

        // 16 times, we add the numbers from 0 to 1000 to the only element in the stack.
        for _ in 0..16 {
            let s = stack.clone();
            j.push(thread::spawn(move || {
                for n in 0..1001 {
                    loop {
                        if let Some(x) = s.pop() {
                            s.push(*x + n);
                            break;
                        }
                    }
                }
            }));
        }

        for i in j {
            i.join().unwrap();
        }

        assert_eq!(*stack.pop().unwrap(), 16 * 1000 * 1001 / 2);
    }

    #[test]
    fn sum() {
        let stack = Arc::new(Treiber::<i64>::new());
        let mut j = Vec::new();

        for _ in 0..1000 {
            stack.push(10);
        }

        // We preserve the sum of the stack's elements.
        for _ in 0..16 {
            let s = stack.clone();
            j.push(thread::spawn(move || {
                for _ in 0..100000 {
                    loop {
                        if let Some(a) = s.pop() {
                            loop {
                                if let Some(b) = s.pop() {
                                    s.push(*a + 1);
                                    s.push(*b - 1);

                                    break;
                                }
                            }

                            break;
                        }
                    }
                }
            }));
        }

        for i in j {
            i.join().unwrap();
        }

        let mut sum = 0;
        while let Some(x) = stack.pop() {
            sum += *x;
        }
        assert_eq!(sum, 10000);
    }

    #[test]
    fn drop() {
        let drops = Arc::new(AtomicUsize::default());
        let stack = Arc::new(Treiber::new());

        let d = Dropper {
            d: drops.clone(),
        };

        let mut j = Vec::new();
        for _ in 0..16 {
            let d = d.clone();
            let stack = stack.clone();

            j.push(thread::spawn(move || {
                for _ in 0..20 {
                    stack.push(d.clone());
                }

                stack.pop();
                stack.pop();
            }))
        }

        for i in j {
            i.join().unwrap();
        }

        ::gc();
        // The 16 are for the `d` variable in the loop above.
        assert_eq!(drops.load(atomic::Ordering::Relaxed), 32 + 16);

        // Drop the last arc.
        let _ = stack;
        ::gc();

        assert_eq!(drops.load(atomic::Ordering::Relaxed), 200 + 16);
    }
}
<commit_msg>Add `conc::sync::Treiber::top()` for getting the top item of the stack w/o popping.<commit_after>//! Treiber stacks.

use std::sync::atomic;
use std::ptr;
use {Atomic, Guard};

/// A Treiber stack.
///
/// Treiber stacks are one way to implement concurrent LIFO stack.
///
/// Treiber stacks builds on linked lists. They are lock-free and non-blocking. It can be compared
/// to transactional memory in that it repeats operations, if another thread changes it while.
///
/// The ABA problem is of course addressed through the API of this crate.
pub struct Treiber<T> {
    /// The head node.
    head: Atomic<Node<T>>,
}

impl<T> Drop for Treiber<T> {
    fn drop(&mut self) {
        // Due to the nature of Treiber stacks, there are no active guards of things within the
        // structure. They're all gone, thus we can safely mess with the inner structure.

        unsafe {
            let ptr = self.head.get_inner_mut().get_mut();

            if !ptr.is_null() {
                // Call destructors on the stack.
                (**ptr).destroy();
                // Finally deallocate the pointer itself.
                drop(Box::from_raw(*ptr));
                // Set it to null to prevent `Atomic`'s destructor from running.
                *ptr = ptr::null_mut();
            }
        }
    }
}

impl<T> Treiber<T> {
    /// Create a new, empty Treiber stack.
    pub fn new() -> Treiber<T> {
        Treiber {
            head: Atomic::default(),
        }
    }

    /// Get the top item of the stack without popping it.
    ///
    /// This is several times faster than `pop`.
    ///
    /// # Memory usage
    ///
    /// This keeps alive what corresponds to the whole stack at the time of the call, such that it
    /// won't be dropped until the guard is dead.
    pub fn top(&self) -> Option<Guard<T>> {
        // Just load the head and map it.
        self.head.load(atomic::Ordering::Acquire).map(|guard| guard.map(|node| &node.item))
    }

    /// Pop an item from the stack.
    pub fn pop(&self) -> Option<Guard<T>> {
        // TODO: Use `catch {}` here when it lands.
        // Read the head snapshot.
        let mut snapshot = self.head.load(atomic::Ordering::Acquire);

        // Unless the head snapshot is `None`, try to replace it with the tail.
        while let Some(node) = snapshot {
            // Attempt to replace the head with the tail of the head.
            match unsafe {
                self.head.compare_and_swap_raw(
                    node.as_raw(),
                    node.next as *mut Node<T>,
                    atomic::Ordering::Release,
                )
            } {
                // It succeeded; return the item.
                Ok(_) => return Some(node.map(|x| &x.item)),
                // It failed, update the head snapshot and continue.
                Err(new) => snapshot = new,
            }
        }

        // As the head was empty, there is nothing to pop.
        None
    }

    /// Push an item to the stack.
    pub fn push(&self, item: T)
    where T: 'static {
        // Load the head snapshot.
        let mut snapshot = self.head.load(atomic::Ordering::Relaxed);
        let mut snapshot_ptr: Option<*const Node<T>>;

        // TODO: Use `catch {}` here when it lands.
        // Construct a node, which will be the new head.
        let mut node = Box::new(Node {
            item: item,
            // Placeholder; we will replace it with an actual value in the loop.
            next: ptr::null(),
        });

        loop {
            // Derive the nullable snapshot pointer from the head snapshot.
            snapshot_ptr = snapshot.as_ref().map(Guard::as_raw);
            // Construct the next-pointer of the new node from the head snapshot.
            node.next = snapshot_ptr.unwrap_or(ptr::null());

            // TODO: This should be something that ignores the guard creation when the CAS
            //       succeeds, because it's expensive to do and not used anyway. It should be easy
            //       enough to implement, but I am struggling to come up with a good name for the
            //       method.

            // CAS from the read pointer to the new head.
            match self.head.compare_and_swap(snapshot_ptr, Some(node), atomic::Ordering::Release) {
                // If it succeeds, the item has been pushed.
                Ok(_) => break,
                // If it fails, we will retry the CAS with updated values.
                Err((new_head, Some(node2))) => {
                    // Update the head snapshot.
                    snapshot = new_head;
                    // Put the box we gave back to the variable where it belongs.
                    node = node2;
                },
                // This should never be reached as we gave an argument which was unconditionally
                // `Some`.
                _ => unreachable!(),
            }
        }
    }
}

/// A node in the stack.
struct Node<T> {
    /// The data this node holds.
    item: T,
    /// The next node.
    next: *const Node<T>,
}

impl<T> Node<T> {
    /// Destroy the node and its precessors.
    ///
    /// This doesn't call the destructor on `T`.
    ///
    /// The reason this isn't replaced by a recursive destructor through `Drop` is that such thing
    /// would cause double drop, as the earlier top element getting dropped would cause the whole
    /// list to be dropped.
    ///
    /// # Safety
    ///
    /// As this can be called multiple times, it is marked unsafe.
    unsafe fn destroy(&mut self) {
        // FIXME: Since this is recursive (and although it is likely optimized out), there might be
        //        cases where this leads to stack overflow, given correct compilation flags and
        //        sufficiently many elements.

        // Recursively drop the next node, if it exists.
        if !self.next.is_null() {
            drop(Box::from_raw(self.next as *mut Node<T>));
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::thread;
    use std::sync::Arc;
    use std::sync::atomic::AtomicUsize;

    #[derive(Clone)]
    struct Dropper {
        d: Arc<AtomicUsize>,
    }

    impl Drop for Dropper {
        fn drop(&mut self) {
            self.d.fetch_add(1, atomic::Ordering::Relaxed);
        }
    }

    #[test]
    fn simple1() {
        let stack = Treiber::new();
        assert!(stack.top().is_none());

        stack.push(1);
        assert_eq!(*stack.top().unwrap(), 1);
        stack.push(200);
        assert_eq!(*stack.top().unwrap(), 200);
        stack.push(44);
        assert_eq!(*stack.top().unwrap(), 44);

        assert_eq!(*stack.pop().unwrap(), 44);
        assert_eq!(*stack.pop().unwrap(), 200);
        assert_eq!(*stack.pop().unwrap(), 1);
        assert!(stack.pop().is_none());

        ::gc();
    }

    #[test]
    fn simple2() {
        let stack = Treiber::new();
        assert!(stack.top().is_none());

        for _ in 0..16 {
            stack.push(1);
            stack.push(200);
            stack.push(44);

            assert_eq!(*stack.top().unwrap(), 44);
            assert_eq!(*stack.pop().unwrap(), 44);
            assert_eq!(*stack.top().unwrap(), 200);
            assert_eq!(*stack.pop().unwrap(), 200);
            assert_eq!(*stack.top().unwrap(), 44);
            stack.push(20000);

            assert_eq!(*stack.top().unwrap(), 20000);
            assert_eq!(*stack.pop().unwrap(), 20000);
            assert_eq!(*stack.top().unwrap(), 1);
            assert_eq!(*stack.pop().unwrap(), 1);

            assert!(stack.top().is_none());

            assert!(stack.pop().is_none());
            assert!(stack.pop().is_none());
            assert!(stack.pop().is_none());
            assert!(stack.pop().is_none());
        }

        ::gc();
    }

    #[test]
    fn simple3() {
        let stack = Treiber::new();
        assert!(stack.top().is_none());

        for i in 0..10000 {
            stack.push(i);
        }

        for i in (0..10000).rev() {
            assert_eq!(*stack.top().unwrap(), i);
            assert_eq!(*stack.pop().unwrap(), i);
        }

        for i in 0..10000 {
            stack.push(i);
        }

        for i in (0..10000).rev() {
            assert_eq!(*stack.top().unwrap(), i);
            assert_eq!(*stack.pop().unwrap(), i);
        }

        assert!(stack.top().is_none());
        assert!(stack.pop().is_none());
        assert!(stack.pop().is_none());
        assert!(stack.pop().is_none());
        assert!(stack.pop().is_none());
    }

    #[test]
    fn push_pop() {
        let stack = Arc::new(Treiber::new());
        let mut j = Vec::new();
        for _ in 0..16 {
            let s = stack.clone();
            j.push(thread::spawn(move || {
                for _ in 0..10_000_000 {
                    s.push(23);
                    assert_eq!(*s.top().unwrap(), 23);
                    assert_eq!(*s.pop().unwrap(), 23);
                }
            }));
        }

        for i in j {
            i.join().unwrap();
        }
    }

    #[test]
    fn increment() {
        let stack = Arc::new(Treiber::<u64>::new());
        stack.push(0);
        let mut j = Vec::new();

        // 16 times, we add the numbers from 0 to 1000 to the only element in the stack.
        for _ in 0..16 {
            let s = stack.clone();
            j.push(thread::spawn(move || {
                for n in 0..1001 {
                    loop {
                        if let Some(x) = s.pop() {
                            s.push(*x + n);
                            break;
                        }
                    }
                }
            }));
        }

        for i in j {
            i.join().unwrap();
        }

        assert_eq!(*stack.pop().unwrap(), 16 * 1000 * 1001 / 2);
    }

    #[test]
    fn sum() {
        let stack = Arc::new(Treiber::<i64>::new());
        let mut j = Vec::new();

        for _ in 0..1000 {
            stack.push(10);
        }

        // We preserve the sum of the stack's elements.
        for _ in 0..16 {
            let s = stack.clone();
            j.push(thread::spawn(move || {
                for _ in 0..100000 {
                    loop {
                        if let Some(a) = s.pop() {
                            loop {
                                if let Some(b) = s.pop() {
                                    s.push(*a + 1);
                                    s.push(*b - 1);

                                    break;
                                }
                            }

                            break;
                        }
                    }
                }
            }));
        }

        for i in j {
            i.join().unwrap();
        }

        let mut sum = 0;
        while let Some(x) = stack.pop() {
            sum += *x;
        }
        assert_eq!(sum, 10000);
    }

    #[test]
    fn drop() {
        let drops = Arc::new(AtomicUsize::default());
        let stack = Arc::new(Treiber::new());

        let d = Dropper {
            d: drops.clone(),
        };

        let mut j = Vec::new();
        for _ in 0..16 {
            let d = d.clone();
            let stack = stack.clone();

            j.push(thread::spawn(move || {
                for _ in 0..20 {
                    stack.push(d.clone());
                }

                stack.pop();
                stack.pop();
            }))
        }

        for i in j {
            i.join().unwrap();
        }

        ::gc();
        // The 16 are for the `d` variable in the loop above.
        assert_eq!(drops.load(atomic::Ordering::Relaxed), 32 + 16);

        // Drop the last arc.
        let _ = stack;
        ::gc();

        assert_eq!(drops.load(atomic::Ordering::Relaxed), 200 + 16);
    }
}
<|endoftext|>


<commit_before>use std::{iter::once, ops::RangeInclusive};

use syntax::{
    algo::replace_children,
    ast::{
        self,
        edit::{AstNodeEdit, IndentLevel},
        make,
    },
    AstNode,
    SyntaxKind::{FN, LOOP_EXPR, L_CURLY, R_CURLY, WHILE_EXPR, WHITESPACE},
    SyntaxNode,
};

use crate::{
    assist_context::{AssistContext, Assists},
    utils::invert_boolean_expression,
    AssistId, AssistKind,
};

// Assist: convert_to_guarded_return
//
// Replace a large conditional with a guarded return.
//
// ```
// fn main() {
//     $0if cond {
//         foo();
//         bar();
//     }
// }
// ```
// ->
// ```
// fn main() {
//     if !cond {
//         return;
//     }
//     foo();
//     bar();
// }
// ```
pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
    let if_expr: ast::IfExpr = ctx.find_node_at_offset()?;
    if if_expr.else_branch().is_some() {
        return None;
    }

    let cond = if_expr.condition()?;

    // Check if there is an IfLet that we can handle.
    let if_let_pat = match cond.pat() {
        None => None, // No IfLet, supported.
        Some(ast::Pat::TupleStructPat(pat)) if pat.fields().count() == 1 => {
            let path = pat.path()?;
            match path.qualifier() {
                None => {
                    let bound_ident = pat.fields().next().unwrap();
                    if ast::IdentPat::can_cast(bound_ident.syntax().kind()) {
                        Some((path, bound_ident))
                    } else {
                        return None;
                    }
                }
                Some(_) => return None,
            }
        }
        Some(_) => return None, // Unsupported IfLet.
    };

    let cond_expr = cond.expr()?;
    let then_block = if_expr.then_branch()?;

    let parent_block = if_expr.syntax().parent()?.ancestors().find_map(ast::BlockExpr::cast)?;

    if parent_block.tail_expr()? != if_expr.clone().into() {
        return None;
    }

    // check for early return and continue
    let first_in_then_block = then_block.syntax().first_child()?;
    if ast::ReturnExpr::can_cast(first_in_then_block.kind())
        || ast::ContinueExpr::can_cast(first_in_then_block.kind())
        || first_in_then_block
            .children()
            .any(|x| ast::ReturnExpr::can_cast(x.kind()) || ast::ContinueExpr::can_cast(x.kind()))
    {
        return None;
    }

    let parent_container = parent_block.syntax().parent()?;

    let early_expression: ast::Expr = match parent_container.kind() {
        WHILE_EXPR | LOOP_EXPR => make::expr_continue(),
        FN => make::expr_return(None),
        _ => return None,
    };

    if then_block.syntax().first_child_or_token().map(|t| t.kind() == L_CURLY).is_none() {
        return None;
    }

    then_block.syntax().last_child_or_token().filter(|t| t.kind() == R_CURLY)?;

    let target = if_expr.syntax().text_range();
    acc.add(
        AssistId("convert_to_guarded_return", AssistKind::RefactorRewrite),
        "Convert to guarded return",
        target,
        |edit| {
            let if_indent_level = IndentLevel::from_node(if_expr.syntax());
            let new_block = match if_let_pat {
                None => {
                    // If.
                    let new_expr = {
                        let then_branch =
                            make::block_expr(once(make::expr_stmt(early_expression).into()), None);
                        let cond = invert_boolean_expression(cond_expr);
                        make::expr_if(make::condition(cond, None), then_branch, None)
                            .indent(if_indent_level)
                    };
                    replace(new_expr.syntax(), &then_block, &parent_block, &if_expr)
                }
                Some((path, bound_ident)) => {
                    // If-let.
                    let match_expr = {
                        let happy_arm = {
                            let pat = make::tuple_struct_pat(
                                path,
                                once(make::ext::simple_ident_pat(make::name("it")).into()),
                            );
                            let expr = {
                                let path = make::ext::ident_path("it");
                                make::expr_path(path)
                            };
                            make::match_arm(once(pat.into()), None, expr)
                        };

                        let sad_arm = make::match_arm(
                            // FIXME: would be cool to use `None` or `Err(_)` if appropriate
                            once(make::wildcard_pat().into()),
                            None,
                            early_expression,
                        );

                        make::expr_match(cond_expr, make::match_arm_list(vec![happy_arm, sad_arm]))
                    };

                    let let_stmt = make::let_stmt(bound_ident, None, Some(match_expr));
                    let let_stmt = let_stmt.indent(if_indent_level);
                    replace(let_stmt.syntax(), &then_block, &parent_block, &if_expr)
                }
            };
            edit.replace_ast(parent_block, ast::BlockExpr::cast(new_block).unwrap());

            fn replace(
                new_expr: &SyntaxNode,
                then_block: &ast::BlockExpr,
                parent_block: &ast::BlockExpr,
                if_expr: &ast::IfExpr,
            ) -> SyntaxNode {
                let then_block_items = then_block.dedent(IndentLevel(1));
                let end_of_then = then_block_items.syntax().last_child_or_token().unwrap();
                let end_of_then =
                    if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) {
                        end_of_then.prev_sibling_or_token().unwrap()
                    } else {
                        end_of_then
                    };
                let mut then_statements = new_expr.children_with_tokens().chain(
                    then_block_items
                        .syntax()
                        .children_with_tokens()
                        .skip(1)
                        .take_while(|i| *i != end_of_then),
                );
                replace_children(
                    parent_block.syntax(),
                    RangeInclusive::new(
                        if_expr.clone().syntax().clone().into(),
                        if_expr.syntax().clone().into(),
                    ),
                    &mut then_statements,
                )
            }
        },
    )
}

#[cfg(test)]
mod tests {
    use crate::tests::{check_assist, check_assist_not_applicable};

    use super::*;

    #[test]
    fn convert_inside_fn() {
        check_assist(
            convert_to_guarded_return,
            r#"
fn main() {
    bar();
    if$0 true {
        foo();

        // comment
        bar();
    }
}
"#,
            r#"
fn main() {
    bar();
    if false {
        return;
    }
    foo();

    // comment
    bar();
}
"#,
        );
    }

    #[test]
    fn convert_let_inside_fn() {
        check_assist(
            convert_to_guarded_return,
            r#"
fn main(n: Option<String>) {
    bar();
    if$0 let Some(n) = n {
        foo(n);

        // comment
        bar();
    }
}
"#,
            r#"
fn main(n: Option<String>) {
    bar();
    let n = match n {
        Some(it) => it,
        _ => return,
    };
    foo(n);

    // comment
    bar();
}
"#,
        );
    }

    #[test]
    fn convert_if_let_result() {
        check_assist(
            convert_to_guarded_return,
            r#"
fn main() {
    if$0 let Ok(x) = Err(92) {
        foo(x);
    }
}
"#,
            r#"
fn main() {
    let x = match Err(92) {
        Ok(it) => it,
        _ => return,
    };
    foo(x);
}
"#,
        );
    }

    #[test]
    fn convert_let_ok_inside_fn() {
        check_assist(
            convert_to_guarded_return,
            r#"
fn main(n: Option<String>) {
    bar();
    if$0 let Some(n) = n {
        foo(n);

        // comment
        bar();
    }
}
"#,
            r#"
fn main(n: Option<String>) {
    bar();
    let n = match n {
        Some(it) => it,
        _ => return,
    };
    foo(n);

    // comment
    bar();
}
"#,
        );
    }

    #[test]
    fn convert_let_mut_ok_inside_fn() {
        check_assist(
            convert_to_guarded_return,
            r#"
fn main(n: Option<String>) {
    bar();
    if$0 let Some(mut n) = n {
        foo(n);

        // comment
        bar();
    }
}
"#,
            r#"
fn main(n: Option<String>) {
    bar();
    let mut n = match n {
        Some(it) => it,
        _ => return,
    };
    foo(n);

    // comment
    bar();
}
"#,
        );
    }

    #[test]
    fn convert_let_ref_ok_inside_fn() {
        check_assist(
            convert_to_guarded_return,
            r#"
fn main(n: Option<&str>) {
    bar();
    if$0 let Some(ref n) = n {
        foo(n);

        // comment
        bar();
    }
}
"#,
            r#"
fn main(n: Option<&str>) {
    bar();
    let ref n = match n {
        Some(it) => it,
        _ => return,
    };
    foo(n);

    // comment
    bar();
}
"#,
        );
    }

    #[test]
    fn convert_inside_while() {
        check_assist(
            convert_to_guarded_return,
            r#"
fn main() {
    while true {
        if$0 true {
            foo();
            bar();
        }
    }
}
"#,
            r#"
fn main() {
    while true {
        if false {
            continue;
        }
        foo();
        bar();
    }
}
"#,
        );
    }

    #[test]
    fn convert_let_inside_while() {
        check_assist(
            convert_to_guarded_return,
            r#"
fn main() {
    while true {
        if$0 let Some(n) = n {
            foo(n);
            bar();
        }
    }
}
"#,
            r#"
fn main() {
    while true {
        let n = match n {
            Some(it) => it,
            _ => continue,
        };
        foo(n);
        bar();
    }
}
"#,
        );
    }

    #[test]
    fn convert_inside_loop() {
        check_assist(
            convert_to_guarded_return,
            r#"
fn main() {
    loop {
        if$0 true {
            foo();
            bar();
        }
    }
}
"#,
            r#"
fn main() {
    loop {
        if false {
            continue;
        }
        foo();
        bar();
    }
}
"#,
        );
    }

    #[test]
    fn convert_let_inside_loop() {
        check_assist(
            convert_to_guarded_return,
            r#"
fn main() {
    loop {
        if$0 let Some(n) = n {
            foo(n);
            bar();
        }
    }
}
"#,
            r#"
fn main() {
    loop {
        let n = match n {
            Some(it) => it,
            _ => continue,
        };
        foo(n);
        bar();
    }
}
"#,
        );
    }

    #[test]
    fn ignore_already_converted_if() {
        check_assist_not_applicable(
            convert_to_guarded_return,
            r#"
fn main() {
    if$0 true {
        return;
    }
}
"#,
        );
    }

    #[test]
    fn ignore_already_converted_loop() {
        check_assist_not_applicable(
            convert_to_guarded_return,
            r#"
fn main() {
    loop {
        if$0 true {
            continue;
        }
    }
}
"#,
        );
    }

    #[test]
    fn ignore_return() {
        check_assist_not_applicable(
            convert_to_guarded_return,
            r#"
fn main() {
    if$0 true {
        return
    }
}
"#,
        );
    }

    #[test]
    fn ignore_else_branch() {
        check_assist_not_applicable(
            convert_to_guarded_return,
            r#"
fn main() {
    if$0 true {
        foo();
    } else {
        bar()
    }
}
"#,
        );
    }

    #[test]
    fn ignore_statements_aftert_if() {
        check_assist_not_applicable(
            convert_to_guarded_return,
            r#"
fn main() {
    if$0 true {
        foo();
    }
    bar();
}
"#,
        );
    }

    #[test]
    fn ignore_statements_inside_if() {
        check_assist_not_applicable(
            convert_to_guarded_return,
            r#"
fn main() {
    if false {
        if$0 true {
            foo();
        }
    }
}
"#,
        );
    }
}
<commit_msg>internal: remove old editing API<commit_after>use std::iter::once;

use syntax::{
    ast::{
        self,
        edit::{AstNodeEdit, IndentLevel},
        make,
    },
    ted, AstNode,
    SyntaxKind::{FN, LOOP_EXPR, WHILE_EXPR, WHITESPACE},
    T,
};

use crate::{
    assist_context::{AssistContext, Assists},
    utils::invert_boolean_expression,
    AssistId, AssistKind,
};

// Assist: convert_to_guarded_return
//
// Replace a large conditional with a guarded return.
//
// ```
// fn main() {
//     $0if cond {
//         foo();
//         bar();
//     }
// }
// ```
// ->
// ```
// fn main() {
//     if !cond {
//         return;
//     }
//     foo();
//     bar();
// }
// ```
pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
    let if_expr: ast::IfExpr = ctx.find_node_at_offset()?;
    if if_expr.else_branch().is_some() {
        return None;
    }

    let cond = if_expr.condition()?;

    // Check if there is an IfLet that we can handle.
    let if_let_pat = match cond.pat() {
        None => None, // No IfLet, supported.
        Some(ast::Pat::TupleStructPat(pat)) if pat.fields().count() == 1 => {
            let path = pat.path()?;
            if path.qualifier().is_some() {
                return None;
            }

            let bound_ident = pat.fields().next().unwrap();
            if !ast::IdentPat::can_cast(bound_ident.syntax().kind()) {
                return None;
            }

            Some((path, bound_ident))
        }
        Some(_) => return None, // Unsupported IfLet.
    };

    let cond_expr = cond.expr()?;
    let then_block = if_expr.then_branch()?;

    let parent_block = if_expr.syntax().parent()?.ancestors().find_map(ast::BlockExpr::cast)?;

    if parent_block.tail_expr()? != if_expr.clone().into() {
        return None;
    }

    // check for early return and continue
    let first_in_then_block = then_block.syntax().first_child()?;
    if ast::ReturnExpr::can_cast(first_in_then_block.kind())
        || ast::ContinueExpr::can_cast(first_in_then_block.kind())
        || first_in_then_block
            .children()
            .any(|x| ast::ReturnExpr::can_cast(x.kind()) || ast::ContinueExpr::can_cast(x.kind()))
    {
        return None;
    }

    let parent_container = parent_block.syntax().parent()?;

    let early_expression: ast::Expr = match parent_container.kind() {
        WHILE_EXPR | LOOP_EXPR => make::expr_continue(),
        FN => make::expr_return(None),
        _ => return None,
    };

    if then_block.syntax().first_child_or_token().map(|t| t.kind() == T!['{']).is_none() {
        return None;
    }

    then_block.syntax().last_child_or_token().filter(|t| t.kind() == T!['}'])?;

    let target = if_expr.syntax().text_range();
    acc.add(
        AssistId("convert_to_guarded_return", AssistKind::RefactorRewrite),
        "Convert to guarded return",
        target,
        |edit| {
            let if_expr = edit.make_mut(if_expr);
            let if_indent_level = IndentLevel::from_node(if_expr.syntax());
            let replacement = match if_let_pat {
                None => {
                    // If.
                    let new_expr = {
                        let then_branch =
                            make::block_expr(once(make::expr_stmt(early_expression).into()), None);
                        let cond = invert_boolean_expression(cond_expr);
                        make::expr_if(make::condition(cond, None), then_branch, None)
                            .indent(if_indent_level)
                    };
                    new_expr.syntax().clone_for_update()
                }
                Some((path, bound_ident)) => {
                    // If-let.
                    let match_expr = {
                        let happy_arm = {
                            let pat = make::tuple_struct_pat(
                                path,
                                once(make::ext::simple_ident_pat(make::name("it")).into()),
                            );
                            let expr = {
                                let path = make::ext::ident_path("it");
                                make::expr_path(path)
                            };
                            make::match_arm(once(pat.into()), None, expr)
                        };

                        let sad_arm = make::match_arm(
                            // FIXME: would be cool to use `None` or `Err(_)` if appropriate
                            once(make::wildcard_pat().into()),
                            None,
                            early_expression,
                        );

                        make::expr_match(cond_expr, make::match_arm_list(vec![happy_arm, sad_arm]))
                    };

                    let let_stmt = make::let_stmt(bound_ident, None, Some(match_expr));
                    let let_stmt = let_stmt.indent(if_indent_level);
                    let_stmt.syntax().clone_for_update()
                }
            };

            let then_block_items = then_block.dedent(IndentLevel(1)).clone_for_update();

            let end_of_then = then_block_items.syntax().last_child_or_token().unwrap();
            let end_of_then =
                if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) {
                    end_of_then.prev_sibling_or_token().unwrap()
                } else {
                    end_of_then
                };

            let then_statements = replacement
                .children_with_tokens()
                .chain(
                    then_block_items
                        .syntax()
                        .children_with_tokens()
                        .skip(1)
                        .take_while(|i| *i != end_of_then),
                )
                .collect();

            ted::replace_with_many(if_expr.syntax(), then_statements)
        },
    )
}

#[cfg(test)]
mod tests {
    use crate::tests::{check_assist, check_assist_not_applicable};

    use super::*;

    #[test]
    fn convert_inside_fn() {
        check_assist(
            convert_to_guarded_return,
            r#"
fn main() {
    bar();
    if$0 true {
        foo();

        // comment
        bar();
    }
}
"#,
            r#"
fn main() {
    bar();
    if false {
        return;
    }
    foo();

    // comment
    bar();
}
"#,
        );
    }

    #[test]
    fn convert_let_inside_fn() {
        check_assist(
            convert_to_guarded_return,
            r#"
fn main(n: Option<String>) {
    bar();
    if$0 let Some(n) = n {
        foo(n);

        // comment
        bar();
    }
}
"#,
            r#"
fn main(n: Option<String>) {
    bar();
    let n = match n {
        Some(it) => it,
        _ => return,
    };
    foo(n);

    // comment
    bar();
}
"#,
        );
    }

    #[test]
    fn convert_if_let_result() {
        check_assist(
            convert_to_guarded_return,
            r#"
fn main() {
    if$0 let Ok(x) = Err(92) {
        foo(x);
    }
}
"#,
            r#"
fn main() {
    let x = match Err(92) {
        Ok(it) => it,
        _ => return,
    };
    foo(x);
}
"#,
        );
    }

    #[test]
    fn convert_let_ok_inside_fn() {
        check_assist(
            convert_to_guarded_return,
            r#"
fn main(n: Option<String>) {
    bar();
    if$0 let Some(n) = n {
        foo(n);

        // comment
        bar();
    }
}
"#,
            r#"
fn main(n: Option<String>) {
    bar();
    let n = match n {
        Some(it) => it,
        _ => return,
    };
    foo(n);

    // comment
    bar();
}
"#,
        );
    }

    #[test]
    fn convert_let_mut_ok_inside_fn() {
        check_assist(
            convert_to_guarded_return,
            r#"
fn main(n: Option<String>) {
    bar();
    if$0 let Some(mut n) = n {
        foo(n);

        // comment
        bar();
    }
}
"#,
            r#"
fn main(n: Option<String>) {
    bar();
    let mut n = match n {
        Some(it) => it,
        _ => return,
    };
    foo(n);

    // comment
    bar();
}
"#,
        );
    }

    #[test]
    fn convert_let_ref_ok_inside_fn() {
        check_assist(
            convert_to_guarded_return,
            r#"
fn main(n: Option<&str>) {
    bar();
    if$0 let Some(ref n) = n {
        foo(n);

        // comment
        bar();
    }
}
"#,
            r#"
fn main(n: Option<&str>) {
    bar();
    let ref n = match n {
        Some(it) => it,
        _ => return,
    };
    foo(n);

    // comment
    bar();
}
"#,
        );
    }

    #[test]
    fn convert_inside_while() {
        check_assist(
            convert_to_guarded_return,
            r#"
fn main() {
    while true {
        if$0 true {
            foo();
            bar();
        }
    }
}
"#,
            r#"
fn main() {
    while true {
        if false {
            continue;
        }
        foo();
        bar();
    }
}
"#,
        );
    }

    #[test]
    fn convert_let_inside_while() {
        check_assist(
            convert_to_guarded_return,
            r#"
fn main() {
    while true {
        if$0 let Some(n) = n {
            foo(n);
            bar();
        }
    }
}
"#,
            r#"
fn main() {
    while true {
        let n = match n {
            Some(it) => it,
            _ => continue,
        };
        foo(n);
        bar();
    }
}
"#,
        );
    }

    #[test]
    fn convert_inside_loop() {
        check_assist(
            convert_to_guarded_return,
            r#"
fn main() {
    loop {
        if$0 true {
            foo();
            bar();
        }
    }
}
"#,
            r#"
fn main() {
    loop {
        if false {
            continue;
        }
        foo();
        bar();
    }
}
"#,
        );
    }

    #[test]
    fn convert_let_inside_loop() {
        check_assist(
            convert_to_guarded_return,
            r#"
fn main() {
    loop {
        if$0 let Some(n) = n {
            foo(n);
            bar();
        }
    }
}
"#,
            r#"
fn main() {
    loop {
        let n = match n {
            Some(it) => it,
            _ => continue,
        };
        foo(n);
        bar();
    }
}
"#,
        );
    }

    #[test]
    fn ignore_already_converted_if() {
        check_assist_not_applicable(
            convert_to_guarded_return,
            r#"
fn main() {
    if$0 true {
        return;
    }
}
"#,
        );
    }

    #[test]
    fn ignore_already_converted_loop() {
        check_assist_not_applicable(
            convert_to_guarded_return,
            r#"
fn main() {
    loop {
        if$0 true {
            continue;
        }
    }
}
"#,
        );
    }

    #[test]
    fn ignore_return() {
        check_assist_not_applicable(
            convert_to_guarded_return,
            r#"
fn main() {
    if$0 true {
        return
    }
}
"#,
        );
    }

    #[test]
    fn ignore_else_branch() {
        check_assist_not_applicable(
            convert_to_guarded_return,
            r#"
fn main() {
    if$0 true {
        foo();
    } else {
        bar()
    }
}
"#,
        );
    }

    #[test]
    fn ignore_statements_aftert_if() {
        check_assist_not_applicable(
            convert_to_guarded_return,
            r#"
fn main() {
    if$0 true {
        foo();
    }
    bar();
}
"#,
        );
    }

    #[test]
    fn ignore_statements_inside_if() {
        check_assist_not_applicable(
            convert_to_guarded_return,
            r#"
fn main() {
    if false {
        if$0 true {
            foo();
        }
    }
}
"#,
        );
    }
}
<|endoftext|>