<commit_before>// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! # Rust implementation of the `uenum.h` C API header for ICU.

use {
    rust_icu_common as common, rust_icu_sys as sys,
    rust_icu_sys::*,
    std::{convert::TryFrom, ffi, str},
};

/// Rust wrapper for the UEnumeration iterator.
///
/// Implements `UEnumeration`
#[derive(Debug)]
pub struct Enumeration {
    // The raw underlying character array, in case the underlying char array is
    // owned by this enumeration.
    raw: Option<common::CStringVec>,

    // Internal low-level representation of the enumeration.  The internal
    // representation relies on `raw` and `len` above and must live at most as
    // long.
    rep: *mut sys::UEnumeration,
}

impl Enumeration {
    /// Internal representation, for ICU4C methods that require it.
    pub fn repr(&mut self) -> *mut sys::UEnumeration {
        self.rep
    }

    /// Creates an empty `Enumeration`.
    pub fn empty() -> Self {
        Enumeration::try_from(&vec![][..]).unwrap()
    }
}

impl Default for Enumeration {
    fn default() -> Self {
        Self::empty()
    }
}

/// Creates an enumeration iterator from a vector of UTF-8 strings.
impl TryFrom<&[&str]> for Enumeration {
    type Error = common::Error;

    /// Constructs an enumeration from a string slice.
    ///
    /// Implements `uenum_openCharStringsEnumeration`
    fn try_from(v: &[&str]) -> Result<Enumeration, common::Error> {
        let raw = common::CStringVec::new(v)?;
        let mut status = common::Error::OK_CODE;
        let rep: *mut sys::UEnumeration = unsafe {
            versioned_function!(uenum_openCharStringsEnumeration)(
                raw.as_c_array(),
                raw.len() as i32,
                &mut status,
            )
        };
        common::Error::ok_or_warning(status)?;
        // rep should not be null without an error set, but:
        // https://unicode-org.atlassian.net/browse/ICU-20918
        assert!(!rep.is_null());
        Ok(Enumeration {
            rep: rep,
            raw: Some(raw),
        })
    }
}

impl Drop for Enumeration {
    /// Drops the Enumeration, deallocating its internal representation hopefully correctly.
    ///
    /// Implements `uenum_close`
    fn drop(&mut self) {
        unsafe { versioned_function!(uenum_close)(self.rep) };
    }
}

impl Iterator for Enumeration {
    type Item = Result<String, common::Error>;

    /// Yields the next element stored in the enumeration.
    ///
    /// Implements `uenum_next`
    fn next(&mut self) -> Option<Self::Item> {
        let mut len: i32 = 0;
        let mut status = common::Error::OK_CODE;
        // Requires that self.rep is a valid pointer to a sys::UEnumeration.
        assert!(!self.rep.is_null());
        let raw = unsafe { versioned_function!(uenum_next)(self.rep, &mut len, &mut status) };
        if raw.is_null() {
            // No more elements to iterate over.
            return None;
        }
        let result = common::Error::ok_or_warning(status);
        match result {
            Ok(()) => {
                assert!(!raw.is_null());
                // Requires that raw is a valid pointer to a C string.
                let cstring = unsafe { ffi::CStr::from_ptr(raw) }; // Borrowing
                Some(Ok(cstring
                    .to_str()
                    .expect("could not convert to string")
                    .to_string()))
            }
            Err(e) => Some(Err(e)),
        }
    }
}

impl Enumeration {
    /// Constructs an [Enumeration] from a raw pointer.
    ///
    /// **DO NOT USE THIS FUNCTION UNLESS THERE IS NO OTHER CHOICE!**
    ///
    /// We tried to keep this function hidden to avoid the
    /// need to have a such a powerful unsafe function in the
    /// public API.  It worked up to a point for free functions.
    ///
    /// It no longer works on high-level methods that return
    /// enumerations, since they we'd need to depend on them to
    /// create an [Enumeration].
    pub unsafe fn from_raw_parts(
        raw: Option<common::CStringVec>,
        rep: *mut sys::UEnumeration,
    ) -> Enumeration {
        Enumeration { raw, rep }
    }
}

#[doc(hidden)]
/// Implements `ucal_openCountryTimeZones`.
// This should be in the `ucal` crate, but not possible because of the raw enum initialization.
// Tested in `ucal`.
pub fn ucal_open_country_time_zones(country: &str) -> Result<Enumeration, common::Error> {
    let mut status = common::Error::OK_CODE;
    let asciiz_country = ffi::CString::new(country)?;
    // Requires that the asciiz country be a pointer to a valid C string.
    let raw_enum = unsafe {
        assert!(common::Error::is_ok(status));
        versioned_function!(ucal_openCountryTimeZones)(asciiz_country.as_ptr(), &mut status)
    };
    common::Error::ok_or_warning(status)?;
    Ok(Enumeration {
        raw: None,
        rep: raw_enum,
    })
}

#[doc(hidden)]
/// Implements `ucal_openTimeZoneIDEnumeration`
// This should be in the `ucal` crate, but not possible because of the raw enum initialization.
// Tested in `ucal`.
pub fn ucal_open_time_zone_id_enumeration(
    zone_type: sys::USystemTimeZoneType,
    region: Option<&str>,
    raw_offset: Option<i32>,
) -> Result<Enumeration, common::Error> {
    let mut status = common::Error::OK_CODE;
    let asciiz_region = match region {
        None => None,
        Some(region) => Some(ffi::CString::new(region)?),
    };
    let mut repr_raw_offset: i32 = raw_offset.unwrap_or_default();

    // asciiz_region should be a valid asciiz pointer. raw_offset is an encoding
    // of an optional value by a C pointer.
    let raw_enum = unsafe {
        assert!(common::Error::is_ok(status));
        versioned_function!(ucal_openTimeZoneIDEnumeration)(
            zone_type,
            // Note that for the string pointer to remain valid, we must borrow the CString from
            // asciiz_region, not move the CString out.
            match &asciiz_region {
                Some(asciiz_region) => asciiz_region.as_ptr(),
                None => std::ptr::null(),
            },
            match raw_offset {
                Some(_) => &mut repr_raw_offset,
                None => std::ptr::null_mut(),
            },
            &mut status,
        )
    };
    common::Error::ok_or_warning(status)?;
    Ok(Enumeration {
        raw: None,
        rep: raw_enum,
    })
}

#[doc(hidden)]
/// Opens a list of available time zones.
///
/// Implements `ucal_openTimeZones`
// This should be in the `ucal` crate, but not possible because of the raw enum initialization.
// Tested in `ucal`.
pub fn open_time_zones() -> Result<Enumeration, common::Error> {
    let mut status = common::Error::OK_CODE;
    let raw_enum = unsafe {
        assert!(common::Error::is_ok(status));
        versioned_function!(ucal_openTimeZones)(&mut status)
    };
    common::Error::ok_or_warning(status)?;
    Ok(Enumeration {
        raw: None,
        rep: raw_enum,
    })
}

#[doc(hidden)]
/// Implements `uloc_openKeywords`.
// This should be in the `uloc` crate, but this is not possible because of the raw enum
// initialization. Tested in `uloc`.
pub fn uloc_open_keywords(locale: &str) -> Result<Enumeration, common::Error> {
    let mut status = common::Error::OK_CODE;
    let asciiz_locale = ffi::CString::new(locale)?;
    let raw_enum = unsafe {
        assert!(common::Error::is_ok(status));
        versioned_function!(uloc_openKeywords)(asciiz_locale.as_ptr(), &mut status)
    };
    common::Error::ok_or_warning(status)?;
    // "No error but null" means that there are no keywords
    if raw_enum.is_null() {
        Ok(Enumeration::empty())
    } else {
        Ok(Enumeration {
            raw: None,
            rep: raw_enum,
        })
    }
}

#[cfg(test)]
mod tests {
    use {super::*, std::convert::TryFrom};

    #[test]
    fn iter() {
        let e = Enumeration::try_from(&vec!["hello", "world", "💖"][..]).expect("enumeration?");
        let mut count = 0;
        let mut results = vec![];
        for result in e {
            let elem = result.expect("no error");
            count = count + 1;
            results.push(elem);
        }
        assert_eq!(count, 3, "results: {:?}", results);
        assert_eq!(
            results,
            vec!["hello", "world", "💖"],
            "results: {:?}",
            results
        );
    }

    #[test]
    fn error() {
        // A mutilated sparkle heart from https://doc.rust-lang.org/std/str/fn.from_utf8_unchecked.html
        let destroyed_sparkle_heart = vec![0, 159, 164, 150];
        let invalid_utf8 = unsafe { str::from_utf8_unchecked(&destroyed_sparkle_heart) };
        let e = Enumeration::try_from(&vec!["hello", "world", "💖", invalid_utf8][..]);
        assert!(e.is_err(), "was: {:?}", e);
    }
}
<commit_msg>Fixes up the documentation for Enumeration::from_raw_parts<commit_after>// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! # Rust implementation of the `uenum.h` C API header for ICU.

use {
    rust_icu_common as common, rust_icu_sys as sys,
    rust_icu_sys::*,
    std::{convert::TryFrom, ffi, str},
};

/// Rust wrapper for the UEnumeration iterator.
///
/// Implements `UEnumeration`
#[derive(Debug)]
pub struct Enumeration {
    // The raw underlying character array, in case the underlying char array is
    // owned by this enumeration.
    raw: Option<common::CStringVec>,

    // Internal low-level representation of the enumeration.  The internal
    // representation relies on `raw` and `len` above and must live at most as
    // long.
    rep: *mut sys::UEnumeration,
}

impl Enumeration {
    /// Internal representation, for ICU4C methods that require it.
    pub fn repr(&mut self) -> *mut sys::UEnumeration {
        self.rep
    }

    /// Creates an empty `Enumeration`.
    pub fn empty() -> Self {
        Enumeration::try_from(&vec![][..]).unwrap()
    }
}

impl Default for Enumeration {
    fn default() -> Self {
        Self::empty()
    }
}

/// Creates an enumeration iterator from a vector of UTF-8 strings.
impl TryFrom<&[&str]> for Enumeration {
    type Error = common::Error;

    /// Constructs an enumeration from a string slice.
    ///
    /// Implements `uenum_openCharStringsEnumeration`
    fn try_from(v: &[&str]) -> Result<Enumeration, common::Error> {
        let raw = common::CStringVec::new(v)?;
        let mut status = common::Error::OK_CODE;
        let rep: *mut sys::UEnumeration = unsafe {
            versioned_function!(uenum_openCharStringsEnumeration)(
                raw.as_c_array(),
                raw.len() as i32,
                &mut status,
            )
        };
        common::Error::ok_or_warning(status)?;
        // rep should not be null without an error set, but:
        // https://unicode-org.atlassian.net/browse/ICU-20918
        assert!(!rep.is_null());
        Ok(Enumeration {
            rep: rep,
            raw: Some(raw),
        })
    }
}

impl Drop for Enumeration {
    /// Drops the Enumeration, deallocating its internal representation hopefully correctly.
    ///
    /// Implements `uenum_close`
    fn drop(&mut self) {
        unsafe { versioned_function!(uenum_close)(self.rep) };
    }
}

impl Iterator for Enumeration {
    type Item = Result<String, common::Error>;

    /// Yields the next element stored in the enumeration.
    ///
    /// Implements `uenum_next`
    fn next(&mut self) -> Option<Self::Item> {
        let mut len: i32 = 0;
        let mut status = common::Error::OK_CODE;
        // Requires that self.rep is a valid pointer to a sys::UEnumeration.
        assert!(!self.rep.is_null());
        let raw = unsafe { versioned_function!(uenum_next)(self.rep, &mut len, &mut status) };
        if raw.is_null() {
            // No more elements to iterate over.
            return None;
        }
        let result = common::Error::ok_or_warning(status);
        match result {
            Ok(()) => {
                assert!(!raw.is_null());
                // Requires that raw is a valid pointer to a C string.
                let cstring = unsafe { ffi::CStr::from_ptr(raw) }; // Borrowing
                Some(Ok(cstring
                    .to_str()
                    .expect("could not convert to string")
                    .to_string()))
            }
            Err(e) => Some(Err(e)),
        }
    }
}

impl Enumeration {
    /// Constructs an [Enumeration] from a raw pointer.
    ///
    /// **DO NOT USE THIS FUNCTION UNLESS THERE IS NO OTHER CHOICE!**
    ///
    /// We tried to keep this function hidden to avoid the
    /// need to have a such a powerful unsafe function in the
    /// public API.  It worked up to a point for free functions.
    ///
    /// It no longer works on high-level methods that return
    /// enumerations, since then we'd need to depend on them to
    /// create an [Enumeration].
    #[doc(hidden)]
    pub unsafe fn from_raw_parts(
        raw: Option<common::CStringVec>,
        rep: *mut sys::UEnumeration,
    ) -> Enumeration {
        Enumeration { raw, rep }
    }
}

#[doc(hidden)]
/// Implements `ucal_openCountryTimeZones`.
// This should be in the `ucal` crate, but not possible because of the raw enum initialization.
// Tested in `ucal`.
pub fn ucal_open_country_time_zones(country: &str) -> Result<Enumeration, common::Error> {
    let mut status = common::Error::OK_CODE;
    let asciiz_country = ffi::CString::new(country)?;
    // Requires that the asciiz country be a pointer to a valid C string.
    let raw_enum = unsafe {
        assert!(common::Error::is_ok(status));
        versioned_function!(ucal_openCountryTimeZones)(asciiz_country.as_ptr(), &mut status)
    };
    common::Error::ok_or_warning(status)?;
    Ok(Enumeration {
        raw: None,
        rep: raw_enum,
    })
}

#[doc(hidden)]
/// Implements `ucal_openTimeZoneIDEnumeration`
// This should be in the `ucal` crate, but not possible because of the raw enum initialization.
// Tested in `ucal`.
pub fn ucal_open_time_zone_id_enumeration(
    zone_type: sys::USystemTimeZoneType,
    region: Option<&str>,
    raw_offset: Option<i32>,
) -> Result<Enumeration, common::Error> {
    let mut status = common::Error::OK_CODE;
    let asciiz_region = match region {
        None => None,
        Some(region) => Some(ffi::CString::new(region)?),
    };
    let mut repr_raw_offset: i32 = raw_offset.unwrap_or_default();

    // asciiz_region should be a valid asciiz pointer. raw_offset is an encoding
    // of an optional value by a C pointer.
    let raw_enum = unsafe {
        assert!(common::Error::is_ok(status));
        versioned_function!(ucal_openTimeZoneIDEnumeration)(
            zone_type,
            // Note that for the string pointer to remain valid, we must borrow the CString from
            // asciiz_region, not move the CString out.
            match &asciiz_region {
                Some(asciiz_region) => asciiz_region.as_ptr(),
                None => std::ptr::null(),
            },
            match raw_offset {
                Some(_) => &mut repr_raw_offset,
                None => std::ptr::null_mut(),
            },
            &mut status,
        )
    };
    common::Error::ok_or_warning(status)?;
    Ok(Enumeration {
        raw: None,
        rep: raw_enum,
    })
}

#[doc(hidden)]
/// Opens a list of available time zones.
///
/// Implements `ucal_openTimeZones`
// This should be in the `ucal` crate, but not possible because of the raw enum initialization.
// Tested in `ucal`.
pub fn open_time_zones() -> Result<Enumeration, common::Error> {
    let mut status = common::Error::OK_CODE;
    let raw_enum = unsafe {
        assert!(common::Error::is_ok(status));
        versioned_function!(ucal_openTimeZones)(&mut status)
    };
    common::Error::ok_or_warning(status)?;
    Ok(Enumeration {
        raw: None,
        rep: raw_enum,
    })
}

#[doc(hidden)]
/// Implements `uloc_openKeywords`.
// This should be in the `uloc` crate, but this is not possible because of the raw enum
// initialization. Tested in `uloc`.
pub fn uloc_open_keywords(locale: &str) -> Result<Enumeration, common::Error> {
    let mut status = common::Error::OK_CODE;
    let asciiz_locale = ffi::CString::new(locale)?;
    let raw_enum = unsafe {
        assert!(common::Error::is_ok(status));
        versioned_function!(uloc_openKeywords)(asciiz_locale.as_ptr(), &mut status)
    };
    common::Error::ok_or_warning(status)?;
    // "No error but null" means that there are no keywords
    if raw_enum.is_null() {
        Ok(Enumeration::empty())
    } else {
        Ok(Enumeration {
            raw: None,
            rep: raw_enum,
        })
    }
}

#[cfg(test)]
mod tests {
    use {super::*, std::convert::TryFrom};

    #[test]
    fn iter() {
        let e = Enumeration::try_from(&vec!["hello", "world", "💖"][..]).expect("enumeration?");
        let mut count = 0;
        let mut results = vec![];
        for result in e {
            let elem = result.expect("no error");
            count = count + 1;
            results.push(elem);
        }
        assert_eq!(count, 3, "results: {:?}", results);
        assert_eq!(
            results,
            vec!["hello", "world", "💖"],
            "results: {:?}",
            results
        );
    }

    #[test]
    fn error() {
        // A mutilated sparkle heart from https://doc.rust-lang.org/std/str/fn.from_utf8_unchecked.html
        let destroyed_sparkle_heart = vec![0, 159, 164, 150];
        let invalid_utf8 = unsafe { str::from_utf8_unchecked(&destroyed_sparkle_heart) };
        let e = Enumeration::try_from(&vec!["hello", "world", "💖", invalid_utf8][..]);
        assert!(e.is_err(), "was: {:?}", e);
    }
}
<|endoftext|>


<commit_before>mod wildcard {
    use std::{char, u8};
    use std::iter::FromIterator;
    use char_iter;
    use cripes::pattern::{ByteOrChar, Class, Element, Wildcard};
    use cripes::util::set::{Contains, IsSubsetOf, IsSupersetOf};

    #[test]
    fn test_is_subset_of() {
        let wild = Wildcard::<char>;
        assert!(wild.is_superset_of(&'a'));
        assert!(wild.is_superset_of(&Element::Atom('a')));
        assert!(!wild.is_subset_of(&Element::Atom('a')));
        assert!(wild.is_subset_of(&Wildcard));

        assert!(ByteOrChar::Byte(b'H').is_subset_of(&Wildcard));

        let cls = Class::from_iter("xyz".chars());
        assert!(wild.is_superset_of(&cls));
    }

    #[test]
    fn test_contains() {
        let wildc = Wildcard::<char>;
        for c in char_iter::new(0 as char, char::MAX) {
            assert!(wildc.contains(c));
        }

        let wildbc = Wildcard::<ByteOrChar>;
        for c in char_iter::new(0 as char, char::MAX) {
            assert!(wildbc.contains(c.into()));
        }
        for b in 0...u8::MAX {
            assert!(wildbc.contains(b.into()));
        }
    }
}

mod class {
    use std::char;
    use std::iter::FromIterator;

    use char_iter;
    use cripes::pattern::{Class, Element, Polarity};
    use cripes::util::set::{Contains, IsSubsetOf, IsSupersetOf};

    #[test]
    fn test_contains() {
        let cls = Class::new(Polarity::NORMAL, ['a', 'b', 'c'].into_iter().cloned());
        //assert_eq!(3, cls.len());

        // contains
        assert!(cls.contains('a'));
        assert!(cls.contains('b'));
        assert!(cls.contains('c'));
        for c in char_iter::new(0 as char, char::MAX) {
            if c == 'a' || c == 'b' || c == 'c' {
                assert!(cls.contains(c));
            } else {
                assert!(!cls.contains(c))
            }
        }
    }

    #[test]
    fn test_is_subset_of() {
        let cls1: Class<char> = FromIterator::from_iter(['x', 'y', 'z'].into_iter().cloned());
        assert!(Element::Wildcard.is_superset_of(&cls1));

        let cls2: Class<char> = FromIterator::from_iter(['x', 'y'].into_iter().cloned());
        assert!(cls2.is_subset_of(&cls1));
    }
}
<commit_msg>test/test_pattern: more tests for the pattern types<commit_after>mod sequence {
    use std::iter::FromIterator;
    use cripes::pattern::{Class, Element, Sequence, Union};
    use cripes::util::set::IsSubsetOf;

    #[test]
    fn test_is_subset_of() {
        let unions = (&["abc", "def", "ghi"]).iter().map(|s| Union::<char>::from_iter(s.chars().map(|c| c.into()))).collect::<Vec<_>>();
        let classes = (&["abc", "def", "ghi"]).iter().map(|s| Class::<char>::from_iter(s.chars())).collect::<Vec<_>>();
        let seqs = (&["adg", "beh", "cfi"]).iter().map(|s| Sequence::<char>::from_iter(s.chars().map(|c| c.into()))).collect::<Vec<_>>();
        let useq = Sequence::from_iter(unions.iter().map(|u| Element::Union(u.clone())));
        let cseq = Sequence::from_iter(classes.iter().map(|c| Element::Class(c.clone())));
        assert!(useq.is_subset_of(&cseq));
        assert!(cseq.is_subset_of(&useq));

        for seq in seqs.iter() {
            assert!(seq.is_subset_of(&cseq));
            assert!(seq.is_subset_of(&useq));
        }
    }
}

mod wildcard {
    use std::{char, u8};
    use std::iter::FromIterator;
    use char_iter;
    use cripes::pattern::{ByteOrChar, Class, Element, Wildcard};
    use cripes::util::set::{Contains, IsSubsetOf, IsSupersetOf};

    #[test]
    fn test_is_subset_of() {
        let wild = Wildcard::<char>;
        assert!(wild.is_superset_of(&'a'));
        assert!(wild.is_superset_of(&Element::Atom('a')));
        assert!(!wild.is_subset_of(&Element::Atom('a')));
        assert!(wild.is_subset_of(&Wildcard));

        assert!(ByteOrChar::Byte(b'H').is_subset_of(&Wildcard));

        let cls = Class::from_iter("xyz".chars());
        assert!(wild.is_superset_of(&cls));
    }

    #[test]
    fn test_contains() {
        let wildc = Wildcard::<char>;
        for c in char_iter::new(0 as char, char::MAX) {
            assert!(wildc.contains(c));
        }

        let wildbc = Wildcard::<ByteOrChar>;
        for c in char_iter::new(0 as char, char::MAX) {
            assert!(wildbc.contains(c.into()));
        }
        for b in 0...u8::MAX {
            assert!(wildbc.contains(b.into()));
        }
    }
}

mod class {
    use std::char;
    use std::iter::FromIterator;

    use char_iter;
    use cripes::pattern::{Class, ClassMember, Element, Polarity, Wildcard};
    use cripes::util::set::{Contains, IsSubsetOf, IsSupersetOf};

    #[test]
    fn test_contains() {
        let range = ClassMember::Range('a', 'z');
        panic_unless_eq!(26, range.len());
        assert!(range.contains('a'));
        assert!(range.contains('z'));
        assert!(! range.contains('A'));
        assert!(! range.contains('Z'));

        let atom = ClassMember::Atom('X');
        panic_unless_eq!(1, atom.len());
        assert!(atom.contains('X'));
        assert!(! atom.contains('x'));

        let class_from_members = Class::from_members([range, atom].into_iter().cloned());
        assert!(class_from_members.contains(&range));
        assert!(class_from_members.contains(&atom));
        panic_unless_eq!(27, class_from_members.len());
        for c in char_iter::new('a', 'z') {
            assert!(class_from_members.contains(c));
        }
        for c in char_iter::new('A', 'W') {
            assert!(! class_from_members.contains(c));
        }
        assert!(class_from_members.contains('X'));
        for c in char_iter::new('Y', 'Z') {
            assert!(! class_from_members.contains(c));
        }

        let cls = Class::from_iter(['a', 'b', 'c'].into_iter().cloned());
        assert_eq!(3, cls.len());

        // contains
        assert!(cls.contains('a'));
        assert!(cls.contains('b'));
        assert!(cls.contains('c'));
        for c in char_iter::new(0 as char, char::MAX) {
            if c == 'a' || c == 'b' || c == 'c' {
                assert!(cls.contains(c));
            } else {
                assert!(!cls.contains(c))
            }
        }
    }

    #[test]
    fn test_is_subset_of() {
        let cls1: Class<char> = FromIterator::from_iter(['x', 'y', 'z'].into_iter().cloned());
        assert!(Wildcard.is_superset_of(&cls1));

        let cls2: Class<char> = FromIterator::from_iter(['x', 'y'].into_iter().cloned());
        assert!(cls2.is_subset_of(&cls1));
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 Deyan Ginev. See the LICENSE
// file at the top-level directory of this distribution.
//
// Licensed under the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>.
// This file may not be copied, modified, or distributed
// except according to those terms.
extern crate cortex;
extern crate rustc_serialize;

use std::collections::HashMap;
use std::path::Path;
use std::fs;
// use std::io::Read;
use std::io::Error;

use cortex::sysinfo;
use cortex::backend::{Backend};
use cortex::data::{Corpus};

fn main() {
  println!("Importing arXiv...");
}<commit_msg>arxiv import script makes progress<commit_after>// Copyright 2015 Deyan Ginev. See the LICENSE
// file at the top-level directory of this distribution.
//
// Licensed under the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>.
// This file may not be copied, modified, or distributed
// except according to those terms.
extern crate cortex;
extern crate rustc_serialize;

// use std::collections::HashMap;
// use std::path::Path;
// use std::fs;
use std::env;
// use std::io::Read;
// use std::io::Error;
// use cortex::sysinfo;
use cortex::backend::{Backend};
use cortex::data::{Corpus, Task, TaskStatus};

fn main() {
  let mut input_args = env::args();
  let corpus_path = match input_args.next() {
    Some(path) => path,
    None => "/arXMLiv/modern/".to_string()
  };
  let corpus_name = match input_args.next() {
    Some(name) => name,
    None => "arXMLiv".to_string()
  };
  println!("Importing arXiv...");
  let backend = Backend::default();

  let corpus = backend.add(
    Corpus {
      id : None,
      name : corpus_name,
      path : corpus_path.clone(),
      complex : true,
    }).unwrap();

  backend.add(
    Task {
      id : None,
      entry : corpus_path,
      serviceid : 1, // Init service always has id 1
      corpusid : corpus.id.unwrap().clone(),
      status : TaskStatus::TODO.raw()
    }).unwrap();
}<|endoftext|>


<commit_before>use anyhow::Result;
use clap::Parser;
use regex::Regex;
use std::collections::HashMap;
use std::fs::File;
use std::io::{self, BufRead};
use std::path::PathBuf;
use std::time::Instant;

use polars::frame::DataFrame;
use polars::prelude::*;

#[derive(Parser, Debug)]
#[clap(author, version, about, long_about = None)]
struct Args {
    /// List of input files. Ideally with headers
    #[clap(short, long, multiple_values(true))]
    input: Vec<PathBuf>,

    /// An output file
    #[clap(short, long)]
    output: String,

    /// The column to select. If files have no headers, use "column_N"
    #[clap(short, long, multiple_values(true))]
    select: Vec<String>,

    /// The size of chunks to process at once
    #[clap(short, long, default_value_t = 100)]
    chunksize: usize,
}

fn load_data(path: &PathBuf, delim: u8) -> Result<DataFrame> {
    let mut df: DataFrame = CsvReader::from_path(path)?
        .has_header(true)
        .with_delimiter(delim)
        .with_null_values(Some(NullValues::AllColumns("NA".to_string())))
        .infer_schema(None)
        .finish()
        .unwrap();

    // hstack the experiment name (derived from the filename) into the DataFrame
    let iter_exp =
        vec![path.file_name().unwrap().to_str().unwrap().split('-').collect::<Vec<&str>>()[0..=2]
            .join("-")]
        .into_iter();
    let exp_col: Series = Series::new(
        "experiment",
        iter_exp.flat_map(|n| std::iter::repeat(n).take(df.height())).collect::<Vec<String>>(),
    );
    // exp_col.rename("experiment");
    df.hstack_mut(&[exp_col]).unwrap();

    Ok(df)
}

fn baseline_get_median_gt_zero(str_val: &Series) -> Series {
    let lists = str_val
        .utf8()
        .unwrap()
        .into_iter()
        .map(|x| {
            x.unwrap()
                .split(',')
                .into_iter()
                .map(|y| y.parse::<f64>().unwrap())
                .collect::<Vec<f64>>()
        });

    let medians: Vec<bool> =
        lists.into_iter().map(|x| Series::from_iter(x).median().unwrap() > 0.0).collect();
    Series::from_iter(medians)
}

fn filter_input(input: &mut DataFrame, baseline: bool) -> DataFrame {
    if baseline {
        filter_baseline(input)
    } else {
        filter_differential(input)
    }
}

fn filter_baseline(input: &mut DataFrame) -> DataFrame {
    // This is a baseline experiment.
    // Find columns starting with lower case g, then apply the function to convert to
    // medain and select greater than zero
    let mut meas = Vec::<String>::new();

    for col in input.get_column_names_owned() {
        if col.starts_with('g') {
            input.apply(&col, baseline_get_median_gt_zero).unwrap();
            meas.push(col);
        }
    }
    println!("{:?}", meas);
    // Selection should now have all the gN column names in it
    input
        .clone()
        .lazy()
        .filter(any_exprs(meas.into_iter().map(|x| col(&x)).collect::<Vec<Expr>>()))
        .collect()
        .unwrap()
}

fn filter_differential(input: &DataFrame) -> DataFrame {
    // find the p value and log fold columns
    let pv_regex = Regex::new(r".*p-value.*").unwrap();
    let log_fold_regex = Regex::new(r".*log2.*").unwrap();
    let mut pv: String = "p-value".to_string();
    let mut lf: String = "log2fold".to_string();
    for col in input.get_column_names_owned() {
        if pv_regex.is_match(&col) {
            pv = col;
        } else if log_fold_regex.is_match(&col) {
            lf = col
        }
    }

    input
        .clone()
        .lazy()
        .filter(all_exprs([col(&pv).is_not_null(), col(&lf).neq(lit(0))]))
        .collect()
        .unwrap()
}

fn load_chunk(
    paths: &mut Vec<PathBuf>,
    select: &mut Vec<String>,
) -> Result<DataFrame, anyhow::Error> {
    /*
    Work with the chunks of input to reduce them into a single dataframe of the genes we want from
    that particular set of experiments. Should return a single dataframe
    */
    let mut output: Option<DataFrame> = None;
    while let Some(infile) = paths.pop() {
        // Load the input Csv
        let mut input: DataFrame = if infile.extension().unwrap() == "tsv" {
            load_data(&infile, b'\t')
        } else {
            load_data(&infile, b',')
        }
        .unwrap_or_else(|error| match error.downcast_ref::<PolarsError>() {
            Some(PolarsError::Io(_string)) => panic!("Input file does not exist! {:?}", infile),
            _ => panic!("An error occurred! {:?}", error),
        });

        // Rename columns to remove . in the names. Now also remove spaces
        let mut new_cols = Vec::new();
        for nm in input.get_column_names().iter() {
            new_cols.push(nm.replace('.', "");//.replace(' ', ""));
        }

        if new_cols != input.get_column_names() {
            input.set_column_names(&new_cols).unwrap_or_else(|error| {
                panic!("Failed to set column names for some reason {:?}", error);
            });
        }

        // Need to detect which type of experiment it is - differential or baseline
        let type_re = Regex::new(r"tpms").unwrap();
        let filtered = filter_input(&mut input, type_re.is_match(infile.to_str().unwrap()));
        // Drop everything from the input except the genes
        let genes: DataFrame = filtered.select(select.iter()).unwrap_or_else(|error| match error {
            PolarsError::NotFound(_string) => {
                panic!(
                    "{:?} was not found in the header, does the file have a header?\n{:?}",
                    select,
                    input.get_column_names()
                );
            },
            _ => panic!("Error selecting column from input: {:?}", error),
        });

        output = match output {
            None => {
                let output_df = genes.clone();
                Some(output_df)
            },
            Some(mut output_df) => {
                // genes =/= output, so we are handling a new file
                output_df.extend(&genes).unwrap_or_else(|error| {
                    panic!("Failed to extend the output dataframe. {:?}", error)
                });
                output_df =
                    output_df.unique(None, UniqueKeepStrategy::First).unwrap_or_else(|error| {
                        panic!("Failed to parse selected column for uniqueness. {:?}", error)
                    });
                Some(output_df)
            },
        }
    }
    Ok(output.unwrap())
}

fn get_taxid(paths: &mut Vec<PathBuf>, lookup_table: &mut HashMap<String, String>) {
    let tax_regex = Regex::new(r".*Taxon_([0-9]{4,})").unwrap();

    while let Some(path) = paths.pop() {
        let exp_name =
            path.file_name().unwrap().to_str().unwrap().split('-').collect::<Vec<&str>>()[0..=2]
                .join("-")
                .replace(".condensed", "");

        let file = File::open(path).unwrap();
        let lines = io::BufReader::new(file).lines();
        for line in lines {
            match line {
                Ok(line) => {
                    let captures = tax_regex.captures(line.as_str());
                    match captures {
                        Some(caps) => {
                            let taxid = caps.get(1).map_or("", |m| m.as_str());
                            lookup_table.insert(exp_name, taxid.to_string());
                            break;
                        },
                        None => {},
                    };
                },
                Err(_idc) => {},
            }
        }
    }
}

fn main() {
    let timer = Instant::now();

    let mut args = Args::parse();

    // Always select the experiment
    args.select.push("experiment".to_string());

    // separate expression data from taxa data
    let mut taxa_files: Vec<PathBuf> = Vec::new();
    let mut expr_files: Vec<PathBuf> = Vec::new();

    let re = Regex::new(r"sdrf.tsv").unwrap();

    for infile in args.input {
        if re.is_match(infile.to_str().unwrap()) {
            // File is experiment setup data, parse for taxa
            taxa_files.push(infile);
        } else {
            // Should only be expression data, parse for genes
            expr_files.push(infile);
        }
    }

    println!("{} taxa files to parse", taxa_files.len());
    println!("{} expression files to parse", expr_files.len());

    let n_files = expr_files.len();

    let mut dataframes: Vec<DataFrame> = Vec::new();

    let chunked_input: Vec<Vec<PathBuf>> =
        expr_files.chunks(args.chunksize).map(|x| x.to_vec()).collect();
    // Read everything into a big vector
    let mut n_chunks = 0;
    for mut files_chunk in chunked_input {
        dataframes.push(
            load_chunk(&mut files_chunk, &mut args.select)
                .unwrap_or_else(|_error| panic!("Something wrong in one of the reads, aborting")),
        );
        n_chunks += 1;
        println!("Done {} files", n_chunks * args.chunksize);
    }

    let mut output: DataFrame = dataframes[0].clone();

    for item in dataframes.iter() {
        output.extend(item).unwrap_or_else(|error| {
            panic!("Unable to extend the DataFrame! Out of memory? {:?}", error)
        });
        output.unique(None, UniqueKeepStrategy::First).unwrap();
    }

    // Now parse the experiment files to get taxa
    let chunked_taxa: Vec<Vec<PathBuf>> =
        taxa_files.chunks(args.chunksize).map(|x| x.to_vec()).collect();

    let mut lookup_table: HashMap<String, String> = HashMap::new();

    for mut tax_chunk in chunked_taxa {
        get_taxid(&mut tax_chunk, &mut lookup_table);
    }

    // Now we have the experiment - taxid lookup_table, we just need to add the column to the output df

    let expt_col = output.select(["experiment"]).unwrap();
    let mut tax_ids: Vec<String> = Vec::with_capacity(expt_col.height());
    for ex in expt_col.iter() {
        let mut warn_flag: bool = true;
        let uhy = ex.utf8().unwrap();
        for (idx, x) in uhy.into_iter().enumerate() {
            if lookup_table.contains_key(x.unwrap()) {
                tax_ids.insert(idx, lookup_table.get(x.unwrap()).unwrap().to_string());
            } else {
                tax_ids.insert(idx, String::new());
                if warn_flag {
                    println!("Experiment {} does not name a taxon", x.unwrap());
                    warn_flag = false;
                }
            }
        }
    }

    output.hstack_mut(&[Series::new("taxid", tax_ids.into_iter())]).unwrap();

    let out_stream: File = File::create(args.output).unwrap();
    CsvWriter::new(out_stream)
        .has_header(true)
        .finish(&mut output)
        .unwrap_or_else(|error| panic!("Something wrong writing file: {:?}", error));

    println!("Processed {} in {} seconds", n_files, timer.elapsed().as_secs());
}
<commit_msg>Fix missing close paren<commit_after>use anyhow::Result;
use clap::Parser;
use regex::Regex;
use std::collections::HashMap;
use std::fs::File;
use std::io::{self, BufRead};
use std::path::PathBuf;
use std::time::Instant;

use polars::frame::DataFrame;
use polars::prelude::*;

#[derive(Parser, Debug)]
#[clap(author, version, about, long_about = None)]
struct Args {
    /// List of input files. Ideally with headers
    #[clap(short, long, multiple_values(true))]
    input: Vec<PathBuf>,

    /// An output file
    #[clap(short, long)]
    output: String,

    /// The column to select. If files have no headers, use "column_N"
    #[clap(short, long, multiple_values(true))]
    select: Vec<String>,

    /// The size of chunks to process at once
    #[clap(short, long, default_value_t = 100)]
    chunksize: usize,
}

fn load_data(path: &PathBuf, delim: u8) -> Result<DataFrame> {
    let mut df: DataFrame = CsvReader::from_path(path)?
        .has_header(true)
        .with_delimiter(delim)
        .with_null_values(Some(NullValues::AllColumns("NA".to_string())))
        .infer_schema(None)
        .finish()
        .unwrap();

    // hstack the experiment name (derived from the filename) into the DataFrame
    let iter_exp =
        vec![path.file_name().unwrap().to_str().unwrap().split('-').collect::<Vec<&str>>()[0..=2]
            .join("-")]
        .into_iter();
    let exp_col: Series = Series::new(
        "experiment",
        iter_exp.flat_map(|n| std::iter::repeat(n).take(df.height())).collect::<Vec<String>>(),
    );
    // exp_col.rename("experiment");
    df.hstack_mut(&[exp_col]).unwrap();

    Ok(df)
}

fn baseline_get_median_gt_zero(str_val: &Series) -> Series {
    let lists = str_val
        .utf8()
        .unwrap()
        .into_iter()
        .map(|x| {
            x.unwrap()
                .split(',')
                .into_iter()
                .map(|y| y.parse::<f64>().unwrap())
                .collect::<Vec<f64>>()
        });

    let medians: Vec<bool> =
        lists.into_iter().map(|x| Series::from_iter(x).median().unwrap() > 0.0).collect();
    Series::from_iter(medians)
}

fn filter_input(input: &mut DataFrame, baseline: bool) -> DataFrame {
    if baseline {
        filter_baseline(input)
    } else {
        filter_differential(input)
    }
}

fn filter_baseline(input: &mut DataFrame) -> DataFrame {
    // This is a baseline experiment.
    // Find columns starting with lower case g, then apply the function to convert to
    // medain and select greater than zero
    let mut meas = Vec::<String>::new();

    for col in input.get_column_names_owned() {
        if col.starts_with('g') {
            input.apply(&col, baseline_get_median_gt_zero).unwrap();
            meas.push(col);
        }
    }
    println!("{:?}", meas);
    // Selection should now have all the gN column names in it
    input
        .clone()
        .lazy()
        .filter(any_exprs(meas.into_iter().map(|x| col(&x)).collect::<Vec<Expr>>()))
        .collect()
        .unwrap()
}

fn filter_differential(input: &DataFrame) -> DataFrame {
    // find the p value and log fold columns
    let pv_regex = Regex::new(r".*p-value.*").unwrap();
    let log_fold_regex = Regex::new(r".*log2.*").unwrap();
    let mut pv: String = "p-value".to_string();
    let mut lf: String = "log2fold".to_string();
    for col in input.get_column_names_owned() {
        if pv_regex.is_match(&col) {
            pv = col;
        } else if log_fold_regex.is_match(&col) {
            lf = col
        }
    }

    input
        .clone()
        .lazy()
        .filter(all_exprs([col(&pv).is_not_null(), col(&lf).neq(lit(0))]))
        .collect()
        .unwrap()
}

fn load_chunk(
    paths: &mut Vec<PathBuf>,
    select: &mut Vec<String>,
) -> Result<DataFrame, anyhow::Error> {
    /*
    Work with the chunks of input to reduce them into a single dataframe of the genes we want from
    that particular set of experiments. Should return a single dataframe
    */
    let mut output: Option<DataFrame> = None;
    while let Some(infile) = paths.pop() {
        // Load the input Csv
        let mut input: DataFrame = if infile.extension().unwrap() == "tsv" {
            load_data(&infile, b'\t')
        } else {
            load_data(&infile, b',')
        }
        .unwrap_or_else(|error| match error.downcast_ref::<PolarsError>() {
            Some(PolarsError::Io(_string)) => panic!("Input file does not exist! {:?}", infile),
            _ => panic!("An error occurred! {:?}", error),
        });

        // Rename columns to remove . in the names. Now also remove spaces
        let mut new_cols = Vec::new();
        for nm in input.get_column_names().iter() {
            new_cols.push(nm.replace('.', ""));//.replace(' ', ""));
        }

        if new_cols != input.get_column_names() {
            input.set_column_names(&new_cols).unwrap_or_else(|error| {
                panic!("Failed to set column names for some reason {:?}", error);
            });
        }

        // Need to detect which type of experiment it is - differential or baseline
        let type_re = Regex::new(r"tpms").unwrap();
        let filtered = filter_input(&mut input, type_re.is_match(infile.to_str().unwrap()));
        // Drop everything from the input except the genes
        let genes: DataFrame = filtered.select(select.iter()).unwrap_or_else(|error| match error {
            PolarsError::NotFound(_string) => {
                panic!(
                    "{:?} was not found in the header, does the file have a header?\n{:?}",
                    select,
                    input.get_column_names()
                );
            },
            _ => panic!("Error selecting column from input: {:?}", error),
        });

        output = match output {
            None => {
                let output_df = genes.clone();
                Some(output_df)
            },
            Some(mut output_df) => {
                // genes =/= output, so we are handling a new file
                output_df.extend(&genes).unwrap_or_else(|error| {
                    panic!("Failed to extend the output dataframe. {:?}", error)
                });
                output_df =
                    output_df.unique(None, UniqueKeepStrategy::First).unwrap_or_else(|error| {
                        panic!("Failed to parse selected column for uniqueness. {:?}", error)
                    });
                Some(output_df)
            },
        }
    }
    Ok(output.unwrap())
}

fn get_taxid(paths: &mut Vec<PathBuf>, lookup_table: &mut HashMap<String, String>) {
    let tax_regex = Regex::new(r".*Taxon_([0-9]{4,})").unwrap();

    while let Some(path) = paths.pop() {
        let exp_name =
            path.file_name().unwrap().to_str().unwrap().split('-').collect::<Vec<&str>>()[0..=2]
                .join("-")
                .replace(".condensed", "");

        let file = File::open(path).unwrap();
        let lines = io::BufReader::new(file).lines();
        for line in lines {
            match line {
                Ok(line) => {
                    let captures = tax_regex.captures(line.as_str());
                    match captures {
                        Some(caps) => {
                            let taxid = caps.get(1).map_or("", |m| m.as_str());
                            lookup_table.insert(exp_name, taxid.to_string());
                            break;
                        },
                        None => {},
                    };
                },
                Err(_idc) => {},
            }
        }
    }
}

fn main() {
    let timer = Instant::now();

    let mut args = Args::parse();

    // Always select the experiment
    args.select.push("experiment".to_string());

    // separate expression data from taxa data
    let mut taxa_files: Vec<PathBuf> = Vec::new();
    let mut expr_files: Vec<PathBuf> = Vec::new();

    let re = Regex::new(r"sdrf.tsv").unwrap();

    for infile in args.input {
        if re.is_match(infile.to_str().unwrap()) {
            // File is experiment setup data, parse for taxa
            taxa_files.push(infile);
        } else {
            // Should only be expression data, parse for genes
            expr_files.push(infile);
        }
    }

    println!("{} taxa files to parse", taxa_files.len());
    println!("{} expression files to parse", expr_files.len());

    let n_files = expr_files.len();

    let mut dataframes: Vec<DataFrame> = Vec::new();

    let chunked_input: Vec<Vec<PathBuf>> =
        expr_files.chunks(args.chunksize).map(|x| x.to_vec()).collect();
    // Read everything into a big vector
    let mut n_chunks = 0;
    for mut files_chunk in chunked_input {
        dataframes.push(
            load_chunk(&mut files_chunk, &mut args.select)
                .unwrap_or_else(|_error| panic!("Something wrong in one of the reads, aborting")),
        );
        n_chunks += 1;
        println!("Done {} files", n_chunks * args.chunksize);
    }

    let mut output: DataFrame = dataframes[0].clone();

    for item in dataframes.iter() {
        output.extend(item).unwrap_or_else(|error| {
            panic!("Unable to extend the DataFrame! Out of memory? {:?}", error)
        });
        output.unique(None, UniqueKeepStrategy::First).unwrap();
    }

    // Now parse the experiment files to get taxa
    let chunked_taxa: Vec<Vec<PathBuf>> =
        taxa_files.chunks(args.chunksize).map(|x| x.to_vec()).collect();

    let mut lookup_table: HashMap<String, String> = HashMap::new();

    for mut tax_chunk in chunked_taxa {
        get_taxid(&mut tax_chunk, &mut lookup_table);
    }

    // Now we have the experiment - taxid lookup_table, we just need to add the column to the output df

    let expt_col = output.select(["experiment"]).unwrap();
    let mut tax_ids: Vec<String> = Vec::with_capacity(expt_col.height());
    for ex in expt_col.iter() {
        let mut warn_flag: bool = true;
        let uhy = ex.utf8().unwrap();
        for (idx, x) in uhy.into_iter().enumerate() {
            if lookup_table.contains_key(x.unwrap()) {
                tax_ids.insert(idx, lookup_table.get(x.unwrap()).unwrap().to_string());
            } else {
                tax_ids.insert(idx, String::new());
                if warn_flag {
                    println!("Experiment {} does not name a taxon", x.unwrap());
                    warn_flag = false;
                }
            }
        }
    }

    output.hstack_mut(&[Series::new("taxid", tax_ids.into_iter())]).unwrap();

    let out_stream: File = File::create(args.output).unwrap();
    CsvWriter::new(out_stream)
        .has_header(true)
        .finish(&mut output)
        .unwrap_or_else(|error| panic!("Something wrong writing file: {:?}", error));

    println!("Processed {} in {} seconds", n_files, timer.elapsed().as_secs());
}
<|endoftext|>


<commit_before>use rustc::lint::*;
use rustc::hir::*;
use syntax::codemap::mk_sp;
use utils::{differing_macro_contexts, snippet_opt, span_lint_and_then, SpanlessEq};

/// **What it does:** This lints manual swapping.
///
/// **Why is this bad?** The `std::mem::swap` function exposes the intent better without
/// deinitializing or copying either variable.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust,ignore
/// let t = b;
/// b = a;
/// a = t;
/// ```
declare_lint! {
    pub MANUAL_SWAP,
    Warn,
    "manual swap"
}

/// **What it does:** This lints `foo = bar; bar = foo` sequences.
///
/// **Why is this bad?** This looks like a failed attempt to swap.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust,ignore
/// a = b;
/// b = a;
/// ```
declare_lint! {
    pub ALMOST_SWAPPED,
    Warn,
    "`foo = bar; bar = foo` sequence"
}

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

impl LintPass for Swap {
    fn get_lints(&self) -> LintArray {
        lint_array![MANUAL_SWAP, ALMOST_SWAPPED]
    }
}

impl LateLintPass for Swap {
    fn check_block(&mut self, cx: &LateContext, block: &Block) {
        check_manual_swap(cx, block);
        check_suspicious_swap(cx, block);
    }
}

/// Implementation of the `MANUAL_SWAP` lint.
fn check_manual_swap(cx: &LateContext, block: &Block) {
    for w in block.stmts.windows(3) {
        if_let_chain!{[
            // let t = foo();
            let StmtDecl(ref tmp, _) = w[0].node,
            let DeclLocal(ref tmp) = tmp.node,
            let Some(ref tmp_init) = tmp.init,
            let PatKind::Binding(_, ref tmp_name, None) = tmp.pat.node,

            // foo() = bar();
            let StmtSemi(ref first, _) = w[1].node,
            let ExprAssign(ref lhs1, ref rhs1) = first.node,

            // bar() = t;
            let StmtSemi(ref second, _) = w[2].node,
            let ExprAssign(ref lhs2, ref rhs2) = second.node,
            let ExprPath(None, ref rhs2) = rhs2.node,
            rhs2.segments.len() == 1,

            tmp_name.node.as_str() == rhs2.segments[0].name.as_str(),
            SpanlessEq::new(cx).ignore_fn().eq_expr(tmp_init, lhs1),
            SpanlessEq::new(cx).ignore_fn().eq_expr(rhs1, lhs2)
        ], {
            let (what, lhs, rhs) = if let (Some(first), Some(second)) = (snippet_opt(cx, lhs1.span), snippet_opt(cx, rhs1.span)) {
                (format!(" `{}` and `{}`", first, second), first, second)
            } else {
                ("".to_owned(), "".to_owned(), "".to_owned())
            };

            let span = mk_sp(tmp.span.lo, second.span.hi);

            span_lint_and_then(cx,
                               MANUAL_SWAP,
                               span,
                               &format!("this looks like you are swapping{} manually", what),
                               |db| {
                                   if !what.is_empty() {
                                       db.span_suggestion(span, "try",
                                                          format!("std::mem::swap(&mut {}, &mut {})", lhs, rhs));
                                       db.note("or maybe you should use `std::mem::replace`?");
                                   }
                               });
        }}
    }
}

/// Implementation of the `ALMOST_SWAPPED` lint.
fn check_suspicious_swap(cx: &LateContext, block: &Block) {
    for w in block.stmts.windows(2) {
        if_let_chain!{[
            let StmtSemi(ref first, _) = w[0].node,
            let StmtSemi(ref second, _) = w[1].node,
            !differing_macro_contexts(first.span, second.span),
            let ExprAssign(ref lhs0, ref rhs0) = first.node,
            let ExprAssign(ref lhs1, ref rhs1) = second.node,
            SpanlessEq::new(cx).ignore_fn().eq_expr(lhs0, rhs1),
            SpanlessEq::new(cx).ignore_fn().eq_expr(lhs1, rhs0)
        ], {
            let (what, lhs, rhs) = if let (Some(first), Some(second)) = (snippet_opt(cx, lhs0.span), snippet_opt(cx, rhs0.span)) {
                (format!(" `{}` and `{}`", first, second), first, second)
            } else {
                ("".to_owned(), "".to_owned(), "".to_owned())
            };

            let span = mk_sp(first.span.lo, second.span.hi);

            span_lint_and_then(cx,
                               ALMOST_SWAPPED,
                               span,
                               &format!("this looks like you are trying to swap{}", what),
                               |db| {
                                   if !what.is_empty() {
                                       db.span_suggestion(span, "try",
                                                          format!("std::mem::swap(&mut {}, &mut {})", lhs, rhs));
                                       db.note("or maybe you should use `std::mem::replace`?");
                                   }
                               });
        }}
    }
}
<commit_msg>Fix wrong suggestion in `MANUAL_SWAP`<commit_after>use rustc::lint::*;
use rustc::hir::*;
use syntax::codemap::mk_sp;
use utils::{differing_macro_contexts, snippet_opt, span_lint_and_then, SpanlessEq};

/// **What it does:** This lints manual swapping.
///
/// **Why is this bad?** The `std::mem::swap` function exposes the intent better without
/// deinitializing or copying either variable.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust,ignore
/// let t = b;
/// b = a;
/// a = t;
/// ```
declare_lint! {
    pub MANUAL_SWAP,
    Warn,
    "manual swap"
}

/// **What it does:** This lints `foo = bar; bar = foo` sequences.
///
/// **Why is this bad?** This looks like a failed attempt to swap.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust,ignore
/// a = b;
/// b = a;
/// ```
declare_lint! {
    pub ALMOST_SWAPPED,
    Warn,
    "`foo = bar; bar = foo` sequence"
}

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

impl LintPass for Swap {
    fn get_lints(&self) -> LintArray {
        lint_array![MANUAL_SWAP, ALMOST_SWAPPED]
    }
}

impl LateLintPass for Swap {
    fn check_block(&mut self, cx: &LateContext, block: &Block) {
        check_manual_swap(cx, block);
        check_suspicious_swap(cx, block);
    }
}

/// Implementation of the `MANUAL_SWAP` lint.
fn check_manual_swap(cx: &LateContext, block: &Block) {
    for w in block.stmts.windows(3) {
        if_let_chain!{[
            // let t = foo();
            let StmtDecl(ref tmp, _) = w[0].node,
            let DeclLocal(ref tmp) = tmp.node,
            let Some(ref tmp_init) = tmp.init,
            let PatKind::Binding(_, ref tmp_name, None) = tmp.pat.node,

            // foo() = bar();
            let StmtSemi(ref first, _) = w[1].node,
            let ExprAssign(ref lhs1, ref rhs1) = first.node,

            // bar() = t;
            let StmtSemi(ref second, _) = w[2].node,
            let ExprAssign(ref lhs2, ref rhs2) = second.node,
            let ExprPath(None, ref rhs2) = rhs2.node,
            rhs2.segments.len() == 1,

            tmp_name.node.as_str() == rhs2.segments[0].name.as_str(),
            SpanlessEq::new(cx).ignore_fn().eq_expr(tmp_init, lhs1),
            SpanlessEq::new(cx).ignore_fn().eq_expr(rhs1, lhs2)
        ], {
            let (what, lhs, rhs) = if let (Some(first), Some(second)) = (snippet_opt(cx, lhs1.span), snippet_opt(cx, rhs1.span)) {
                (format!(" `{}` and `{}`", first, second), first, second)
            } else {
                ("".to_owned(), "".to_owned(), "".to_owned())
            };

            let span = mk_sp(w[0].span.lo, second.span.hi);

            span_lint_and_then(cx,
                               MANUAL_SWAP,
                               span,
                               &format!("this looks like you are swapping{} manually", what),
                               |db| {
                                   if !what.is_empty() {
                                       db.span_suggestion(span, "try",
                                                          format!("std::mem::swap(&mut {}, &mut {})", lhs, rhs));
                                       db.note("or maybe you should use `std::mem::replace`?");
                                   }
                               });
        }}
    }
}

/// Implementation of the `ALMOST_SWAPPED` lint.
fn check_suspicious_swap(cx: &LateContext, block: &Block) {
    for w in block.stmts.windows(2) {
        if_let_chain!{[
            let StmtSemi(ref first, _) = w[0].node,
            let StmtSemi(ref second, _) = w[1].node,
            !differing_macro_contexts(first.span, second.span),
            let ExprAssign(ref lhs0, ref rhs0) = first.node,
            let ExprAssign(ref lhs1, ref rhs1) = second.node,
            SpanlessEq::new(cx).ignore_fn().eq_expr(lhs0, rhs1),
            SpanlessEq::new(cx).ignore_fn().eq_expr(lhs1, rhs0)
        ], {
            let (what, lhs, rhs) = if let (Some(first), Some(second)) = (snippet_opt(cx, lhs0.span), snippet_opt(cx, rhs0.span)) {
                (format!(" `{}` and `{}`", first, second), first, second)
            } else {
                ("".to_owned(), "".to_owned(), "".to_owned())
            };

            let span = mk_sp(first.span.lo, second.span.hi);

            span_lint_and_then(cx,
                               ALMOST_SWAPPED,
                               span,
                               &format!("this looks like you are trying to swap{}", what),
                               |db| {
                                   if !what.is_empty() {
                                       db.span_suggestion(span, "try",
                                                          format!("std::mem::swap(&mut {}, &mut {})", lhs, rhs));
                                       db.note("or maybe you should use `std::mem::replace`?");
                                   }
                               });
        }}
    }
}
<|endoftext|>


<commit_before>use itertools::Itertools;
use syntax::{
    ast::{self, AstNode, AstToken},
    match_ast, NodeOrToken, SyntaxElement, TextSize, T,
};

use crate::{AssistContext, AssistId, AssistKind, Assists};

// Assist: remove_dbg
//
// Removes `dbg!()` macro call.
//
// ```
// fn main() {
//     $0dbg!(92);
// }
// ```
// ->
// ```
// fn main() {
//     92;
// }
// ```
pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
    let macro_call = ctx.find_node_at_offset::<ast::MacroCall>()?;
    let tt = macro_call.token_tree()?;
    let r_delim = NodeOrToken::Token(tt.right_delimiter_token()?);
    if macro_call.path()?.segment()?.name_ref()?.text() != "dbg"
        || macro_call.excl_token().is_none()
    {
        return None;
    }

    let mac_input = tt.syntax().children_with_tokens().skip(1).take_while(|it| *it != r_delim);
    let input_expressions = mac_input.group_by(|tok| tok.kind() == T![,]);
    let input_expressions = input_expressions
        .into_iter()
        .filter_map(|(is_sep, group)| (!is_sep).then(|| group))
        .map(|mut tokens| syntax::hacks::parse_expr_from_str(&tokens.join("")))
        .collect::<Option<Vec<ast::Expr>>>()?;

    let macro_expr = ast::MacroExpr::cast(macro_call.syntax().parent()?)?;
    let parent = macro_expr.syntax().parent()?;
    let (range, text) = match &*input_expressions {
        // dbg!()
        [] => {
            match_ast! {
                match parent {
                    ast::StmtList(__) => {
                        let range = macro_expr.syntax().text_range();
                        let range = match whitespace_start(macro_expr.syntax().prev_sibling_or_token()) {
                            Some(start) => range.cover_offset(start),
                            None => range,
                        };
                        (range, String::new())
                    },
                    ast::ExprStmt(it) => {
                        let range = it.syntax().text_range();
                        let range = match whitespace_start(it.syntax().prev_sibling_or_token()) {
                            Some(start) => range.cover_offset(start),
                            None => range,
                        };
                        (range, String::new())
                    },
                    _ => (macro_call.syntax().text_range(), "()".to_owned())
                }
            }
        }
        // dbg!(expr0)
        [expr] => {
            let wrap = match ast::Expr::cast(parent) {
                Some(parent) => match (expr, parent) {
                    (ast::Expr::CastExpr(_), ast::Expr::CastExpr(_)) => false,
                    (
                        ast::Expr::BoxExpr(_) | ast::Expr::PrefixExpr(_) | ast::Expr::RefExpr(_),
                        ast::Expr::AwaitExpr(_)
                        | ast::Expr::CallExpr(_)
                        | ast::Expr::CastExpr(_)
                        | ast::Expr::FieldExpr(_)
                        | ast::Expr::IndexExpr(_)
                        | ast::Expr::MethodCallExpr(_)
                        | ast::Expr::RangeExpr(_)
                        | ast::Expr::TryExpr(_),
                    ) => true,
                    (
                        ast::Expr::BinExpr(_) | ast::Expr::CastExpr(_) | ast::Expr::RangeExpr(_),
                        ast::Expr::AwaitExpr(_)
                        | ast::Expr::BinExpr(_)
                        | ast::Expr::CallExpr(_)
                        | ast::Expr::CastExpr(_)
                        | ast::Expr::FieldExpr(_)
                        | ast::Expr::IndexExpr(_)
                        | ast::Expr::MethodCallExpr(_)
                        | ast::Expr::PrefixExpr(_)
                        | ast::Expr::RangeExpr(_)
                        | ast::Expr::RefExpr(_)
                        | ast::Expr::TryExpr(_),
                    ) => true,
                    _ => false,
                },
                None => false,
            };
            (
                macro_call.syntax().text_range(),
                if wrap { format!("({expr})") } else { expr.to_string() },
            )
        }
        // dbg!(expr0, expr1, ...)
        exprs => (macro_call.syntax().text_range(), format!("({})", exprs.iter().format(", "))),
    };

    acc.add(AssistId("remove_dbg", AssistKind::Refactor), "Remove dbg!()", range, |builder| {
        builder.replace(range, text);
    })
}

fn whitespace_start(it: Option<SyntaxElement>) -> Option<TextSize> {
    Some(it?.into_token().and_then(ast::Whitespace::cast)?.syntax().text_range().start())
}

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

    use super::*;

    fn check(ra_fixture_before: &str, ra_fixture_after: &str) {
        check_assist(
            remove_dbg,
            &format!("fn main() {{\n{ra_fixture_before}\n}}"),
            &format!("fn main() {{\n{ra_fixture_after}\n}}"),
        );
    }

    #[test]
    fn test_remove_dbg() {
        check("$0dbg!(1 + 1)", "1 + 1");
        check("dbg!$0(1 + 1)", "1 + 1");
        check("dbg!(1 $0+ 1)", "1 + 1");
        check("dbg![$01 + 1]", "1 + 1");
        check("dbg!{$01 + 1}", "1 + 1");
    }

    #[test]
    fn test_remove_dbg_not_applicable() {
        check_assist_not_applicable(remove_dbg, "fn main() {$0vec![1, 2, 3]}");
        check_assist_not_applicable(remove_dbg, "fn main() {$0dbg(5, 6, 7)}");
        check_assist_not_applicable(remove_dbg, "fn main() {$0dbg!(5, 6, 7}");
    }

    #[test]
    fn test_remove_dbg_keep_semicolon_in_let() {
        // https://github.com/rust-lang/rust-analyzer/issues/5129#issuecomment-651399779
        check(
            r#"let res = $0dbg!(1 * 20); // needless comment"#,
            r#"let res = 1 * 20; // needless comment"#,
        );
        check(r#"let res = $0dbg!(); // needless comment"#, r#"let res = (); // needless comment"#);
        check(
            r#"let res = $0dbg!(1, 2); // needless comment"#,
            r#"let res = (1, 2); // needless comment"#,
        );
    }

    #[test]
    fn test_remove_dbg_cast_cast() {
        check(r#"let res = $0dbg!(x as u32) as u32;"#, r#"let res = x as u32 as u32;"#);
    }

    #[test]
    fn test_remove_dbg_prefix() {
        check(r#"let res = $0dbg!(&result).foo();"#, r#"let res = (&result).foo();"#);
        check(r#"let res = &$0dbg!(&result);"#, r#"let res = &&result;"#);
        check(r#"let res = $0dbg!(!result) && true;"#, r#"let res = !result && true;"#);
    }

    #[test]
    fn test_remove_dbg_post_expr() {
        check(r#"let res = $0dbg!(fut.await).foo();"#, r#"let res = fut.await.foo();"#);
        check(r#"let res = $0dbg!(result?).foo();"#, r#"let res = result?.foo();"#);
        check(r#"let res = $0dbg!(foo as u32).foo();"#, r#"let res = (foo as u32).foo();"#);
        check(r#"let res = $0dbg!(array[3]).foo();"#, r#"let res = array[3].foo();"#);
        check(r#"let res = $0dbg!(tuple.3).foo();"#, r#"let res = tuple.3.foo();"#);
    }

    #[test]
    fn test_remove_dbg_range_expr() {
        check(r#"let res = $0dbg!(foo..bar).foo();"#, r#"let res = (foo..bar).foo();"#);
        check(r#"let res = $0dbg!(foo..=bar).foo();"#, r#"let res = (foo..=bar).foo();"#);
    }

    #[test]
    fn test_remove_empty_dbg() {
        check_assist(remove_dbg, r#"fn foo() { $0dbg!(); }"#, r#"fn foo() { }"#);
        check_assist(
            remove_dbg,
            r#"
fn foo() {
    $0dbg!();
}
"#,
            r#"
fn foo() {
}
"#,
        );
        check_assist(
            remove_dbg,
            r#"
fn foo() {
    let test = $0dbg!();
}"#,
            r#"
fn foo() {
    let test = ();
}"#,
        );
        check_assist(
            remove_dbg,
            r#"
fn foo() {
    let t = {
        println!("Hello, world");
        $0dbg!()
    };
}"#,
            r#"
fn foo() {
    let t = {
        println!("Hello, world");
    };
}"#,
        );
    }

    #[test]
    fn test_remove_multi_dbg() {
        check(r#"$0dbg!(0, 1)"#, r#"(0, 1)"#);
        check(r#"$0dbg!(0, (1, 2))"#, r#"(0, (1, 2))"#);
    }
}
<commit_msg>Make "Remove dbg!()" assist work on selections<commit_after>use itertools::Itertools;
use syntax::{
    ast::{self, AstNode, AstToken},
    match_ast, NodeOrToken, SyntaxElement, TextRange, TextSize, T,
};

use crate::{AssistContext, AssistId, AssistKind, Assists};

// Assist: remove_dbg
//
// Removes `dbg!()` macro call.
//
// ```
// fn main() {
//     $0dbg!(92);
// }
// ```
// ->
// ```
// fn main() {
//     92;
// }
// ```
pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
    let macro_calls = if ctx.has_empty_selection() {
        vec![ctx.find_node_at_offset::<ast::MacroCall>()?]
    } else {
        ctx.covering_element()
            .as_node()?
            .descendants()
            .filter(|node| ctx.selection_trimmed().contains_range(node.text_range()))
            .filter_map(ast::MacroCall::cast)
            .collect()
    };

    let replacements =
        macro_calls.into_iter().filter_map(compute_dbg_replacement).collect::<Vec<_>>();
    if replacements.is_empty() {
        return None;
    }

    acc.add(
        AssistId("remove_dbg", AssistKind::Refactor),
        "Remove dbg!()",
        ctx.selection_trimmed(),
        |builder| {
            for (range, text) in replacements {
                builder.replace(range, text);
            }
        },
    )
}

fn compute_dbg_replacement(macro_call: ast::MacroCall) -> Option<(TextRange, String)> {
    let tt = macro_call.token_tree()?;
    let r_delim = NodeOrToken::Token(tt.right_delimiter_token()?);
    if macro_call.path()?.segment()?.name_ref()?.text() != "dbg"
        || macro_call.excl_token().is_none()
    {
        return None;
    }

    let mac_input = tt.syntax().children_with_tokens().skip(1).take_while(|it| *it != r_delim);
    let input_expressions = mac_input.group_by(|tok| tok.kind() == T![,]);
    let input_expressions = input_expressions
        .into_iter()
        .filter_map(|(is_sep, group)| (!is_sep).then(|| group))
        .map(|mut tokens| syntax::hacks::parse_expr_from_str(&tokens.join("")))
        .collect::<Option<Vec<ast::Expr>>>()?;

    let macro_expr = ast::MacroExpr::cast(macro_call.syntax().parent()?)?;
    let parent = macro_expr.syntax().parent()?;
    Some(match &*input_expressions {
        // dbg!()
        [] => {
            match_ast! {
                match parent {
                    ast::StmtList(__) => {
                        let range = macro_expr.syntax().text_range();
                        let range = match whitespace_start(macro_expr.syntax().prev_sibling_or_token()) {
                            Some(start) => range.cover_offset(start),
                            None => range,
                        };
                        (range, String::new())
                    },
                    ast::ExprStmt(it) => {
                        let range = it.syntax().text_range();
                        let range = match whitespace_start(it.syntax().prev_sibling_or_token()) {
                            Some(start) => range.cover_offset(start),
                            None => range,
                        };
                        (range, String::new())
                    },
                    _ => (macro_call.syntax().text_range(), "()".to_owned())
                }
            }
        }
        // dbg!(expr0)
        [expr] => {
            let wrap = match ast::Expr::cast(parent) {
                Some(parent) => match (expr, parent) {
                    (ast::Expr::CastExpr(_), ast::Expr::CastExpr(_)) => false,
                    (
                        ast::Expr::BoxExpr(_) | ast::Expr::PrefixExpr(_) | ast::Expr::RefExpr(_),
                        ast::Expr::AwaitExpr(_)
                        | ast::Expr::CallExpr(_)
                        | ast::Expr::CastExpr(_)
                        | ast::Expr::FieldExpr(_)
                        | ast::Expr::IndexExpr(_)
                        | ast::Expr::MethodCallExpr(_)
                        | ast::Expr::RangeExpr(_)
                        | ast::Expr::TryExpr(_),
                    ) => true,
                    (
                        ast::Expr::BinExpr(_) | ast::Expr::CastExpr(_) | ast::Expr::RangeExpr(_),
                        ast::Expr::AwaitExpr(_)
                        | ast::Expr::BinExpr(_)
                        | ast::Expr::CallExpr(_)
                        | ast::Expr::CastExpr(_)
                        | ast::Expr::FieldExpr(_)
                        | ast::Expr::IndexExpr(_)
                        | ast::Expr::MethodCallExpr(_)
                        | ast::Expr::PrefixExpr(_)
                        | ast::Expr::RangeExpr(_)
                        | ast::Expr::RefExpr(_)
                        | ast::Expr::TryExpr(_),
                    ) => true,
                    _ => false,
                },
                None => false,
            };
            (
                macro_call.syntax().text_range(),
                if wrap { format!("({expr})") } else { expr.to_string() },
            )
        }
        // dbg!(expr0, expr1, ...)
        exprs => (macro_call.syntax().text_range(), format!("({})", exprs.iter().format(", "))),
    })
}

fn whitespace_start(it: Option<SyntaxElement>) -> Option<TextSize> {
    Some(it?.into_token().and_then(ast::Whitespace::cast)?.syntax().text_range().start())
}

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

    use super::*;

    fn check(ra_fixture_before: &str, ra_fixture_after: &str) {
        check_assist(
            remove_dbg,
            &format!("fn main() {{\n{ra_fixture_before}\n}}"),
            &format!("fn main() {{\n{ra_fixture_after}\n}}"),
        );
    }

    #[test]
    fn test_remove_dbg() {
        check("$0dbg!(1 + 1)", "1 + 1");
        check("dbg!$0(1 + 1)", "1 + 1");
        check("dbg!(1 $0+ 1)", "1 + 1");
        check("dbg![$01 + 1]", "1 + 1");
        check("dbg!{$01 + 1}", "1 + 1");
    }

    #[test]
    fn test_remove_dbg_not_applicable() {
        check_assist_not_applicable(remove_dbg, "fn main() {$0vec![1, 2, 3]}");
        check_assist_not_applicable(remove_dbg, "fn main() {$0dbg(5, 6, 7)}");
        check_assist_not_applicable(remove_dbg, "fn main() {$0dbg!(5, 6, 7}");
    }

    #[test]
    fn test_remove_dbg_keep_semicolon_in_let() {
        // https://github.com/rust-lang/rust-analyzer/issues/5129#issuecomment-651399779
        check(
            r#"let res = $0dbg!(1 * 20); // needless comment"#,
            r#"let res = 1 * 20; // needless comment"#,
        );
        check(r#"let res = $0dbg!(); // needless comment"#, r#"let res = (); // needless comment"#);
        check(
            r#"let res = $0dbg!(1, 2); // needless comment"#,
            r#"let res = (1, 2); // needless comment"#,
        );
    }

    #[test]
    fn test_remove_dbg_cast_cast() {
        check(r#"let res = $0dbg!(x as u32) as u32;"#, r#"let res = x as u32 as u32;"#);
    }

    #[test]
    fn test_remove_dbg_prefix() {
        check(r#"let res = $0dbg!(&result).foo();"#, r#"let res = (&result).foo();"#);
        check(r#"let res = &$0dbg!(&result);"#, r#"let res = &&result;"#);
        check(r#"let res = $0dbg!(!result) && true;"#, r#"let res = !result && true;"#);
    }

    #[test]
    fn test_remove_dbg_post_expr() {
        check(r#"let res = $0dbg!(fut.await).foo();"#, r#"let res = fut.await.foo();"#);
        check(r#"let res = $0dbg!(result?).foo();"#, r#"let res = result?.foo();"#);
        check(r#"let res = $0dbg!(foo as u32).foo();"#, r#"let res = (foo as u32).foo();"#);
        check(r#"let res = $0dbg!(array[3]).foo();"#, r#"let res = array[3].foo();"#);
        check(r#"let res = $0dbg!(tuple.3).foo();"#, r#"let res = tuple.3.foo();"#);
    }

    #[test]
    fn test_remove_dbg_range_expr() {
        check(r#"let res = $0dbg!(foo..bar).foo();"#, r#"let res = (foo..bar).foo();"#);
        check(r#"let res = $0dbg!(foo..=bar).foo();"#, r#"let res = (foo..=bar).foo();"#);
    }

    #[test]
    fn test_remove_empty_dbg() {
        check_assist(remove_dbg, r#"fn foo() { $0dbg!(); }"#, r#"fn foo() { }"#);
        check_assist(
            remove_dbg,
            r#"
fn foo() {
    $0dbg!();
}
"#,
            r#"
fn foo() {
}
"#,
        );
        check_assist(
            remove_dbg,
            r#"
fn foo() {
    let test = $0dbg!();
}"#,
            r#"
fn foo() {
    let test = ();
}"#,
        );
        check_assist(
            remove_dbg,
            r#"
fn foo() {
    let t = {
        println!("Hello, world");
        $0dbg!()
    };
}"#,
            r#"
fn foo() {
    let t = {
        println!("Hello, world");
    };
}"#,
        );
    }

    #[test]
    fn test_remove_multi_dbg() {
        check(r#"$0dbg!(0, 1)"#, r#"(0, 1)"#);
        check(r#"$0dbg!(0, (1, 2))"#, r#"(0, (1, 2))"#);
    }

    #[test]
    fn test_range() {
        check(
            r#"
fn f() {
    dbg!(0) + $0dbg!(1);
    dbg!(())$0
}
"#,
            r#"
fn f() {
    dbg!(0) + 1;
    ()
}
"#,
        );
    }

    #[test]
    fn test_range_partial() {
        check_assist_not_applicable(remove_dbg, r#"$0dbg$0!(0)"#);
        check_assist_not_applicable(remove_dbg, r#"$0dbg!(0$0)"#);
    }
}
<|endoftext|>


<commit_before>extern crate kinder;

use kinder::lift::{Foldable, Monoid, SemiGroup};

fn sum_foldable<B : SemiGroup<A=B> + Monoid<A=B>, T: Foldable<A=B>>(xs: &T) -> B
{
    xs.foldr(B::id(), |x, y| x.add(y))
}

fn main() {
    let ints: Vec<i32> = vec!(1,2,3);
    let floats = vec!(1.0, 2.0, 3.0);
    let strings = vec!(String::from("Hello"), String::from(", "), String::from("World!"));

    println!("{}", sum_foldable(&ints)); //should print 6
    println!("{}", sum_foldable(&floats)); //should print 6.0
    println!("{}", sum_foldable(&strings)); //should print Hello, World!
}
<commit_msg>Update fold-example.rs<commit_after>extern crate kinder;

use kinder::lift::{Foldable, Monoid, SemiGroup};

fn sum_foldable<B : Monoid<A=B>, T: Foldable<A=B>>(xs: &T) -> B
{
    xs.foldr(B::id(), |x, y| x.add(y))
}

fn main() {
    let ints: Vec<i32> = vec!(1,2,3);
    let floats = vec!(1.0, 2.0, 3.0);
    let strings = vec!(String::from("Hello"), String::from(", "), String::from("World!"));

    println!("{}", sum_foldable(&ints)); //should print 6
    println!("{}", sum_foldable(&floats)); //should print 6.0
    println!("{}", sum_foldable(&strings)); //should print Hello, World!
}
<|endoftext|>


<commit_before>use std::fmt;
// use std::iter;
use std::slice;
use std::ops;
use std::collections::HashMap;
use error::*;

#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
pub enum Operator {
    Add,
    Sub,
    Mul,
    Div,
}

#[cfg_attr(rustfmt, rustfmt_skip)]
impl fmt::Display for Operator {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::Operator::*;
        write!(f, "{}", match *self {
            Add => "+",
            Sub => "-",
            Mul => "*",
            Div => "/",
        })
    }
}

#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
pub enum Arity {
    Nullary,
    Unary,
    Binary,
    Ternary,
    Multiary,  // 2+ arguments
}

impl fmt::Display for Arity {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::Arity::*;
        let text = match *self {
            Nullary => "0",
            Unary   => "1",
            Binary  => "2",
            Ternary => "3",
            Multiary => "at least 2",
        };

        write!(f, "{} args", text)
    }
}

#[derive(Copy, Clone, Debug, PartialEq)]
pub enum Atom {
    Op(Operator),
    // Bool(bool),
    Int(i64),
    // Float(f64),
    // Str(String),
}

impl fmt::Display for Atom {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::Atom::*;
        match *self {
            Op(ref t) => write!(f, "{}", t),
            // Bool(ref t) => write!(f, "{}", t),
            Int(ref t) => write!(f, "{}", t),
            // Float(ref t) => write!(f, "{}", t),
            // Str(ref t) => write!(f, "{}", t),
        }
    }
}

impl From<Operator> for Atom {
    fn from(x: Operator) -> Self {
        Atom::Op(x)
    }
}

impl From<i64> for Atom {
    fn from(x: i64) -> Self {
        Atom::Int(x)
    }
}

impl ops::Add<Atom> for Atom {
    type Output = Atom;
    fn add(self, rhs: Atom) -> Self::Output {
        use self::Atom::*;
        match (self, rhs) {
            (Int(i), Int(j)) => Int(i + j),
            _ => panic!(),
        }
    }
}

impl ops::Sub<Atom> for Atom {
    type Output = Atom;
    fn sub(self, rhs: Atom) -> Self::Output {
        use self::Atom::*;
        match (self, rhs) {
            (Int(i), Int(j)) => Int(i - j),
            _ => panic!(),
        }
    }
}

impl ops::Mul<Atom> for Atom {
    type Output = Atom;
    fn mul(self, rhs: Atom) -> Self::Output {
        use self::Atom::*;
        match (self, rhs) {
            (Int(i), Int(j)) => Int(i * j),
            _ => panic!(),
        }
    }
}

impl ops::Div<Atom> for Atom {
    type Output = Atom;
    fn div(self, rhs: Atom) -> Self::Output {
        use self::Atom::*;
        match (self, rhs) {
            (Int(i), Int(j)) => Int(i / j),
            _ => panic!(),
        }
    }
}

#[derive(Clone, Debug, PartialEq)]
pub enum Value {
    Atom(Atom),
    List(List),
}

impl fmt::Display for Value {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::Value::*;
        match *self {
            Atom(ref t) => write!(f, "{}", t),
            List(ref t) => write!(f, "{}", t),
        }
    }
}

impl<T> From<T> for Value where T: Into<Atom> {
    fn from(v: T) -> Self {
        Value::Atom(v.into())
    }
}

impl ops::Add<Value> for Value {
    type Output = Value;
    fn add(self, rhs: Value) -> Self::Output {
        use self::Value::*;
        match (self, rhs) {
            (Atom(i), Atom(j)) => Atom(i + j),
            _ => panic!(),
        }
    }
}

impl ops::Sub<Value> for Value {
    type Output = Value;
    fn sub(self, rhs: Value) -> Self::Output {
        use self::Value::*;
        match (self, rhs) {
            (Atom(i), Atom(j)) => Atom(i - j),
            _ => panic!(),
        }
    }
}

impl ops::Mul<Value> for Value {
    type Output = Value;
    fn mul(self, rhs: Value) -> Self::Output {
        use self::Value::*;
        match (self, rhs) {
            (Atom(i), Atom(j)) => Atom(i * j),
            _ => panic!(),
        }
    }
}

impl ops::Div<Value> for Value {
    type Output = Value;
    fn div(self, rhs: Value) -> Self::Output {
        use self::Value::*;
        match (self, rhs) {
            (Atom(i), Atom(j)) => Atom(i / j),
            _ => panic!(),
        }
    }
}

#[derive(Clone, Debug, PartialEq)]
pub enum Node {
    Atom (Atom),
    List (List),
    Expr (Expr),
}

impl Node {
    fn eval(&self) -> Result<Value> {
        use self::Node::*;
        match *self {
            Atom(a) => Ok(Value::Atom(a)),
            List(ref l) => Ok(Value::List(l.clone())),
            Expr(ref e) => e.eval(),
        }
    }
}

impl<T> From<T> for Node where T: Into<Atom> {
    fn from(v: T) -> Self {
        Node::Atom(v.into())
    }
}

// impl<T> From<T> for Node where T: Into<List> {
//     fn from(l: T) -> Self {
//         Node::List(l.into())
//     }
// }
//
// impl<T> From<T> for Node where T: Into<Expr> {
//     fn from(e: T) -> Self {
//         Node::Expr(e.into())
//     }
// }

impl fmt::Display for Node {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::Node::*;
        match *self {
            Atom(ref a) => write!(f, "{}", a),
            List(ref l) => write!(f, "{}", l),
            Expr(ref e) => write!(f, "{}", e),
        }
    }
}

#[derive(Clone, Debug, PartialEq)]
pub struct List {
    inner: Vec<Node>
}

impl List {
    pub fn new() -> Self {
        List { inner: Vec::new() }
    }
}

impl From<Vec<Node>> for List {
    fn from(v: Vec<Node>) -> Self {
        List { inner: v }
    }
}

impl fmt::Display for List {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let elements = self.inner.iter()
            .map(ToString::to_string)
            .collect::<Vec<_>>()
            .join(" ");
        write!(f, "( {} )", elements)
    }
}

#[derive(Clone, Debug, PartialEq)]
pub struct Expr {
    op: Operator,
    args: Vec<Node>
}

impl Expr {
    pub fn new(op: Operator, args: Vec<Node>) -> Self {
        Expr { op: op, args: args }
    }

    pub fn op(&self) -> Operator {
        self.op
    }

    pub fn args(&self) -> slice::Iter<Node> {
        self.args.iter()
    }

    pub fn num_args(&self) -> usize {
        self.args.len()
    }

    pub fn expect_arity(&self, arity: Arity) -> bool {
        use self::Arity::*;
        let num_args = self.num_args();
        match arity {
            Nullary  => num_args == 0,
            Unary    => num_args == 1,
            Binary   => num_args == 2,
            Ternary  => num_args == 3,
            Multiary => num_args >= 2,
        }
    }

    pub fn eval(&self) -> Result<Value> {
        // use self::Node::*;
        // use self::Atom::*;
        use self::Operator::*;
        use self::Arity::*;

        // Arity = number of arguments accepted
        let mut arity = HashMap::new();

        arity.insert(Add, Multiary);
        arity.insert(Sub, Binary);
        arity.insert(Mul, Multiary);
        arity.insert(Div, Binary);

        let op = self.op();

        let expected_arity = match arity.get(&op) {
            Some(e) => e,
            None => return Err(format!("operator {} not found", op).into())
        };

        // Check if number of arguments match expected number
        if !self.expect_arity(*expected_arity) {
            return Err(format!(
                "{} operator expected {} arguments, but received {}",
                op, expected_arity, self.num_args()
            ).into())
        }

        let evaled_args: Result<Vec<_>> = self.args().map(Node::eval).collect();

        // Return the first expr that cannot eval correctly
        match evaled_args {
            Err(e) => Err(e),
            Ok(v) => {
                let mut eval_args = v.into_iter();
                match op {
                    Add => Ok(eval_args.fold(0.into(), |acc, x| acc + x)),
                    Sub => {
                        let a = eval_args.next().unwrap();
                        let b = eval_args.next().unwrap();
                        Ok(a - b)
                    },
                    Mul => Ok(eval_args.fold(1.into(), |acc, x| acc * x)),
                    Div => {
                        let a = eval_args.next().unwrap();
                        let b = eval_args.next().unwrap();
                        Ok(a / b)
                    }
                }

            }
        }
    }
}

impl fmt::Display for Expr {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let elements = self.args.iter()
            .map(ToString::to_string)
            .collect::<Vec<_>>()
            .join(" ");
        write!(f, "( {} {} )", self.op, elements)
    }
}
<commit_msg>Add (commented out) unstable iter sum / prod<commit_after>use std::fmt;
use std::iter;
use std::slice;
use std::ops;
use std::collections::HashMap;
use error::*;

#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
pub enum Operator {
    Add,
    Sub,
    Mul,
    Div,
}

#[cfg_attr(rustfmt, rustfmt_skip)]
impl fmt::Display for Operator {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::Operator::*;
        write!(f, "{}", match *self {
            Add => "+",
            Sub => "-",
            Mul => "*",
            Div => "/",
        })
    }
}

#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
pub enum Arity {
    Nullary,
    Unary,
    Binary,
    Ternary,
    Multiary,  // 2+ arguments
}

impl fmt::Display for Arity {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::Arity::*;
        let text = match *self {
            Nullary => "0",
            Unary   => "1",
            Binary  => "2",
            Ternary => "3",
            Multiary => "at least 2",
        };

        write!(f, "{} args", text)
    }
}

#[derive(Copy, Clone, Debug, PartialEq)]
pub enum Atom {
    Op(Operator),
    // Bool(bool),
    Int(i64),
    // Float(f64),
    // Str(String),
}

impl fmt::Display for Atom {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::Atom::*;
        match *self {
            Op(ref t) => write!(f, "{}", t),
            // Bool(ref t) => write!(f, "{}", t),
            Int(ref t) => write!(f, "{}", t),
            // Float(ref t) => write!(f, "{}", t),
            // Str(ref t) => write!(f, "{}", t),
        }
    }
}

impl From<Operator> for Atom {
    fn from(x: Operator) -> Self {
        Atom::Op(x)
    }
}

impl From<i64> for Atom {
    fn from(x: i64) -> Self {
        Atom::Int(x)
    }
}

impl ops::Add<Atom> for Atom {
    type Output = Atom;
    fn add(self, rhs: Atom) -> Self::Output {
        use self::Atom::*;
        match (self, rhs) {
            (Int(i), Int(j)) => Int(i + j),
            _ => panic!(),
        }
    }
}

impl ops::Sub<Atom> for Atom {
    type Output = Atom;
    fn sub(self, rhs: Atom) -> Self::Output {
        use self::Atom::*;
        match (self, rhs) {
            (Int(i), Int(j)) => Int(i - j),
            _ => panic!(),
        }
    }
}

impl ops::Mul<Atom> for Atom {
    type Output = Atom;
    fn mul(self, rhs: Atom) -> Self::Output {
        use self::Atom::*;
        match (self, rhs) {
            (Int(i), Int(j)) => Int(i * j),
            _ => panic!(),
        }
    }
}

impl ops::Div<Atom> for Atom {
    type Output = Atom;
    fn div(self, rhs: Atom) -> Self::Output {
        use self::Atom::*;
        match (self, rhs) {
            (Int(i), Int(j)) => Int(i / j),
            _ => panic!(),
        }
    }
}

#[derive(Clone, Debug, PartialEq)]
pub enum Value {
    Atom(Atom),
    List(List),
}

impl fmt::Display for Value {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::Value::*;
        match *self {
            Atom(ref t) => write!(f, "{}", t),
            List(ref t) => write!(f, "{}", t),
        }
    }
}

impl<T> From<T> for Value where T: Into<Atom> {
    fn from(v: T) -> Self {
        Value::Atom(v.into())
    }
}

impl ops::Add<Value> for Value {
    type Output = Value;
    fn add(self, rhs: Value) -> Self::Output {
        use self::Value::*;
        match (self, rhs) {
            (Atom(i), Atom(j)) => Atom(i + j),
            _ => panic!(),
        }
    }
}

impl ops::Sub<Value> for Value {
    type Output = Value;
    fn sub(self, rhs: Value) -> Self::Output {
        use self::Value::*;
        match (self, rhs) {
            (Atom(i), Atom(j)) => Atom(i - j),
            _ => panic!(),
        }
    }
}

impl ops::Mul<Value> for Value {
    type Output = Value;
    fn mul(self, rhs: Value) -> Self::Output {
        use self::Value::*;
        match (self, rhs) {
            (Atom(i), Atom(j)) => Atom(i * j),
            _ => panic!(),
        }
    }
}

impl ops::Div<Value> for Value {
    type Output = Value;
    fn div(self, rhs: Value) -> Self::Output {
        use self::Value::*;
        match (self, rhs) {
            (Atom(i), Atom(j)) => Atom(i / j),
            _ => panic!(),
        }
    }
}

// Unstable feature iter_arith_traits - issue #34529
// Stabilized by Rust 1.12
// impl iter::Sum<Value> for Value {
//     fn sum<I>(iter: I) -> Self where I: Iterator<Item=Value> {
//         iter.fold(0.into(), |acc, x| acc + x)
//     }
// }

// Unstable feature iter_arith_traits - issue #34529
// Stabilized by Rust 1.12
// impl iter::Mul<Value> for Value {
//     fn mul<I>(iter: I) -> Self where I: Iterator<Item=Value> {
//         iter.fold(1.into(), |acc, x| acc * x)
//     }
// }

#[derive(Clone, Debug, PartialEq)]
pub enum Node {
    Atom (Atom),
    List (List),
    Expr (Expr),
}

impl Node {
    fn eval(&self) -> Result<Value> {
        use self::Node::*;
        match *self {
            Atom(a) => Ok(Value::Atom(a)),
            List(ref l) => Ok(Value::List(l.clone())),
            Expr(ref e) => e.eval(),
        }
    }
}

impl<T> From<T> for Node where T: Into<Atom> {
    fn from(v: T) -> Self {
        Node::Atom(v.into())
    }
}

// impl<T> From<T> for Node where T: Into<List> {
//     fn from(l: T) -> Self {
//         Node::List(l.into())
//     }
// }
//
// impl<T> From<T> for Node where T: Into<Expr> {
//     fn from(e: T) -> Self {
//         Node::Expr(e.into())
//     }
// }

impl fmt::Display for Node {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::Node::*;
        match *self {
            Atom(ref a) => write!(f, "{}", a),
            List(ref l) => write!(f, "{}", l),
            Expr(ref e) => write!(f, "{}", e),
        }
    }
}

#[derive(Clone, Debug, PartialEq)]
pub struct List {
    inner: Vec<Node>
}

impl List {
    pub fn new() -> Self {
        List { inner: Vec::new() }
    }
}

impl From<Vec<Node>> for List {
    fn from(v: Vec<Node>) -> Self {
        List { inner: v }
    }
}

impl fmt::Display for List {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let elements = self.inner.iter()
            .map(ToString::to_string)
            .collect::<Vec<_>>()
            .join(" ");
        write!(f, "( {} )", elements)
    }
}

#[derive(Clone, Debug, PartialEq)]
pub struct Expr {
    op: Operator,
    args: Vec<Node>
}

impl Expr {
    pub fn new(op: Operator, args: Vec<Node>) -> Self {
        Expr { op: op, args: args }
    }

    pub fn op(&self) -> Operator {
        self.op
    }

    pub fn args(&self) -> slice::Iter<Node> {
        self.args.iter()
    }

    pub fn num_args(&self) -> usize {
        self.args.len()
    }

    pub fn expect_arity(&self, arity: Arity) -> bool {
        use self::Arity::*;
        let num_args = self.num_args();
        match arity {
            Nullary  => num_args == 0,
            Unary    => num_args == 1,
            Binary   => num_args == 2,
            Ternary  => num_args == 3,
            Multiary => num_args >= 2,
        }
    }

    pub fn eval(&self) -> Result<Value> {
        // use self::Node::*;
        // use self::Atom::*;
        use self::Operator::*;
        use self::Arity::*;

        // Arity = number of arguments accepted
        let mut arity = HashMap::new();

        arity.insert(Add, Multiary);
        arity.insert(Sub, Binary);
        arity.insert(Mul, Multiary);
        arity.insert(Div, Binary);

        let op = self.op();

        let expected_arity = match arity.get(&op) {
            Some(e) => e,
            None => return Err(format!("operator {} not found", op).into())
        };

        // Check if number of arguments match expected number
        if !self.expect_arity(*expected_arity) {
            return Err(format!(
                "{} operator expected {} arguments, but received {}",
                op, expected_arity, self.num_args()
            ).into())
        }

        let evaled_args: Result<Vec<_>> = self.args().map(Node::eval).collect();

        // Return the first expr that cannot eval correctly
        match evaled_args {
            Err(e) => Err(e),
            Ok(v) => {
                let mut eval_args = v.into_iter();
                match op {
                    Add => Ok(eval_args.fold(0.into(), |acc, x| acc + x)),
                    Sub => {
                        let a = eval_args.next().unwrap();
                        let b = eval_args.next().unwrap();
                        Ok(a - b)
                    },
                    Mul => Ok(eval_args.fold(1.into(), |acc, x| acc * x)),
                    Div => {
                        let a = eval_args.next().unwrap();
                        let b = eval_args.next().unwrap();
                        Ok(a / b)
                    }
                }

            }
        }
    }
}

impl fmt::Display for Expr {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let elements = self.args.iter()
            .map(ToString::to_string)
            .collect::<Vec<_>>()
            .join(" ");
        write!(f, "( {} {} )", self.op, elements)
    }
}
<|endoftext|>