<commit_before>use std::error::Error;
use std::fmt::Display;
use std::fmt::Formatter;
use std::fmt::Error as FmtError;
use std::clone::Clone;

/**
 * Kind of store error
 */
#[derive(Clone, Copy, Debug)]
pub enum StoreErrorKind {
    FileError,
    IdLocked,
    IdNotFound,
    OutOfMemory,
    FileNotFound,
    FileNotCreated,
    StorePathExists,
    // maybe more
}

fn store_error_type_as_str(e: &StoreErrorKind) -> &'static str {
    match e {
        &StoreErrorKind::FileError       => "File Error",
        &StoreErrorKind::IdLocked        => "ID locked",
        &StoreErrorKind::IdNotFound      => "ID not found",
        &StoreErrorKind::OutOfMemory     => "Out of Memory",
        &StoreErrorKind::FileNotFound    => "File corresponding to ID not found",
        &StoreErrorKind::FileNotCreated  => "File corresponding to ID could not be created",
        &StoreErrorKind::StorePathExists => "Store path exists",
    }
}

impl Display for StoreErrorKind {

    fn fmt(&self, fmt: &mut Formatter) -> Result<(), FmtError> {
        try!(write!(fmt, "{}", store_error_type_as_str(self)));
        Ok(())
    }

}

/**
 * Store error type
 */
#[derive(Debug)]
pub struct StoreError {
    err_type: StoreErrorKind,
    cause: Option<Box<Error>>,
}

impl StoreError {

    /**
     * Build a new StoreError from an StoreErrorKind, optionally with cause
     */
    pub fn new(errtype: StoreErrorKind, cause: Option<Box<Error>>)
        -> StoreError
    {
        StoreError {
            err_type: errtype,
            cause: cause,
        }
    }

    /**
     * Get the error type of this StoreError
     */
    pub fn err_type(&self) -> StoreErrorKind {
        self.err_type.clone()
    }

}

impl Display for StoreError {

    fn fmt(&self, fmt: &mut Formatter) -> Result<(), FmtError> {
        try!(write!(fmt, "[{}]", store_error_type_as_str(&self.err_type.clone())));
        Ok(())
    }

}

impl Error for StoreError {

    fn description(&self) -> &str {
        store_error_type_as_str(&self.err_type.clone())
    }

    fn cause(&self) -> Option<&Error> {
        self.cause.as_ref().map(|e| &**e)
    }

}

<commit_msg>Add error kind: Create errors<commit_after>use std::error::Error;
use std::fmt::Display;
use std::fmt::Formatter;
use std::fmt::Error as FmtError;
use std::clone::Clone;

/**
 * Kind of store error
 */
#[derive(Clone, Copy, Debug)]
pub enum StoreErrorKind {
    FileError,
    IdLocked,
    IdNotFound,
    OutOfMemory,
    FileNotFound,
    FileNotCreated,
    StorePathExists,
    StorePathCreate,
    // maybe more
}

fn store_error_type_as_str(e: &StoreErrorKind) -> &'static str {
    match e {
        &StoreErrorKind::FileError       => "File Error",
        &StoreErrorKind::IdLocked        => "ID locked",
        &StoreErrorKind::IdNotFound      => "ID not found",
        &StoreErrorKind::OutOfMemory     => "Out of Memory",
        &StoreErrorKind::FileNotFound    => "File corresponding to ID not found",
        &StoreErrorKind::FileNotCreated  => "File corresponding to ID could not be created",
        &StoreErrorKind::StorePathExists => "Store path exists",
        &StoreErrorKind::StorePathCreate => "Store path create",
    }
}

impl Display for StoreErrorKind {

    fn fmt(&self, fmt: &mut Formatter) -> Result<(), FmtError> {
        try!(write!(fmt, "{}", store_error_type_as_str(self)));
        Ok(())
    }

}

/**
 * Store error type
 */
#[derive(Debug)]
pub struct StoreError {
    err_type: StoreErrorKind,
    cause: Option<Box<Error>>,
}

impl StoreError {

    /**
     * Build a new StoreError from an StoreErrorKind, optionally with cause
     */
    pub fn new(errtype: StoreErrorKind, cause: Option<Box<Error>>)
        -> StoreError
    {
        StoreError {
            err_type: errtype,
            cause: cause,
        }
    }

    /**
     * Get the error type of this StoreError
     */
    pub fn err_type(&self) -> StoreErrorKind {
        self.err_type.clone()
    }

}

impl Display for StoreError {

    fn fmt(&self, fmt: &mut Formatter) -> Result<(), FmtError> {
        try!(write!(fmt, "[{}]", store_error_type_as_str(&self.err_type.clone())));
        Ok(())
    }

}

impl Error for StoreError {

    fn description(&self) -> &str {
        store_error_type_as_str(&self.err_type.clone())
    }

    fn cause(&self) -> Option<&Error> {
        self.cause.as_ref().map(|e| &**e)
    }

}

<|endoftext|>


<commit_before>// This code is heavily inspired by hoedown.

//
// Performance observations:
//
// Note that buf.head() == Some(&ch) is much faster than buf[0] == ch.
//

extern crate extra;
use extra::test::BenchHarness;

static SP: u8 = ' ' as u8;
static NL: u8 = '\n' as u8;

/// Returns true if the line is a horizontal rule.
fn is_hrule_old(buf: &[u8]) -> bool {
    let len = buf.len();

    if len < 3 { return false }

    let mut pos: uint = 0;

    // Skip up to three leading spaces.
    //
    // We don't need to care about tabs here as in this position they are
    // equivalent to 4 spaces.
    if buf[0] == SP {
        pos += 1;
        if buf[1] == SP {
            pos += 1;
            if buf[2] == SP {
                pos += 1;
            }
        }
    }

    // We need at least 3 items
    if pos + 2 >= len { return false }

    let item = buf[pos];

    if !(item == ('*' as u8) || item == ('-' as u8) || item == ('_' as u8)) {
        return false;
    }

    let mut cnt: uint = 0; // The count of '*', '-' or '_'
    for &ch in buf.slice_from(pos).iter() {
        if ch == item {
            cnt += 1;
        } else if ch == NL {
            break;
        } else if ch != SP {
            return false;
        }
    }

    return cnt >= 3;
}

/// Returns true if the line is a horizontal rule.
fn is_hrule(buf: &[u8]) -> bool {
    let mut buf = buf;

    if buf.len() < 3 { return false }

    // Skip up to three leading spaces.
    //
    // We don't need to care about tabs here as in this position they are
    // equivalent to 4 spaces.
    if buf.head() == Some(&SP) { buf = buf.tail(); }
    if buf.head() == Some(&SP) { buf = buf.tail(); }
    if buf.head() == Some(&SP) { buf = buf.tail(); }

    // We need at least 3 items
    if buf.len() < 3 { return false }

    let item = buf[0];

    if !(item == ('*' as u8) || item == ('-' as u8) || item == ('_' as u8)) {
        return false;
    }

    // The count of '*', '-' or '_'
    let mut cnt: uint = 0;

    for &ch in buf.iter() {
        if ch == item {
            cnt += 1;
        } else if ch == NL {
            break;
        } else if ch != SP {
            return false;
        }
    }

    return cnt >= 3;
}

#[test]
fn test_is_hrule() {
    // examples as given on the markdown homepage
    assert_eq!(is_hrule(bytes!("* * *\n")), true);
    assert_eq!(is_hrule(bytes!("***\n")), true);
    assert_eq!(is_hrule(bytes!("*****\n")), true);
    assert_eq!(is_hrule(bytes!("- - -\n")), true);
    assert_eq!(is_hrule(bytes!("---------------------------------------\n")), true);

    // up to three spaces ignored
    assert_eq!(is_hrule(bytes!(" ***\n")), true);
    assert_eq!(is_hrule(bytes!("  ***\n")), true);
    assert_eq!(is_hrule(bytes!("   ***\n")), true);

    // but not four, or a tab which is equivalent to four spaces
    assert_eq!(is_hrule(bytes!("    ***\n")), false);
    assert_eq!(is_hrule(bytes!("\t***\n")), false);

    // need at least three
    assert_eq!(is_hrule(bytes!("*\n")), false);
    assert_eq!(is_hrule(bytes!("**\n")), false);
    assert_eq!(is_hrule(bytes!("* *\n")), false);
    assert_eq!(is_hrule(bytes!("   * *\n")), false);

    // Also works without newline at the end
    assert_eq!(is_hrule(bytes!("* * *")), true);

    // And underscores also supported
    assert_eq!(is_hrule(bytes!("___")), true);
    assert_eq!(is_hrule(bytes!("______________")), true);
    assert_eq!(is_hrule(bytes!(" ______________")), true);
}

#[bench]
fn bench_is_hrule(b: &mut BenchHarness) {
    let s = bytes!("   * * * * * * * * * * * * * * * *\n");
    b.iter(|| is_hrule(s));
}

#[bench]
fn bench_is_hrule_old(b: &mut BenchHarness) {
    let s = bytes!("   * * * * * * * * * * * * * * * *\n");
    b.iter(|| is_hrule_old(s));
}

<commit_msg>Get rid of old code<commit_after>// This code is heavily inspired by hoedown.

//
// Performance observations:
//
// Note that buf.head() == Some(&ch) is much faster than buf[0] == ch.
//

extern crate extra;
use extra::test::BenchHarness;

static SP: u8 = ' ' as u8;
static NL: u8 = '\n' as u8;

/// Returns true if the line is a horizontal rule.
fn is_hrule(buf: &[u8]) -> bool {
    let mut buf = buf;

    if buf.len() < 3 { return false }

    // Skip up to three leading spaces.
    //
    // We don't need to care about tabs here as in this position they are
    // equivalent to 4 spaces.
    if buf.head() == Some(&SP) { buf = buf.tail(); }
    if buf.head() == Some(&SP) { buf = buf.tail(); }
    if buf.head() == Some(&SP) { buf = buf.tail(); }

    // We need at least 3 items
    if buf.len() < 3 { return false }

    let item = buf[0];

    if !(item == ('*' as u8) || item == ('-' as u8) || item == ('_' as u8)) {
        return false;
    }

    // The count of '*', '-' or '_'
    let mut cnt: uint = 0;

    for &ch in buf.iter() {
        if ch == item {
            cnt += 1;
        } else if ch == NL {
            break;
        } else if ch != SP {
            return false;
        }
    }

    return cnt >= 3;
}

#[test]
fn test_is_hrule() {
    // examples as given on the markdown homepage
    assert_eq!(is_hrule(bytes!("* * *\n")), true);
    assert_eq!(is_hrule(bytes!("***\n")), true);
    assert_eq!(is_hrule(bytes!("*****\n")), true);
    assert_eq!(is_hrule(bytes!("- - -\n")), true);
    assert_eq!(is_hrule(bytes!("---------------------------------------\n")), true);

    // up to three spaces ignored
    assert_eq!(is_hrule(bytes!(" ***\n")), true);
    assert_eq!(is_hrule(bytes!("  ***\n")), true);
    assert_eq!(is_hrule(bytes!("   ***\n")), true);

    // but not four, or a tab which is equivalent to four spaces
    assert_eq!(is_hrule(bytes!("    ***\n")), false);
    assert_eq!(is_hrule(bytes!("\t***\n")), false);

    // need at least three
    assert_eq!(is_hrule(bytes!("*\n")), false);
    assert_eq!(is_hrule(bytes!("**\n")), false);
    assert_eq!(is_hrule(bytes!("* *\n")), false);
    assert_eq!(is_hrule(bytes!("   * *\n")), false);

    // Also works without newline at the end
    assert_eq!(is_hrule(bytes!("* * *")), true);

    // And underscores also supported
    assert_eq!(is_hrule(bytes!("___")), true);
    assert_eq!(is_hrule(bytes!("______________")), true);
    assert_eq!(is_hrule(bytes!(" ______________")), true);
}

#[bench]
fn bench_is_hrule(b: &mut BenchHarness) {
    let s = bytes!("   * * * * * * * * * * * * * * * *\n");
    b.iter(|| is_hrule(s));
}
<|endoftext|>


<commit_before>//! Utility functions for Header implementations.

use std::str::{FromStr, from_utf8};
use std::fmt::{mod, Show};
use time::{Tm, strptime};

/// Reads a single raw string when parsing a header
pub fn from_one_raw_str<T: FromStr>(raw: &[Vec<u8>]) -> Option<T> {
    if raw.len() != 1 {
        return None;
    }
    // we JUST checked that raw.len() == 1, so raw[0] WILL exist.
    match from_utf8(unsafe { raw[].unsafe_get(0)[] }) {
        Ok(s) => FromStr::from_str(s),
        Err(_) => None
    }
}

/// Reads a comma-delimited raw header into a Vec.
#[inline]
pub fn from_comma_delimited<T: FromStr>(raw: &[Vec<u8>]) -> Option<Vec<T>> {
    if raw.len() != 1 {
        return None;
    }
    // we JUST checked that raw.len() == 1, so raw[0] WILL exist.
    from_one_comma_delimited(unsafe { raw.as_slice().unsafe_get(0).as_slice() })
}

/// Reads a comma-delimited raw string into a Vec.
pub fn from_one_comma_delimited<T: FromStr>(raw: &[u8]) -> Option<Vec<T>> {
    match from_utf8(raw) {
        Ok(s) => {
            Some(s.as_slice()
                 .split([',', ' '].as_slice())
                 .filter_map(FromStr::from_str)
                 .collect())
        }
        Err(_) => None
    }
}

/// Format an array into a comma-delimited string.
pub fn fmt_comma_delimited<T: Show>(fmt: &mut fmt::Formatter, parts: &[T]) -> fmt::Result {
    let last = parts.len() - 1;
    for (i, part) in parts.iter().enumerate() {
        try!(part.fmt(fmt));
        if i < last {
            try!(", ".fmt(fmt));
        }
    }
    Ok(())
}

/// Get a Tm from HTTP date formats.
//    Prior to 1995, there were three different formats commonly used by
//   servers to communicate timestamps.  For compatibility with old
//   implementations, all three are defined here.  The preferred format is
//   a fixed-length and single-zone subset of the date and time
//   specification used by the Internet Message Format [RFC5322].
//
//     HTTP-date    = IMF-fixdate / obs-date
//
//   An example of the preferred format is
//
//     Sun, 06 Nov 1994 08:49:37 GMT    ; IMF-fixdate
//
//   Examples of the two obsolete formats are
//
//     Sunday, 06-Nov-94 08:49:37 GMT   ; obsolete RFC 850 format
//     Sun Nov  6 08:49:37 1994         ; ANSI C's asctime() format
//
//   A recipient that parses a timestamp value in an HTTP header field
//   MUST accept all three HTTP-date formats.  When a sender generates a
//   header field that contains one or more timestamps defined as
//   HTTP-date, the sender MUST generate those timestamps in the
//   IMF-fixdate format.
pub fn tm_from_str(s: &str) -> Option<Tm> {
    strptime(s, "%a, %d %b %Y %T %Z").or_else(|_| {
        strptime(s, "%A, %d-%b-%y %T %Z")
    }).or_else(|_| {
        strptime(s, "%c")
    }).ok()
}
<commit_msg>Fixes from_one_comma_delimited, no more split at space<commit_after>//! Utility functions for Header implementations.

use std::str::{FromStr, from_utf8};
use std::fmt::{mod, Show};
use time::{Tm, strptime};

/// Reads a single raw string when parsing a header
pub fn from_one_raw_str<T: FromStr>(raw: &[Vec<u8>]) -> Option<T> {
    if raw.len() != 1 {
        return None;
    }
    // we JUST checked that raw.len() == 1, so raw[0] WILL exist.
    match from_utf8(unsafe { raw[].unsafe_get(0)[] }) {
        Ok(s) => FromStr::from_str(s),
        Err(_) => None
    }
}

/// Reads a comma-delimited raw header into a Vec.
#[inline]
pub fn from_comma_delimited<T: FromStr>(raw: &[Vec<u8>]) -> Option<Vec<T>> {
    if raw.len() != 1 {
        return None;
    }
    // we JUST checked that raw.len() == 1, so raw[0] WILL exist.
    from_one_comma_delimited(unsafe { raw.as_slice().unsafe_get(0).as_slice() })
}

/// Reads a comma-delimited raw string into a Vec.
pub fn from_one_comma_delimited<T: FromStr>(raw: &[u8]) -> Option<Vec<T>> {
    match from_utf8(raw) {
        Ok(s) => {
            Some(s.as_slice()
                 .split(',')
                 .map(|x| x.trim())
                 .filter_map(FromStr::from_str)
                 .collect())
        }
        Err(_) => None
    }
}

/// Format an array into a comma-delimited string.
pub fn fmt_comma_delimited<T: Show>(fmt: &mut fmt::Formatter, parts: &[T]) -> fmt::Result {
    let last = parts.len() - 1;
    for (i, part) in parts.iter().enumerate() {
        try!(part.fmt(fmt));
        if i < last {
            try!(", ".fmt(fmt));
        }
    }
    Ok(())
}

/// Get a Tm from HTTP date formats.
//    Prior to 1995, there were three different formats commonly used by
//   servers to communicate timestamps.  For compatibility with old
//   implementations, all three are defined here.  The preferred format is
//   a fixed-length and single-zone subset of the date and time
//   specification used by the Internet Message Format [RFC5322].
//
//     HTTP-date    = IMF-fixdate / obs-date
//
//   An example of the preferred format is
//
//     Sun, 06 Nov 1994 08:49:37 GMT    ; IMF-fixdate
//
//   Examples of the two obsolete formats are
//
//     Sunday, 06-Nov-94 08:49:37 GMT   ; obsolete RFC 850 format
//     Sun Nov  6 08:49:37 1994         ; ANSI C's asctime() format
//
//   A recipient that parses a timestamp value in an HTTP header field
//   MUST accept all three HTTP-date formats.  When a sender generates a
//   header field that contains one or more timestamps defined as
//   HTTP-date, the sender MUST generate those timestamps in the
//   IMF-fixdate format.
pub fn tm_from_str(s: &str) -> Option<Tm> {
    strptime(s, "%a, %d %b %Y %T %Z").or_else(|_| {
        strptime(s, "%A, %d-%b-%y %T %Z")
    }).or_else(|_| {
        strptime(s, "%c")
    }).ok()
}
<|endoftext|>


<commit_before>use core::ops::{Bound, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo};

// Test the Range structs and syntax.

#[test]
fn test_range() {
    let r = Range { start: 2, end: 10 };
    let mut count = 0;
    for (i, ri) in r.enumerate() {
        assert_eq!(ri, i + 2);
        assert!(ri >= 2 && ri < 10);
        count += 1;
    }
    assert_eq!(count, 8);
}

#[test]
fn test_range_from() {
    let r = RangeFrom { start: 2 };
    let mut count = 0;
    for (i, ri) in r.take(10).enumerate() {
        assert_eq!(ri, i + 2);
        assert!(ri >= 2 && ri < 12);
        count += 1;
    }
    assert_eq!(count, 10);
}

#[test]
fn test_range_to() {
    // Not much to test.
    let _ = RangeTo { end: 42 };
}

#[test]
fn test_full_range() {
    // Not much to test.
    let _ = RangeFull;
}

#[test]
fn test_range_inclusive() {
    let mut r = RangeInclusive::new(1i8, 2);
    assert_eq!(r.next(), Some(1));
    assert_eq!(r.next(), Some(2));
    assert_eq!(r.next(), None);

    r = RangeInclusive::new(127i8, 127);
    assert_eq!(r.next(), Some(127));
    assert_eq!(r.next(), None);

    r = RangeInclusive::new(-128i8, -128);
    assert_eq!(r.next_back(), Some(-128));
    assert_eq!(r.next_back(), None);

    // degenerate
    r = RangeInclusive::new(1, -1);
    assert_eq!(r.size_hint(), (0, Some(0)));
    assert_eq!(r.next(), None);
}

#[test]
fn test_range_is_empty() {
    assert!(!(0.0..10.0).is_empty());
    assert!((-0.0..0.0).is_empty());
    assert!((10.0..0.0).is_empty());

    assert!(!(f32::NEG_INFINITY..f32::INFINITY).is_empty());
    assert!((f32::EPSILON..f32::NAN).is_empty());
    assert!((f32::NAN..f32::EPSILON).is_empty());
    assert!((f32::NAN..f32::NAN).is_empty());

    assert!(!(0.0..=10.0).is_empty());
    assert!(!(-0.0..=0.0).is_empty());
    assert!((10.0..=0.0).is_empty());

    assert!(!(f32::NEG_INFINITY..=f32::INFINITY).is_empty());
    assert!((f32::EPSILON..=f32::NAN).is_empty());
    assert!((f32::NAN..=f32::EPSILON).is_empty());
    assert!((f32::NAN..=f32::NAN).is_empty());
}

#[test]
fn test_bound_cloned_unbounded() {
    assert_eq!(Bound::<&u32>::Unbounded.cloned(), Bound::Unbounded);
}

#[test]
fn test_bound_cloned_included() {
    assert_eq!(Bound::Included(&3).cloned(), Bound::Included(3));
}

#[test]
fn test_bound_cloned_excluded() {
    assert_eq!(Bound::Excluded(&3).cloned(), Bound::Excluded(3));
}

#[test]
#[allow(unused_comparisons)]
#[allow(unused_mut)]
fn test_range_syntax() {
    let mut count = 0;
    for i in 0_usize..10 {
        assert!(i >= 0 && i < 10);
        count += i;
    }
    assert_eq!(count, 45);

    let mut count = 0;
    let mut range = 0_usize..10;
    for i in range {
        assert!(i >= 0 && i < 10);
        count += i;
    }
    assert_eq!(count, 45);

    let mut count = 0;
    let mut rf = 3_usize..;
    for i in rf.take(10) {
        assert!(i >= 3 && i < 13);
        count += i;
    }
    assert_eq!(count, 75);

    let _ = 0_usize..4 + 4 - 3;

    fn foo() -> isize {
        42
    }
    let _ = 0..foo();

    let _ = { &42..&100 }; // references to literals are OK
    let _ = ..42_usize;

    // Test we can use two different types with a common supertype.
    let x = &42;
    {
        let y = 42;
        let _ = x..&y;
    }
}

#[test]
#[allow(dead_code)]
fn test_range_syntax_in_return_statement() {
    fn return_range_to() -> RangeTo<i32> {
        return ..1;
    }
    fn return_full_range() -> RangeFull {
        return ..;
    }
    // Not much to test.
}
<commit_msg>Test structural matching for all range types<commit_after>use core::ops::{Bound, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive};

// Test the Range structs and syntax.

#[test]
fn test_range() {
    let r = Range { start: 2, end: 10 };
    let mut count = 0;
    for (i, ri) in r.enumerate() {
        assert_eq!(ri, i + 2);
        assert!(ri >= 2 && ri < 10);
        count += 1;
    }
    assert_eq!(count, 8);
}

#[test]
fn test_range_from() {
    let r = RangeFrom { start: 2 };
    let mut count = 0;
    for (i, ri) in r.take(10).enumerate() {
        assert_eq!(ri, i + 2);
        assert!(ri >= 2 && ri < 12);
        count += 1;
    }
    assert_eq!(count, 10);
}

#[test]
fn test_range_to() {
    // Not much to test.
    let _ = RangeTo { end: 42 };
}

#[test]
fn test_full_range() {
    // Not much to test.
    let _ = RangeFull;
}

#[test]
fn test_range_inclusive() {
    let mut r = RangeInclusive::new(1i8, 2);
    assert_eq!(r.next(), Some(1));
    assert_eq!(r.next(), Some(2));
    assert_eq!(r.next(), None);

    r = RangeInclusive::new(127i8, 127);
    assert_eq!(r.next(), Some(127));
    assert_eq!(r.next(), None);

    r = RangeInclusive::new(-128i8, -128);
    assert_eq!(r.next_back(), Some(-128));
    assert_eq!(r.next_back(), None);

    // degenerate
    r = RangeInclusive::new(1, -1);
    assert_eq!(r.size_hint(), (0, Some(0)));
    assert_eq!(r.next(), None);
}

#[test]
fn test_range_to_inclusive() {
    // Not much to test.
    let _ = RangeToInclusive { end: 42 };
}

#[test]
fn test_range_is_empty() {
    assert!(!(0.0..10.0).is_empty());
    assert!((-0.0..0.0).is_empty());
    assert!((10.0..0.0).is_empty());

    assert!(!(f32::NEG_INFINITY..f32::INFINITY).is_empty());
    assert!((f32::EPSILON..f32::NAN).is_empty());
    assert!((f32::NAN..f32::EPSILON).is_empty());
    assert!((f32::NAN..f32::NAN).is_empty());

    assert!(!(0.0..=10.0).is_empty());
    assert!(!(-0.0..=0.0).is_empty());
    assert!((10.0..=0.0).is_empty());

    assert!(!(f32::NEG_INFINITY..=f32::INFINITY).is_empty());
    assert!((f32::EPSILON..=f32::NAN).is_empty());
    assert!((f32::NAN..=f32::EPSILON).is_empty());
    assert!((f32::NAN..=f32::NAN).is_empty());
}

#[test]
fn test_bound_cloned_unbounded() {
    assert_eq!(Bound::<&u32>::Unbounded.cloned(), Bound::Unbounded);
}

#[test]
fn test_bound_cloned_included() {
    assert_eq!(Bound::Included(&3).cloned(), Bound::Included(3));
}

#[test]
fn test_bound_cloned_excluded() {
    assert_eq!(Bound::Excluded(&3).cloned(), Bound::Excluded(3));
}

#[test]
#[allow(unused_comparisons)]
#[allow(unused_mut)]
fn test_range_syntax() {
    let mut count = 0;
    for i in 0_usize..10 {
        assert!(i >= 0 && i < 10);
        count += i;
    }
    assert_eq!(count, 45);

    let mut count = 0;
    let mut range = 0_usize..10;
    for i in range {
        assert!(i >= 0 && i < 10);
        count += i;
    }
    assert_eq!(count, 45);

    let mut count = 0;
    let mut rf = 3_usize..;
    for i in rf.take(10) {
        assert!(i >= 3 && i < 13);
        count += i;
    }
    assert_eq!(count, 75);

    let _ = 0_usize..4 + 4 - 3;

    fn foo() -> isize {
        42
    }
    let _ = 0..foo();

    let _ = { &42..&100 }; // references to literals are OK
    let _ = ..42_usize;

    // Test we can use two different types with a common supertype.
    let x = &42;
    {
        let y = 42;
        let _ = x..&y;
    }
}

#[test]
#[allow(dead_code)]
fn test_range_syntax_in_return_statement() {
    fn return_range_to() -> RangeTo<i32> {
        return ..1;
    }
    fn return_full_range() -> RangeFull {
        return ..;
    }
    // Not much to test.
}

#[test]
fn range_structural_match() {
    // test that all range types can be structurally matched upon

    const RANGE: Range<usize> = 0..1000;
    match RANGE {
        RANGE => {}
        _ => unreachable!(),
    }

    const RANGE_FROM: RangeFrom<usize> = 0..;
    match RANGE_FROM {
        RANGE_FROM => {}
        _ => unreachable!(),
    }

    const RANGE_FULL: RangeFull = ..;
    match RANGE_FULL {
        RANGE_FULL => {}
    }

    const RANGE_INCLUSIVE: RangeInclusive<usize> = 0..=999;
    match RANGE_INCLUSIVE {
        RANGE_INCLUSIVE => {}
        _ => unreachable!(),
    }

    const RANGE_TO: RangeTo<usize> = ..1000;
    match RANGE_TO {
        RANGE_TO => {}
        _ => unreachable!(),
    }

    const RANGE_TO_INCLUSIVE: RangeToInclusive<usize> = ..=999;
    match RANGE_TO_INCLUSIVE {
        RANGE_TO_INCLUSIVE => {}
        _ => unreachable!(),
    }
}
<|endoftext|>


<commit_before>//! Standard library macros
//!
//! This module contains a set of macros which are exported from the standard
//! library. Each macro is available for use when linking against the standard
//! library.

#[doc(include = "../../core/src/macros/panic.md")]
#[macro_export]
#[stable(feature = "rust1", since = "1.0.0")]
#[allow_internal_unstable(libstd_sys_internals)]
#[cfg_attr(not(test), rustc_diagnostic_item = "std_panic_macro")]
macro_rules! panic {
    () => ({ $crate::panic!("explicit panic") });
    ($msg:expr $(,)?) => ({ $crate::rt::begin_panic($msg) });
    ($fmt:expr, $($arg:tt)+) => ({
        $crate::rt::begin_panic_fmt(&$crate::format_args!($fmt, $($arg)+))
    });
}

/// Prints to the standard output.
///
/// Equivalent to the [`println!`] macro except that a newline is not printed at
/// the end of the message.
///
/// Note that stdout is frequently line-buffered by default so it may be
/// necessary to use [`io::stdout().flush()`][flush] to ensure the output is emitted
/// immediately.
///
/// Use `print!` only for the primary output of your program. Use
/// [`eprint!`] instead to print error and progress messages.
///
/// [flush]: crate::io::Write::flush
///
/// # Panics
///
/// Panics if writing to `io::stdout()` fails.
///
/// # Examples
///
/// ```
/// use std::io::{self, Write};
///
/// print!("this ");
/// print!("will ");
/// print!("be ");
/// print!("on ");
/// print!("the ");
/// print!("same ");
/// print!("line ");
///
/// io::stdout().flush().unwrap();
///
/// print!("this string has a newline, why not choose println! instead?\n");
///
/// io::stdout().flush().unwrap();
/// ```
#[macro_export]
#[stable(feature = "rust1", since = "1.0.0")]
#[allow_internal_unstable(print_internals)]
macro_rules! print {
    ($($arg:tt)*) => ($crate::io::_print($crate::format_args!($($arg)*)));
}

/// Prints to the standard output, with a newline.
///
/// On all platforms, the newline is the LINE FEED character (`\n`/`U+000A`) alone
/// (no additional CARRIAGE RETURN (`\r`/`U+000D`)).
///
/// Use the [`format!`] syntax to write data to the standard output.
/// See [`std::fmt`] for more information.
///
/// Use `println!` only for the primary output of your program. Use
/// [`eprintln!`] instead to print error and progress messages.
///
/// [`std::fmt`]: crate::fmt
///
/// # Panics
///
/// Panics if writing to [`io::stdout`] fails.
///
/// [`io::stdout`]: crate::io::stdout
///
/// # Examples
///
/// ```
/// println!(); // prints just a newline
/// println!("hello there!");
/// println!("format {} arguments", "some");
/// ```
#[macro_export]
#[stable(feature = "rust1", since = "1.0.0")]
#[allow_internal_unstable(print_internals, format_args_nl)]
macro_rules! println {
    () => ($crate::print!("\n"));
    ($($arg:tt)*) => ({
        $crate::io::_print($crate::format_args_nl!($($arg)*));
    })
}

/// Prints to the standard error.
///
/// Equivalent to the [`print!`] macro, except that output goes to
/// [`io::stderr`] instead of [`io::stdout`]. See [`print!`] for
/// example usage.
///
/// Use `eprint!` only for error and progress messages. Use `print!`
/// instead for the primary output of your program.
///
/// [`io::stderr`]: crate::io::stderr
/// [`io::stdout`]: crate::io::stdout
///
/// # Panics
///
/// Panics if writing to `io::stderr` fails.
///
/// # Examples
///
/// ```
/// eprint!("Error: Could not complete task");
/// ```
#[macro_export]
#[stable(feature = "eprint", since = "1.19.0")]
#[allow_internal_unstable(print_internals)]
macro_rules! eprint {
    ($($arg:tt)*) => ($crate::io::_eprint($crate::format_args!($($arg)*)));
}

/// Prints to the standard error, with a newline.
///
/// Equivalent to the [`println!`] macro, except that output goes to
/// [`io::stderr`] instead of [`io::stdout`]. See [`println!`] for
/// example usage.
///
/// Use `eprintln!` only for error and progress messages. Use `println!`
/// instead for the primary output of your program.
///
/// [`io::stderr`]: crate::io::stderr
/// [`io::stdout`]: crate::io::stdout
///
/// # Panics
///
/// Panics if writing to `io::stderr` fails.
///
/// # Examples
///
/// ```
/// eprintln!("Error: Could not complete task");
/// ```
#[macro_export]
#[stable(feature = "eprint", since = "1.19.0")]
#[allow_internal_unstable(print_internals, format_args_nl)]
macro_rules! eprintln {
    () => ($crate::eprint!("\n"));
    ($($arg:tt)*) => ({
        $crate::io::_eprint($crate::format_args_nl!($($arg)*));
    })
}

/// Prints and returns the value of a given expression for quick and dirty
/// debugging.
///
/// An example:
///
/// ```rust
/// let a = 2;
/// let b = dbg!(a * 2) + 1;
/// //      ^-- prints: [src/main.rs:2] a * 2 = 4
/// assert_eq!(b, 5);
/// ```
///
/// The macro works by using the `Debug` implementation of the type of
/// the given expression to print the value to [stderr] along with the
/// source location of the macro invocation as well as the source code
/// of the expression.
///
/// Invoking the macro on an expression moves and takes ownership of it
/// before returning the evaluated expression unchanged. If the type
/// of the expression does not implement `Copy` and you don't want
/// to give up ownership, you can instead borrow with `dbg!(&expr)`
/// for some expression `expr`.
///
/// The `dbg!` macro works exactly the same in release builds.
/// This is useful when debugging issues that only occur in release
/// builds or when debugging in release mode is significantly faster.
///
/// Note that the macro is intended as a debugging tool and therefore you
/// should avoid having uses of it in version control for long periods.
/// Use cases involving debug output that should be added to version control
/// are better served by macros such as [`debug!`] from the [`log`] crate.
///
/// # Stability
///
/// The exact output printed by this macro should not be relied upon
/// and is subject to future changes.
///
/// # Panics
///
/// Panics if writing to `io::stderr` fails.
///
/// # Further examples
///
/// With a method call:
///
/// ```rust
/// fn foo(n: usize) {
///     if let Some(_) = dbg!(n.checked_sub(4)) {
///         // ...
///     }
/// }
///
/// foo(3)
/// ```
///
/// This prints to [stderr]:
///
/// ```text,ignore
/// [src/main.rs:4] n.checked_sub(4) = None
/// ```
///
/// Naive factorial implementation:
///
/// ```rust
/// fn factorial(n: u32) -> u32 {
///     if dbg!(n <= 1) {
///         dbg!(1)
///     } else {
///         dbg!(n * factorial(n - 1))
///     }
/// }
///
/// dbg!(factorial(4));
/// ```
///
/// This prints to [stderr]:
///
/// ```text,ignore
/// [src/main.rs:3] n <= 1 = false
/// [src/main.rs:3] n <= 1 = false
/// [src/main.rs:3] n <= 1 = false
/// [src/main.rs:3] n <= 1 = true
/// [src/main.rs:4] 1 = 1
/// [src/main.rs:5] n * factorial(n - 1) = 2
/// [src/main.rs:5] n * factorial(n - 1) = 6
/// [src/main.rs:5] n * factorial(n - 1) = 24
/// [src/main.rs:11] factorial(4) = 24
/// ```
///
/// The `dbg!(..)` macro moves the input:
///
/// ```compile_fail
/// /// A wrapper around `usize` which importantly is not Copyable.
/// #[derive(Debug)]
/// struct NoCopy(usize);
///
/// let a = NoCopy(42);
/// let _ = dbg!(a); // <-- `a` is moved here.
/// let _ = dbg!(a); // <-- `a` is moved again; error!
/// ```
///
/// You can also use `dbg!()` without a value to just print the
/// file and line whenever it's reached.
///
/// Finally, if you want to `dbg!(..)` multiple values, it will treat them as
/// a tuple (and return it, too):
///
/// ```
/// assert_eq!(dbg!(1usize, 2u32), (1, 2));
/// ```
///
/// However, a single argument with a trailing comma will still not be treated
/// as a tuple, following the convention of ignoring trailing commas in macro
/// invocations. You can use a 1-tuple directly if you need one:
///
/// ```
/// assert_eq!(1, dbg!(1u32,)); // trailing comma ignored
/// assert_eq!((1,), dbg!((1u32,))); // 1-tuple
/// ```
///
/// [stderr]: https://en.wikipedia.org/wiki/Standard_streams#Standard_error_(stderr)
/// [`debug!`]: https://docs.rs/log/*/log/macro.debug.html
/// [`log`]: https://crates.io/crates/log
#[macro_export]
#[stable(feature = "dbg_macro", since = "1.32.0")]
macro_rules! dbg {
    () => {
        $crate::eprintln!("[{}:{}]", $crate::file!(), $crate::line!());
    };
    ($val:expr $(,)?) => {
        // Use of `match` here is intentional because it affects the lifetimes
        // of temporaries - https://stackoverflow.com/a/48732525/1063961
        match $val {
            tmp => {
                $crate::eprintln!("[{}:{}] {} = {:#?}",
                    $crate::file!(), $crate::line!(), $crate::stringify!($val), &tmp);
                tmp
            }
        }
    };
    ($($val:expr),+ $(,)?) => {
        ($($crate::dbg!($val)),+,)
    };
}

#[cfg(test)]
macro_rules! assert_approx_eq {
    ($a:expr, $b:expr) => {{
        let (a, b) = (&$a, &$b);
        assert!((*a - *b).abs() < 1.0e-6, "{} is not approximately equal to {}", *a, *b);
    }};
}
<commit_msg>Document why cannot use concat! in dbg!<commit_after>//! Standard library macros
//!
//! This module contains a set of macros which are exported from the standard
//! library. Each macro is available for use when linking against the standard
//! library.

#[doc(include = "../../core/src/macros/panic.md")]
#[macro_export]
#[stable(feature = "rust1", since = "1.0.0")]
#[allow_internal_unstable(libstd_sys_internals)]
#[cfg_attr(not(test), rustc_diagnostic_item = "std_panic_macro")]
macro_rules! panic {
    () => ({ $crate::panic!("explicit panic") });
    ($msg:expr $(,)?) => ({ $crate::rt::begin_panic($msg) });
    ($fmt:expr, $($arg:tt)+) => ({
        $crate::rt::begin_panic_fmt(&$crate::format_args!($fmt, $($arg)+))
    });
}

/// Prints to the standard output.
///
/// Equivalent to the [`println!`] macro except that a newline is not printed at
/// the end of the message.
///
/// Note that stdout is frequently line-buffered by default so it may be
/// necessary to use [`io::stdout().flush()`][flush] to ensure the output is emitted
/// immediately.
///
/// Use `print!` only for the primary output of your program. Use
/// [`eprint!`] instead to print error and progress messages.
///
/// [flush]: crate::io::Write::flush
///
/// # Panics
///
/// Panics if writing to `io::stdout()` fails.
///
/// # Examples
///
/// ```
/// use std::io::{self, Write};
///
/// print!("this ");
/// print!("will ");
/// print!("be ");
/// print!("on ");
/// print!("the ");
/// print!("same ");
/// print!("line ");
///
/// io::stdout().flush().unwrap();
///
/// print!("this string has a newline, why not choose println! instead?\n");
///
/// io::stdout().flush().unwrap();
/// ```
#[macro_export]
#[stable(feature = "rust1", since = "1.0.0")]
#[allow_internal_unstable(print_internals)]
macro_rules! print {
    ($($arg:tt)*) => ($crate::io::_print($crate::format_args!($($arg)*)));
}

/// Prints to the standard output, with a newline.
///
/// On all platforms, the newline is the LINE FEED character (`\n`/`U+000A`) alone
/// (no additional CARRIAGE RETURN (`\r`/`U+000D`)).
///
/// Use the [`format!`] syntax to write data to the standard output.
/// See [`std::fmt`] for more information.
///
/// Use `println!` only for the primary output of your program. Use
/// [`eprintln!`] instead to print error and progress messages.
///
/// [`std::fmt`]: crate::fmt
///
/// # Panics
///
/// Panics if writing to [`io::stdout`] fails.
///
/// [`io::stdout`]: crate::io::stdout
///
/// # Examples
///
/// ```
/// println!(); // prints just a newline
/// println!("hello there!");
/// println!("format {} arguments", "some");
/// ```
#[macro_export]
#[stable(feature = "rust1", since = "1.0.0")]
#[allow_internal_unstable(print_internals, format_args_nl)]
macro_rules! println {
    () => ($crate::print!("\n"));
    ($($arg:tt)*) => ({
        $crate::io::_print($crate::format_args_nl!($($arg)*));
    })
}

/// Prints to the standard error.
///
/// Equivalent to the [`print!`] macro, except that output goes to
/// [`io::stderr`] instead of [`io::stdout`]. See [`print!`] for
/// example usage.
///
/// Use `eprint!` only for error and progress messages. Use `print!`
/// instead for the primary output of your program.
///
/// [`io::stderr`]: crate::io::stderr
/// [`io::stdout`]: crate::io::stdout
///
/// # Panics
///
/// Panics if writing to `io::stderr` fails.
///
/// # Examples
///
/// ```
/// eprint!("Error: Could not complete task");
/// ```
#[macro_export]
#[stable(feature = "eprint", since = "1.19.0")]
#[allow_internal_unstable(print_internals)]
macro_rules! eprint {
    ($($arg:tt)*) => ($crate::io::_eprint($crate::format_args!($($arg)*)));
}

/// Prints to the standard error, with a newline.
///
/// Equivalent to the [`println!`] macro, except that output goes to
/// [`io::stderr`] instead of [`io::stdout`]. See [`println!`] for
/// example usage.
///
/// Use `eprintln!` only for error and progress messages. Use `println!`
/// instead for the primary output of your program.
///
/// [`io::stderr`]: crate::io::stderr
/// [`io::stdout`]: crate::io::stdout
///
/// # Panics
///
/// Panics if writing to `io::stderr` fails.
///
/// # Examples
///
/// ```
/// eprintln!("Error: Could not complete task");
/// ```
#[macro_export]
#[stable(feature = "eprint", since = "1.19.0")]
#[allow_internal_unstable(print_internals, format_args_nl)]
macro_rules! eprintln {
    () => ($crate::eprint!("\n"));
    ($($arg:tt)*) => ({
        $crate::io::_eprint($crate::format_args_nl!($($arg)*));
    })
}

/// Prints and returns the value of a given expression for quick and dirty
/// debugging.
///
/// An example:
///
/// ```rust
/// let a = 2;
/// let b = dbg!(a * 2) + 1;
/// //      ^-- prints: [src/main.rs:2] a * 2 = 4
/// assert_eq!(b, 5);
/// ```
///
/// The macro works by using the `Debug` implementation of the type of
/// the given expression to print the value to [stderr] along with the
/// source location of the macro invocation as well as the source code
/// of the expression.
///
/// Invoking the macro on an expression moves and takes ownership of it
/// before returning the evaluated expression unchanged. If the type
/// of the expression does not implement `Copy` and you don't want
/// to give up ownership, you can instead borrow with `dbg!(&expr)`
/// for some expression `expr`.
///
/// The `dbg!` macro works exactly the same in release builds.
/// This is useful when debugging issues that only occur in release
/// builds or when debugging in release mode is significantly faster.
///
/// Note that the macro is intended as a debugging tool and therefore you
/// should avoid having uses of it in version control for long periods.
/// Use cases involving debug output that should be added to version control
/// are better served by macros such as [`debug!`] from the [`log`] crate.
///
/// # Stability
///
/// The exact output printed by this macro should not be relied upon
/// and is subject to future changes.
///
/// # Panics
///
/// Panics if writing to `io::stderr` fails.
///
/// # Further examples
///
/// With a method call:
///
/// ```rust
/// fn foo(n: usize) {
///     if let Some(_) = dbg!(n.checked_sub(4)) {
///         // ...
///     }
/// }
///
/// foo(3)
/// ```
///
/// This prints to [stderr]:
///
/// ```text,ignore
/// [src/main.rs:4] n.checked_sub(4) = None
/// ```
///
/// Naive factorial implementation:
///
/// ```rust
/// fn factorial(n: u32) -> u32 {
///     if dbg!(n <= 1) {
///         dbg!(1)
///     } else {
///         dbg!(n * factorial(n - 1))
///     }
/// }
///
/// dbg!(factorial(4));
/// ```
///
/// This prints to [stderr]:
///
/// ```text,ignore
/// [src/main.rs:3] n <= 1 = false
/// [src/main.rs:3] n <= 1 = false
/// [src/main.rs:3] n <= 1 = false
/// [src/main.rs:3] n <= 1 = true
/// [src/main.rs:4] 1 = 1
/// [src/main.rs:5] n * factorial(n - 1) = 2
/// [src/main.rs:5] n * factorial(n - 1) = 6
/// [src/main.rs:5] n * factorial(n - 1) = 24
/// [src/main.rs:11] factorial(4) = 24
/// ```
///
/// The `dbg!(..)` macro moves the input:
///
/// ```compile_fail
/// /// A wrapper around `usize` which importantly is not Copyable.
/// #[derive(Debug)]
/// struct NoCopy(usize);
///
/// let a = NoCopy(42);
/// let _ = dbg!(a); // <-- `a` is moved here.
/// let _ = dbg!(a); // <-- `a` is moved again; error!
/// ```
///
/// You can also use `dbg!()` without a value to just print the
/// file and line whenever it's reached.
///
/// Finally, if you want to `dbg!(..)` multiple values, it will treat them as
/// a tuple (and return it, too):
///
/// ```
/// assert_eq!(dbg!(1usize, 2u32), (1, 2));
/// ```
///
/// However, a single argument with a trailing comma will still not be treated
/// as a tuple, following the convention of ignoring trailing commas in macro
/// invocations. You can use a 1-tuple directly if you need one:
///
/// ```
/// assert_eq!(1, dbg!(1u32,)); // trailing comma ignored
/// assert_eq!((1,), dbg!((1u32,))); // 1-tuple
/// ```
///
/// [stderr]: https://en.wikipedia.org/wiki/Standard_streams#Standard_error_(stderr)
/// [`debug!`]: https://docs.rs/log/*/log/macro.debug.html
/// [`log`]: https://crates.io/crates/log
#[macro_export]
#[stable(feature = "dbg_macro", since = "1.32.0")]
macro_rules! dbg {
    // NOTE: We cannot use `concat!` to make a static string as a format argument
    // of `eprintln!` because `file!` could contain a `{` or
    // `$val` expression could be a block (`{ .. }`), in which case the `eprintln!`
    // will be malformed.
    () => {
        $crate::eprintln!("[{}:{}]", $crate::file!(), $crate::line!());
    };
    ($val:expr $(,)?) => {
        // Use of `match` here is intentional because it affects the lifetimes
        // of temporaries - https://stackoverflow.com/a/48732525/1063961
        match $val {
            tmp => {
                $crate::eprintln!("[{}:{}] {} = {:#?}",
                    $crate::file!(), $crate::line!(), $crate::stringify!($val), &tmp);
                tmp
            }
        }
    };
    ($($val:expr),+ $(,)?) => {
        ($($crate::dbg!($val)),+,)
    };
}

#[cfg(test)]
macro_rules! assert_approx_eq {
    ($a:expr, $b:expr) => {{
        let (a, b) = (&$a, &$b);
        assert!((*a - *b).abs() < 1.0e-6, "{} is not approximately equal to {}", *a, *b);
    }};
}
<|endoftext|>


<commit_before>mod add;
mod split;

use crate::config::Config;
use crate::err;
use crate::err::{Error, Result};
use crate::series::config::SeriesConfig;
use crate::series::SeriesData;
use crate::series::{LoadedSeries, SeriesPath};
use crate::tui::component::{Component, Draw};
use crate::tui::widget_util::{block, text};
use crate::tui::UIState;
use add::AddPanel;
use anime::local::{CategorizedEpisodes, SortedEpisodes};
use anime::remote::{Remote, RemoteService, SeriesInfo as RemoteInfo};
use anime::SeriesKind;
use snafu::ResultExt;
use split::{SplitPanel, SplitResult};
use std::borrow::Cow;
use std::mem;
use std::path::PathBuf;
use std::thread;
use std::time::Duration;
use std::{fs, io};
use termion::event::Key;
use tui::backend::Backend;
use tui::layout::{Alignment, Constraint, Direction, Layout, Rect};
use tui::terminal::Frame;
use tui::widgets::Paragraph;

pub struct SplitSeriesPanel {
    state: PanelState,
}

impl SplitSeriesPanel {
    pub fn new() -> Self {
        Self {
            state: PanelState::Loading,
        }
    }

    fn draw_loading_panel<B>(rect: Rect, frame: &mut Frame<B>)
    where
        B: Backend,
    {
        let outline = block::with_borders("Split Series");
        frame.render_widget(outline, rect);

        let layout = Layout::default()
            .direction(Direction::Vertical)
            .constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
            .margin(1)
            .split(rect);

        let text = [text::bold("Loading..")];
        let widget = Paragraph::new(text.iter())
            .alignment(Alignment::Center)
            .wrap(false);
        frame.render_widget(widget, layout[1]);
    }
}

impl Component for SplitSeriesPanel {
    type State = UIState;
    type KeyResult = Result<SplitPanelResult>;

    fn tick(&mut self, state: &mut UIState) -> Result<()> {
        match &mut self.state {
            PanelState::Loading => {
                let series = match state.series.selected() {
                    Some(LoadedSeries::Complete(series)) => &series.data,
                    Some(LoadedSeries::Partial(data, _)) => data,
                    Some(LoadedSeries::None(_, _)) | None => {
                        return Err(Error::CannotSplitErrorSeries)
                    }
                };

                let merged_series =
                    match MergedSeries::resolve(series, &state.remote, &state.config) {
                        Ok(merged) => merged,
                        Err(err) => return Err(err),
                    };

                self.state = PanelState::Splitting(SplitPanel::new(merged_series));
                Ok(())
            }
            PanelState::Splitting(split_panel) => split_panel.tick(state),
            PanelState::AddingSeries(add_panel, _) => add_panel.tick(state),
        }
    }

    fn process_key(&mut self, key: Key, state: &mut Self::State) -> Self::KeyResult {
        match &mut self.state {
            PanelState::Loading => Ok(SplitPanelResult::Ok),
            PanelState::Splitting(split_panel) => match split_panel.process_key(key, state) {
                Ok(SplitResult::Ok) => Ok(SplitPanelResult::Ok),
                Ok(SplitResult::Reset) => Ok(SplitPanelResult::Reset),
                Ok(SplitResult::AddSeries(info, path)) => {
                    let add_panel = AddPanel::new(info, path);
                    let split_panel = mem::take(split_panel);

                    self.state = PanelState::AddingSeries(add_panel, split_panel);

                    Ok(SplitPanelResult::Ok)
                }
                Err(err) => Err(err),
            },
            PanelState::AddingSeries(add_panel, split_panel) => {
                match add_panel.process_key(key, state) {
                    Ok(result @ SplitPanelResult::Reset)
                    | Ok(result @ SplitPanelResult::AddSeries(_, _)) => {
                        let split_panel = mem::take(split_panel);
                        self.state = PanelState::Splitting(split_panel);

                        match result {
                            SplitPanelResult::Reset => Ok(SplitPanelResult::Ok),
                            other => Ok(other),
                        }
                    }
                    other => other,
                }
            }
        }
    }
}

impl<B> Draw<B> for SplitSeriesPanel
where
    B: Backend,
{
    type State = ();

    fn draw(&mut self, _: &Self::State, rect: Rect, frame: &mut Frame<B>) {
        match &mut self.state {
            PanelState::Loading => Self::draw_loading_panel(rect, frame),
            PanelState::Splitting(split_panel) => split_panel.draw(&(), rect, frame),
            PanelState::AddingSeries(add_panel, _) => add_panel.draw(&(), rect, frame),
        }
    }
}

enum PanelState {
    Loading,
    Splitting(SplitPanel),
    AddingSeries(AddPanel, SplitPanel),
}

pub enum SplitPanelResult {
    Ok,
    Reset,
    AddSeries(Box<RemoteInfo>, Box<SeriesConfig>),
}

impl SplitPanelResult {
    #[inline(always)]
    fn add_series(info: RemoteInfo, sconfig: SeriesConfig) -> Self {
        Self::AddSeries(Box::new(info), Box::new(sconfig))
    }
}

#[derive(Debug)]
enum MergedSeries {
    Resolved(ResolvedSeries),
    Failed(SeriesKind),
}

impl MergedSeries {
    fn resolve(data: &SeriesData, remote: &Remote, config: &Config) -> Result<Vec<Self>> {
        let episodes = CategorizedEpisodes::parse(
            data.config.path.absolute(config),
            &data.config.episode_parser,
        )?;

        if !episodes.has_multiple_categories() {
            return Ok(Vec::new());
        }

        let base_info = remote.search_info_by_id(data.info.id as u32)?;

        if base_info.sequels.is_empty() {
            return Ok(Vec::new());
        }

        let mut results = Vec::with_capacity(1);

        for (cat, eps) in episodes.iter() {
            let sequel = match base_info.sequel_by_kind(*cat) {
                Some(sequel) => sequel,
                None => continue,
            };

            // Seasons need special handling as they can have several merged together
            if let SeriesKind::Season = sequel.kind {
                Self::resolve_merged_season(
                    &base_info,
                    &data.config.path,
                    remote,
                    eps,
                    config,
                    &mut results,
                );

                continue;
            }

            thread::sleep(Duration::from_millis(250));

            let sequel_info = match remote.search_info_by_id(sequel.id) {
                Ok(info) => info,
                Err(_) => {
                    results.push(Self::Failed(sequel.kind));
                    continue;
                }
            };

            let resolved =
                ResolvedSeries::new(sequel_info, data.config.path.clone(), eps, 0, config);

            results.push(Self::Resolved(resolved));
        }

        Ok(results)
    }

    fn resolve_merged_season(
        base_info: &RemoteInfo,
        base_path: &SeriesPath,
        remote: &Remote,
        episodes: &SortedEpisodes,
        config: &Config,
        results: &mut Vec<Self>,
    ) {
        let last_episode_num = episodes.last_episode_number();
        let mut info = Cow::Borrowed(base_info);

        // Exit early if we don't have enough episodes locally to have any merged seasons
        if info.episodes > last_episode_num {
            return;
        }

        let mut episode_offset = 0;

        while let Some(sequel) = info.direct_sequel() {
            info = match remote.search_info_by_id(sequel.id) {
                Ok(info) => info.into(),
                Err(_) => {
                    results.push(Self::Failed(sequel.kind));
                    continue;
                }
            };

            episode_offset += info.episodes;

            // Exit early if we don't have enough episodes locally to have another merged season
            if episode_offset > last_episode_num {
                break;
            }

            let resolved = ResolvedSeries::new(
                info.clone().into_owned(),
                base_path.clone(),
                episodes,
                episode_offset,
                config,
            );

            results.push(Self::Resolved(resolved));

            // We don't need to sleep if there isn't another sequel
            if info.sequels.is_empty() {
                break;
            }

            thread::sleep(Duration::from_millis(250));
        }
    }

    fn split_all(merged: &[Self], config: &Config) -> Result<()> {
        for series in merged {
            let series = match series {
                Self::Resolved(series) => series,
                Self::Failed(_) => continue,
            };

            series.perform_split_actions(config)?;
        }

        Ok(())
    }
}

pub type EpisodeOffset = u32;

#[derive(Debug)]
struct ResolvedSeries {
    info: RemoteInfo,
    base_dir: SeriesPath,
    out_dir: SeriesPath,
    offset: EpisodeOffset,
    actions: Vec<SplitAction>,
}

impl ResolvedSeries {
    fn new(
        info: RemoteInfo,
        base_dir: SeriesPath,
        episodes: &SortedEpisodes,
        offset: EpisodeOffset,
        config: &Config,
    ) -> Self {
        let actions = SplitAction::from_merged_seasons(&info, episodes, offset);
        let out_dir = PathBuf::from(&info.title.preferred);
        let out_dir = SeriesPath::new(out_dir, config);

        Self {
            info,
            base_dir,
            out_dir,
            offset,
            actions,
        }
    }

    fn perform_split_actions(&self, config: &Config) -> Result<()> {
        use std::os::unix::fs::symlink;

        if self.actions.is_empty() {
            return Ok(());
        }

        let base_dir = self.base_dir.absolute(config);

        if !base_dir.exists() {
            fs::create_dir_all(&base_dir).context(err::FileIO {
                path: base_dir.to_owned(),
            })?;
        }

        let out_dir = self.out_dir.absolute(config);

        if !out_dir.exists() {
            fs::create_dir_all(&out_dir).context(err::FileIO {
                path: out_dir.to_owned(),
            })?;
        }

        for action in &self.actions {
            let from_path = base_dir.join(&action.old_name);
            let to_path = out_dir.join(&action.new_name);

            if let Err(err) = symlink(&from_path, &to_path) {
                if err.kind() == io::ErrorKind::AlreadyExists {
                    continue;
                }

                return Err(Error::FileLinkFailed {
                    source: err,
                    from: from_path,
                    to: to_path,
                });
            }
        }

        Ok(())
    }
}

#[derive(Debug)]
struct SplitAction {
    old_name: String,
    new_name: String,
}

impl SplitAction {
    fn new<S, N>(old_name: S, new_name: N) -> Self
    where
        S: Into<String>,
        N: Into<String>,
    {
        Self {
            old_name: old_name.into(),
            new_name: new_name.into(),
        }
    }

    fn from_merged_seasons(
        info: &RemoteInfo,
        episodes: &SortedEpisodes,
        offset: EpisodeOffset,
    ) -> Vec<Self> {
        let mut actions = Vec::new();

        let sequel_start = 1 + offset;
        let sequel_end = offset + info.episodes as EpisodeOffset;

        for real_ep_num in sequel_start..=sequel_end {
            let episode = match episodes.find(real_ep_num) {
                Some(episode) => episode,
                None => continue,
            };

            let extension = PathBuf::from(&episode.filename)
                .extension()
                .map(|e| format!(".{}", e.to_string_lossy()).into())
                .unwrap_or_else(|| Cow::Borrowed(""));

            let new_filename = format!(
                "{} - {:02}{}",
                info.title.preferred,
                real_ep_num - offset,
                extension
            );

            let action = Self::new(&episode.filename, new_filename);

            actions.push(action);
        }

        actions
    }
}
<commit_msg>anup: Remove unnecessary episode category check<commit_after>mod add;
mod split;

use crate::config::Config;
use crate::err;
use crate::err::{Error, Result};
use crate::series::config::SeriesConfig;
use crate::series::SeriesData;
use crate::series::{LoadedSeries, SeriesPath};
use crate::tui::component::{Component, Draw};
use crate::tui::widget_util::{block, text};
use crate::tui::UIState;
use add::AddPanel;
use anime::local::{CategorizedEpisodes, SortedEpisodes};
use anime::remote::{Remote, RemoteService, SeriesInfo as RemoteInfo};
use anime::SeriesKind;
use snafu::ResultExt;
use split::{SplitPanel, SplitResult};
use std::borrow::Cow;
use std::mem;
use std::path::PathBuf;
use std::thread;
use std::time::Duration;
use std::{fs, io};
use termion::event::Key;
use tui::backend::Backend;
use tui::layout::{Alignment, Constraint, Direction, Layout, Rect};
use tui::terminal::Frame;
use tui::widgets::Paragraph;

pub struct SplitSeriesPanel {
    state: PanelState,
}

impl SplitSeriesPanel {
    pub fn new() -> Self {
        Self {
            state: PanelState::Loading,
        }
    }

    fn draw_loading_panel<B>(rect: Rect, frame: &mut Frame<B>)
    where
        B: Backend,
    {
        let outline = block::with_borders("Split Series");
        frame.render_widget(outline, rect);

        let layout = Layout::default()
            .direction(Direction::Vertical)
            .constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
            .margin(1)
            .split(rect);

        let text = [text::bold("Loading..")];
        let widget = Paragraph::new(text.iter())
            .alignment(Alignment::Center)
            .wrap(false);
        frame.render_widget(widget, layout[1]);
    }
}

impl Component for SplitSeriesPanel {
    type State = UIState;
    type KeyResult = Result<SplitPanelResult>;

    fn tick(&mut self, state: &mut UIState) -> Result<()> {
        match &mut self.state {
            PanelState::Loading => {
                let series = match state.series.selected() {
                    Some(LoadedSeries::Complete(series)) => &series.data,
                    Some(LoadedSeries::Partial(data, _)) => data,
                    Some(LoadedSeries::None(_, _)) | None => {
                        return Err(Error::CannotSplitErrorSeries)
                    }
                };

                let merged_series =
                    match MergedSeries::resolve(series, &state.remote, &state.config) {
                        Ok(merged) => merged,
                        Err(err) => return Err(err),
                    };

                self.state = PanelState::Splitting(SplitPanel::new(merged_series));
                Ok(())
            }
            PanelState::Splitting(split_panel) => split_panel.tick(state),
            PanelState::AddingSeries(add_panel, _) => add_panel.tick(state),
        }
    }

    fn process_key(&mut self, key: Key, state: &mut Self::State) -> Self::KeyResult {
        match &mut self.state {
            PanelState::Loading => Ok(SplitPanelResult::Ok),
            PanelState::Splitting(split_panel) => match split_panel.process_key(key, state) {
                Ok(SplitResult::Ok) => Ok(SplitPanelResult::Ok),
                Ok(SplitResult::Reset) => Ok(SplitPanelResult::Reset),
                Ok(SplitResult::AddSeries(info, path)) => {
                    let add_panel = AddPanel::new(info, path);
                    let split_panel = mem::take(split_panel);

                    self.state = PanelState::AddingSeries(add_panel, split_panel);

                    Ok(SplitPanelResult::Ok)
                }
                Err(err) => Err(err),
            },
            PanelState::AddingSeries(add_panel, split_panel) => {
                match add_panel.process_key(key, state) {
                    Ok(result @ SplitPanelResult::Reset)
                    | Ok(result @ SplitPanelResult::AddSeries(_, _)) => {
                        let split_panel = mem::take(split_panel);
                        self.state = PanelState::Splitting(split_panel);

                        match result {
                            SplitPanelResult::Reset => Ok(SplitPanelResult::Ok),
                            other => Ok(other),
                        }
                    }
                    other => other,
                }
            }
        }
    }
}

impl<B> Draw<B> for SplitSeriesPanel
where
    B: Backend,
{
    type State = ();

    fn draw(&mut self, _: &Self::State, rect: Rect, frame: &mut Frame<B>) {
        match &mut self.state {
            PanelState::Loading => Self::draw_loading_panel(rect, frame),
            PanelState::Splitting(split_panel) => split_panel.draw(&(), rect, frame),
            PanelState::AddingSeries(add_panel, _) => add_panel.draw(&(), rect, frame),
        }
    }
}

enum PanelState {
    Loading,
    Splitting(SplitPanel),
    AddingSeries(AddPanel, SplitPanel),
}

pub enum SplitPanelResult {
    Ok,
    Reset,
    AddSeries(Box<RemoteInfo>, Box<SeriesConfig>),
}

impl SplitPanelResult {
    #[inline(always)]
    fn add_series(info: RemoteInfo, sconfig: SeriesConfig) -> Self {
        Self::AddSeries(Box::new(info), Box::new(sconfig))
    }
}

#[derive(Debug)]
enum MergedSeries {
    Resolved(ResolvedSeries),
    Failed(SeriesKind),
}

impl MergedSeries {
    fn resolve(data: &SeriesData, remote: &Remote, config: &Config) -> Result<Vec<Self>> {
        let episodes = CategorizedEpisodes::parse(
            data.config.path.absolute(config),
            &data.config.episode_parser,
        )?;

        let base_info = remote.search_info_by_id(data.info.id as u32)?;

        if base_info.sequels.is_empty() {
            return Ok(Vec::new());
        }

        let mut results = Vec::with_capacity(1);

        for (cat, eps) in episodes.iter() {
            let sequel = match base_info.sequel_by_kind(*cat) {
                Some(sequel) => sequel,
                None => continue,
            };

            // Seasons need special handling as they can have several merged together
            if let SeriesKind::Season = sequel.kind {
                Self::resolve_merged_season(
                    &base_info,
                    &data.config.path,
                    remote,
                    eps,
                    config,
                    &mut results,
                );

                continue;
            }

            thread::sleep(Duration::from_millis(250));

            let sequel_info = match remote.search_info_by_id(sequel.id) {
                Ok(info) => info,
                Err(_) => {
                    results.push(Self::Failed(sequel.kind));
                    continue;
                }
            };

            let resolved =
                ResolvedSeries::new(sequel_info, data.config.path.clone(), eps, 0, config);

            results.push(Self::Resolved(resolved));
        }

        Ok(results)
    }

    fn resolve_merged_season(
        base_info: &RemoteInfo,
        base_path: &SeriesPath,
        remote: &Remote,
        episodes: &SortedEpisodes,
        config: &Config,
        results: &mut Vec<Self>,
    ) {
        let last_episode_num = episodes.last_episode_number();
        let mut info = Cow::Borrowed(base_info);

        // Exit early if we don't have enough episodes locally to have any merged seasons
        if info.episodes > last_episode_num {
            return;
        }

        let mut episode_offset = 0;

        while let Some(sequel) = info.direct_sequel() {
            info = match remote.search_info_by_id(sequel.id) {
                Ok(info) => info.into(),
                Err(_) => {
                    results.push(Self::Failed(sequel.kind));
                    continue;
                }
            };

            episode_offset += info.episodes;

            // Exit early if we don't have enough episodes locally to have another merged season
            if episode_offset > last_episode_num {
                break;
            }

            let resolved = ResolvedSeries::new(
                info.clone().into_owned(),
                base_path.clone(),
                episodes,
                episode_offset,
                config,
            );

            results.push(Self::Resolved(resolved));

            // We don't need to sleep if there isn't another sequel
            if info.sequels.is_empty() {
                break;
            }

            thread::sleep(Duration::from_millis(250));
        }
    }

    fn split_all(merged: &[Self], config: &Config) -> Result<()> {
        for series in merged {
            let series = match series {
                Self::Resolved(series) => series,
                Self::Failed(_) => continue,
            };

            series.perform_split_actions(config)?;
        }

        Ok(())
    }
}

pub type EpisodeOffset = u32;

#[derive(Debug)]
struct ResolvedSeries {
    info: RemoteInfo,
    base_dir: SeriesPath,
    out_dir: SeriesPath,
    offset: EpisodeOffset,
    actions: Vec<SplitAction>,
}

impl ResolvedSeries {
    fn new(
        info: RemoteInfo,
        base_dir: SeriesPath,
        episodes: &SortedEpisodes,
        offset: EpisodeOffset,
        config: &Config,
    ) -> Self {
        let actions = SplitAction::from_merged_seasons(&info, episodes, offset);
        let out_dir = PathBuf::from(&info.title.preferred);
        let out_dir = SeriesPath::new(out_dir, config);

        Self {
            info,
            base_dir,
            out_dir,
            offset,
            actions,
        }
    }

    fn perform_split_actions(&self, config: &Config) -> Result<()> {
        use std::os::unix::fs::symlink;

        if self.actions.is_empty() {
            return Ok(());
        }

        let base_dir = self.base_dir.absolute(config);

        if !base_dir.exists() {
            fs::create_dir_all(&base_dir).context(err::FileIO {
                path: base_dir.to_owned(),
            })?;
        }

        let out_dir = self.out_dir.absolute(config);

        if !out_dir.exists() {
            fs::create_dir_all(&out_dir).context(err::FileIO {
                path: out_dir.to_owned(),
            })?;
        }

        for action in &self.actions {
            let from_path = base_dir.join(&action.old_name);
            let to_path = out_dir.join(&action.new_name);

            if let Err(err) = symlink(&from_path, &to_path) {
                if err.kind() == io::ErrorKind::AlreadyExists {
                    continue;
                }

                return Err(Error::FileLinkFailed {
                    source: err,
                    from: from_path,
                    to: to_path,
                });
            }
        }

        Ok(())
    }
}

#[derive(Debug)]
struct SplitAction {
    old_name: String,
    new_name: String,
}

impl SplitAction {
    fn new<S, N>(old_name: S, new_name: N) -> Self
    where
        S: Into<String>,
        N: Into<String>,
    {
        Self {
            old_name: old_name.into(),
            new_name: new_name.into(),
        }
    }

    fn from_merged_seasons(
        info: &RemoteInfo,
        episodes: &SortedEpisodes,
        offset: EpisodeOffset,
    ) -> Vec<Self> {
        let mut actions = Vec::new();

        let sequel_start = 1 + offset;
        let sequel_end = offset + info.episodes as EpisodeOffset;

        for real_ep_num in sequel_start..=sequel_end {
            let episode = match episodes.find(real_ep_num) {
                Some(episode) => episode,
                None => continue,
            };

            let extension = PathBuf::from(&episode.filename)
                .extension()
                .map(|e| format!(".{}", e.to_string_lossy()).into())
                .unwrap_or_else(|| Cow::Borrowed(""));

            let new_filename = format!(
                "{} - {:02}{}",
                info.title.preferred,
                real_ep_num - offset,
                extension
            );

            let action = Self::new(&episode.filename, new_filename);

            actions.push(action);
        }

        actions
    }
}
<|endoftext|>


<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.

use crate::lexer::Lexer;
use crate::lexer::TokenPosition;
use crate::parser::Parser;
use crate::rename::rename;
use crate::server::LspSender;
use crate::server::Message;
use crate::server::Read;
use crate::server::Request;
use crate::server::Server;
use crate::server::Write;
use crate::source_map::SourceMap;
use lsp_types::Diagnostic;
use lsp_types::DiagnosticSeverity;
use lsp_types::DidChangeTextDocumentParams;
use lsp_types::DidOpenTextDocumentParams;
use lsp_types::DocumentHighlight;
use lsp_types::DocumentHighlightParams;
use lsp_types::Position;
use lsp_types::PublishDiagnosticsParams;
use lsp_types::Range;
use lsp_types::RenameParams;
use lsp_types::Url;
use lsp_types::WorkspaceEdit;
use serde_json::json;
use std::collections::HashMap;

/// Runs the main loop of the LSP server.
///
/// This method finishes when `exit` notification is received.
pub fn run<R: Read, W: Write + Send + 'static>(server: Server<R, W>) {
    let mut state = State {
        source_map: SourceMap::new(),
        sender: server.sender(),
    };
    for msg in server {
        state.handle_message(msg);
    }
}

struct State {
    source_map: SourceMap,
    sender: LspSender,
}

fn token_position_to_range(position: &TokenPosition) -> Range {
    Range {
        start: Position {
            line: position.start.line as u64,
            character: position.start.character as u64,
        },
        end: Position {
            line: position.end.line as u64,
            character: position.end.character as u64,
        },
    }
}

impl State {
    fn handle_message(&mut self, msg: Message) {
        match msg {
            Message::Request(req) => match req.method.as_ref() {
                "initialize" => {
                    req.response_handle.respond(Ok(json!({"capabilities": {
                        "renameProvider": true,
                        "documentHighlightProvider": true,
                    }})));
                }
                "textDocument/rename" => {
                    self.handle_rename(req);
                }
                "textDocument/documentHighlight" => {
                    self.handle_document_highlight(req);
                }
                method => {
                    eprintln!("Unrecognized request: {}", method);
                }
            },
            Message::Notification(notification) => match notification.method.as_ref() {
                "initialized" => {}
                "textDocument/didOpen" => {
                    let params: DidOpenTextDocumentParams =
                        serde_json::from_value(notification.params.clone()).unwrap();
                    self.handle_did_open(params);
                }
                "textDocument/didChange" => {
                    let params: DidChangeTextDocumentParams =
                        serde_json::from_value(notification.params.clone()).unwrap();
                    self.handle_did_change(params);
                }
                method => {
                    eprintln!("Unrecognized notification: {}", method);
                }
            },
        }
    }

    fn handle_did_open(&mut self, params: DidOpenTextDocumentParams) {
        self.source_map.add(
            &params.text_document.uri,
            params.text_document.text.to_string(),
        );

        publish_diagnostics(
            &params.text_document.text,
            params.text_document.uri,
            &self.sender,
        );
    }

    fn handle_did_change(&mut self, params: DidChangeTextDocumentParams) {
        // TODO: Add support for partial content changes
        if params.content_changes.len() != 1 {
            panic!("unsupported not one content changes");
        }
        if !params.content_changes[0].range.is_none() {
            panic!("unsupported partial content change");
        }
        self.source_map.add(
            &params.text_document.uri,
            params.content_changes[0].text.to_string(),
        );
        publish_diagnostics(
            &params.content_changes[0].text,
            params.text_document.uri,
            &self.sender,
        );
    }

    fn handle_rename(&self, req: Request) {
        // TODO: This doesn't work yet, it is still WIP!
        let params: RenameParams = serde_json::from_value(req.params.clone()).unwrap();
        let content = self
            .source_map
            .get_content(&params.text_document_position.text_document.uri)
            .unwrap();
        let edits = rename(
            &content,
            params.text_document_position.position,
            &params.new_name,
        )
        .unwrap();
        let mut changes = HashMap::new();
        changes.insert(params.text_document_position.text_document.uri, edits);
        req.response_handle
            .respond(Ok(serde_json::to_value(WorkspaceEdit {
                changes: Some(changes),
                document_changes: None,
            })
            .unwrap()))
    }

    fn handle_document_highlight(&self, req: Request) {
        // TODO: This doesn't work yet, it is still WIP!
        let params: DocumentHighlightParams = serde_json::from_value(req.params.clone()).unwrap();
        let content = self
            .source_map
            .get_content(&params.text_document_position_params.text_document.uri)
            .unwrap();

        let mut parser = Parser::new(Lexer::new(&content));
        let _program = parser.parse();

        let start = params.text_document_position_params.position;
        let mut end = params.text_document_position_params.position;
        end.character += 2;
        req.response_handle
            .respond(Ok(serde_json::to_value(vec![DocumentHighlight {
                kind: None,
                range: Range {
                    start: start,
                    end: end,
                },
            }])
            .unwrap()))
    }
}

fn publish_diagnostics(text: &str, uri: Url, sender: &LspSender) {
    let mut parser = Parser::new(Lexer::new(text));
    parser.parse();
    let mut diagnostics_params = PublishDiagnosticsParams {
        uri: uri,
        diagnostics: Vec::new(),
        version: None,
    };
    for error in parser.errors {
        diagnostics_params.diagnostics.push(Diagnostic {
            range: token_position_to_range(&error.position),
            message: error.message,
            code: None,
            related_information: None,
            severity: Some(DiagnosticSeverity::Error),
            source: None,
            tags: None,
        });
    }
    sender.send_notification(
        "textDocument/publishDiagnostics",
        serde_json::to_value(diagnostics_params).unwrap(),
    );
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::io;
    use std::sync::mpsc::channel;
    use std::sync::mpsc::Receiver;
    use std::sync::mpsc::Sender;

    struct FakeReader {
        receiver: Receiver<String>,
    }

    impl FakeReader {
        fn new() -> (Sender<String>, FakeReader) {
            let (sender, receiver) = channel();
            return (sender, FakeReader { receiver: receiver });
        }
    }

    impl Read for FakeReader {
        fn read_packet(&mut self) -> Result<String, io::Error> {
            self.receiver
                .recv()
                .map_err(|_| io::Error::new(io::ErrorKind::UnexpectedEof, "EOF encountered"))
        }
    }
    struct FakeWriter {
        sender: Sender<String>,
    }

    impl FakeWriter {
        fn new() -> (Receiver<String>, FakeWriter) {
            let (sender, receiver) = channel();
            return (receiver, FakeWriter { sender: sender });
        }
    }

    impl Write for FakeWriter {
        fn write_packet(&self, packet: String) -> Result<(), io::Error> {
            self.sender.send(packet).unwrap();
            Ok(())
        }
    }

    struct Client {
        sender: Sender<String>,
        receiver: Receiver<String>,
    }

    impl Client {
        fn recv(&self) -> Result<serde_json::Value, ()> {
            Ok(self.receiver.recv().unwrap().parse().unwrap())
        }
        fn send(&self, req: serde_json::Value) -> Result<(), ()> {
            self.sender.send(req.to_string()).unwrap();
            Ok(())
        }
    }

    fn create_client_and_server() -> (Client, Server<FakeReader, FakeWriter>) {
        let (writer_ch, writer) = FakeWriter::new();
        let (reader_ch, reader) = FakeReader::new();
        let client = Client {
            sender: reader_ch,
            receiver: writer_ch,
        };
        let server = Server::new(reader, writer);
        return (client, server);
    }

    #[test]
    fn responds_to_initialize() {
        let (client, server) = create_client_and_server();
        let t = std::thread::spawn(move || {
            run(server);
        });

        client
            .send(json!({
                "jsonrpc": "2.0",
                "id": 1,
                "method": "initialize",
                "params": {
                    "processId": serde_json::Value::Null,
                    "rootUri": serde_json::Value::Null,
                    "capabilities": {
                    },
                },
            }))
            .unwrap();
        client.recv().unwrap();
        client
            .send(json!({
                "jsonrpc": "2.0",
                "method": "exit",
            }))
            .unwrap();

        t.join().unwrap();
    }

    #[test]
    // TODO: document highlights do not work yet, we need to add following capabilities first:
    // - add Span to Stmt and Expr
    // - find Stmt/Expr by Position
    // TODO: similar tests that should be added
    // - if cursor is not on the variable, do not return highlight
    // - do not highlight if there is only one variable
    #[ignore]
    fn document_hightlight_highlights_the_same_variable() {
        // TODO: This has to be refactor to make writing tests easy.
        let (client, server) = create_client_and_server();
        let t = std::thread::spawn(move || {
            run(server);
        });

        // Initialize
        client
            .send(json!({
                "jsonrpc": "2.0",
                "id": 1,
                "method": "initialize",
                "params": {
                    "processId": serde_json::Value::Null,
                    "rootUri": serde_json::Value::Null,
                    "capabilities": {
                    },
                },
            }))
            .unwrap();
        // Receive initialized
        // TODO: verify this.
        client.recv().unwrap();

        // Open document (notification)
        client
            .send(json!({
                "jsonrpc": "2.0",
                "id": 1,
                "method": "textDocument/didOpen",
                "params": {
                    "textDocument": {
                        "uri": "file:///home/user/test.vim",
                        "languageId": "vim",
                        "version": 1,
                        "text": "let myvar = 1\nlet myvar = 2\n",
                    },
                },
            }))
            .unwrap();

        // Request hightlights
        client
            .send(json!({
                "jsonrpc": "2.0",
                "id": 1,
                "method": "textDocument/documentHighlight",
                "params": {
                    "textDocument": {
                        "uri": "file:///home/user/test.vim",
                    },
                    "position": {
                        "line": 0,
                        "character": 5,
                    },
                },
            }))
            .unwrap();
        let response = client.recv().unwrap();
        let result = response.get("result").unwrap().clone();
        let x: Vec<DocumentHighlight> = serde_json::from_value(result).unwrap();
        assert_eq!(
            x,
            vec![DocumentHighlight {
                kind: None,
                range: Range {
                    start: Position {
                        line: 0,
                        character: 4,
                    },
                    end: Position {
                        line: 0,
                        character: 9,
                    },
                },
            }]
        );

        // Exit
        client
            .send(json!({
                "jsonrpc": "2.0",
                "method": "exit",
            }))
            .unwrap();

        t.join().unwrap();
    }
}
<commit_msg>Highlight the token under cursor<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.

use crate::lexer::Lexer;
use crate::lexer::TokenPosition;
use crate::parser::Parser;
use crate::rename::rename;
use crate::server::LspSender;
use crate::server::Message;
use crate::server::Read;
use crate::server::Request;
use crate::server::Server;
use crate::lexer::SourcePosition;
use crate::server::Write;
use crate::source_map::SourceMap;
use lsp_types::Diagnostic;
use lsp_types::DiagnosticSeverity;
use lsp_types::DidChangeTextDocumentParams;
use lsp_types::DidOpenTextDocumentParams;
use lsp_types::DocumentHighlight;
use lsp_types::DocumentHighlightParams;
use lsp_types::Position;
use lsp_types::PublishDiagnosticsParams;
use lsp_types::Range;
use lsp_types::RenameParams;
use lsp_types::Url;
use lsp_types::WorkspaceEdit;
use serde_json::json;
use std::collections::HashMap;
use std::convert::TryFrom;

/// Runs the main loop of the LSP server.
///
/// This method finishes when `exit` notification is received.
pub fn run<R: Read, W: Write + Send + 'static>(server: Server<R, W>) {
    let mut state = State {
        source_map: SourceMap::new(),
        sender: server.sender(),
    };
    for msg in server {
        state.handle_message(msg);
    }
}

struct State {
    source_map: SourceMap,
    sender: LspSender,
}

fn token_position_to_range(position: &TokenPosition) -> Range {
    Range {
        start: Position {
            line: position.start.line as u64,
            character: position.start.character as u64,
        },
        end: Position {
            line: position.end.line as u64,
            character: position.end.character as u64,
        },
    }
}

impl State {
    fn handle_message(&mut self, msg: Message) {
        match msg {
            Message::Request(req) => match req.method.as_ref() {
                "initialize" => {
                    req.response_handle.respond(Ok(json!({"capabilities": {
                        "renameProvider": true,
                        "documentHighlightProvider": true,
                    }})));
                }
                "textDocument/rename" => {
                    self.handle_rename(req);
                }
                "textDocument/documentHighlight" => {
                    self.handle_document_highlight(req);
                }
                method => {
                    eprintln!("Unrecognized request: {}", method);
                }
            },
            Message::Notification(notification) => match notification.method.as_ref() {
                "initialized" => {}
                "textDocument/didOpen" => {
                    let params: DidOpenTextDocumentParams =
                        serde_json::from_value(notification.params.clone()).unwrap();
                    self.handle_did_open(params);
                }
                "textDocument/didChange" => {
                    let params: DidChangeTextDocumentParams =
                        serde_json::from_value(notification.params.clone()).unwrap();
                    self.handle_did_change(params);
                }
                method => {
                    eprintln!("Unrecognized notification: {}", method);
                }
            },
        }
    }

    fn handle_did_open(&mut self, params: DidOpenTextDocumentParams) {
        self.source_map.add(
            &params.text_document.uri,
            params.text_document.text.to_string(),
        );

        publish_diagnostics(
            &params.text_document.text,
            params.text_document.uri,
            &self.sender,
        );
    }

    fn handle_did_change(&mut self, params: DidChangeTextDocumentParams) {
        // TODO: Add support for partial content changes
        if params.content_changes.len() != 1 {
            panic!("unsupported not one content changes");
        }
        if !params.content_changes[0].range.is_none() {
            panic!("unsupported partial content change");
        }
        self.source_map.add(
            &params.text_document.uri,
            params.content_changes[0].text.to_string(),
        );
        publish_diagnostics(
            &params.content_changes[0].text,
            params.text_document.uri,
            &self.sender,
        );
    }

    fn handle_rename(&self, req: Request) {
        // TODO: This doesn't work yet, it is still WIP!
        let params: RenameParams = serde_json::from_value(req.params.clone()).unwrap();
        let content = self
            .source_map
            .get_content(&params.text_document_position.text_document.uri)
            .unwrap();
        let edits = rename(
            &content,
            params.text_document_position.position,
            &params.new_name,
        )
        .unwrap();
        let mut changes = HashMap::new();
        changes.insert(params.text_document_position.text_document.uri, edits);
        req.response_handle
            .respond(Ok(serde_json::to_value(WorkspaceEdit {
                changes: Some(changes),
                document_changes: None,
            })
            .unwrap()))
    }

    fn handle_document_highlight(&self, req: Request) {
        // TODO: This doesn't work yet, it is still WIP!
        let params: DocumentHighlightParams = serde_json::from_value(req.params.clone()).unwrap();
        let content = self
            .source_map
            .get_content(&params.text_document_position_params.text_document.uri)
            .unwrap();

        let parser = Parser::new(Lexer::new(&content));
        // let program = parser.parse();
        let pos = params.text_document_position_params.position;
        let token = parser.find_token(SourcePosition{
            line: i32::try_from(pos.line).unwrap(),
            character: i32::try_from(pos.character).unwrap(),
        }).unwrap();
        let token_position = parser.resolve_location(token.location);

        req.response_handle
            .respond(Ok(serde_json::to_value(vec![DocumentHighlight {
                kind: None,
                range: token_position_to_range(&token_position),
            }])
            .unwrap()))
    }
}

fn publish_diagnostics(text: &str, uri: Url, sender: &LspSender) {
    let mut parser = Parser::new(Lexer::new(text));
    parser.parse();
    let mut diagnostics_params = PublishDiagnosticsParams {
        uri: uri,
        diagnostics: Vec::new(),
        version: None,
    };
    for error in parser.errors {
        diagnostics_params.diagnostics.push(Diagnostic {
            range: token_position_to_range(&error.position),
            message: error.message,
            code: None,
            related_information: None,
            severity: Some(DiagnosticSeverity::Error),
            source: None,
            tags: None,
        });
    }
    sender.send_notification(
        "textDocument/publishDiagnostics",
        serde_json::to_value(diagnostics_params).unwrap(),
    );
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::io;
    use std::sync::mpsc::channel;
    use std::sync::mpsc::Receiver;
    use std::sync::mpsc::Sender;

    struct FakeReader {
        receiver: Receiver<String>,
    }

    impl FakeReader {
        fn new() -> (Sender<String>, FakeReader) {
            let (sender, receiver) = channel();
            return (sender, FakeReader { receiver: receiver });
        }
    }

    impl Read for FakeReader {
        fn read_packet(&mut self) -> Result<String, io::Error> {
            self.receiver
                .recv()
                .map_err(|_| io::Error::new(io::ErrorKind::UnexpectedEof, "EOF encountered"))
        }
    }
    struct FakeWriter {
        sender: Sender<String>,
    }

    impl FakeWriter {
        fn new() -> (Receiver<String>, FakeWriter) {
            let (sender, receiver) = channel();
            return (receiver, FakeWriter { sender: sender });
        }
    }

    impl Write for FakeWriter {
        fn write_packet(&self, packet: String) -> Result<(), io::Error> {
            self.sender.send(packet).unwrap();
            Ok(())
        }
    }

    struct Client {
        sender: Sender<String>,
        receiver: Receiver<String>,
    }

    impl Client {
        fn recv(&self) -> Result<serde_json::Value, ()> {
            Ok(self.receiver.recv().unwrap().parse().unwrap())
        }
        fn send(&self, req: serde_json::Value) -> Result<(), ()> {
            self.sender.send(req.to_string()).unwrap();
            Ok(())
        }
    }

    fn create_client_and_server() -> (Client, Server<FakeReader, FakeWriter>) {
        let (writer_ch, writer) = FakeWriter::new();
        let (reader_ch, reader) = FakeReader::new();
        let client = Client {
            sender: reader_ch,
            receiver: writer_ch,
        };
        let server = Server::new(reader, writer);
        return (client, server);
    }

    #[test]
    fn responds_to_initialize() {
        let (client, server) = create_client_and_server();
        let t = std::thread::spawn(move || {
            run(server);
        });

        client
            .send(json!({
                "jsonrpc": "2.0",
                "id": 1,
                "method": "initialize",
                "params": {
                    "processId": serde_json::Value::Null,
                    "rootUri": serde_json::Value::Null,
                    "capabilities": {
                    },
                },
            }))
            .unwrap();
        client.recv().unwrap();
        client
            .send(json!({
                "jsonrpc": "2.0",
                "method": "exit",
            }))
            .unwrap();

        t.join().unwrap();
    }

    #[test]
    fn document_hightlight_highlights_the_same_variable() {
        // TODO: This has to be refactor to make writing tests easy.
        let (client, server) = create_client_and_server();
        let t = std::thread::spawn(move || {
            run(server);
        });

        // Initialize
        client
            .send(json!({
                "jsonrpc": "2.0",
                "id": 1,
                "method": "initialize",
                "params": {
                    "processId": serde_json::Value::Null,
                    "rootUri": serde_json::Value::Null,
                    "capabilities": {
                    },
                },
            }))
            .unwrap();
        // Receive initialized
        // TODO: verify this.
        client.recv().unwrap();

        // Open document (notification)
        client
            .send(json!({
                "jsonrpc": "2.0",
                "method": "textDocument/didOpen",
                "params": {
                    "textDocument": {
                        "uri": "file:///home/user/test.vim",
                        "languageId": "vim",
                        "version": 1,
                        "text": "let myvar = 1\nlet myvar = 2\n",
                    },
                },
            }))
            .unwrap();
        // Diagnostic notification
        client.recv().unwrap();

        // Request hightlights
        client
            .send(json!({
                "jsonrpc": "2.0",
                "id": 1,
                "method": "textDocument/documentHighlight",
                "params": {
                    "textDocument": {
                        "uri": "file:///home/user/test.vim",
                    },
                    "position": {
                        "line": 0,
                        "character": 5,
                    },
                },
            }))
            .unwrap();
        let response = client.recv().unwrap();
        let result = response.get("result").unwrap().clone();
        let x: Vec<DocumentHighlight> = serde_json::from_value(result).unwrap();
        // TODO: This is invalid response, we should actually report both variables not just the
        // first one.
        assert_eq!(
            x,
            vec![DocumentHighlight {
                kind: None,
                range: Range {
                    start: Position {
                        line: 0,
                        character: 4,
                    },
                    end: Position {
                        line: 0,
                        character: 9,
                    },
                },
            }]
        );

        // Exit
        client
            .send(json!({
                "jsonrpc": "2.0",
                "method": "exit",
            }))
            .unwrap();

        t.join().unwrap();
    }
}
<|endoftext|>


<commit_before>use beacon_chain::{BeaconChain, BeaconChainTypes};
use prometheus::{IntGauge, Opts, Registry};
use slot_clock::SlotClock;
use types::Slot;

pub struct LocalMetrics {
    present_slot: IntGauge,
    best_slot: IntGauge,
    validator_count: IntGauge,
    justified_epoch: IntGauge,
    finalized_epoch: IntGauge,
}

impl LocalMetrics {
    /// Create a new instance.
    pub fn new() -> Result<Self, prometheus::Error> {
        Ok(Self {
            present_slot: {
                let opts = Opts::new("present_slot", "slot_at_time_of_scrape");
                IntGauge::with_opts(opts)?
            },
            best_slot: {
                let opts = Opts::new("best_slot", "slot_of_block_at_chain_head");
                IntGauge::with_opts(opts)?
            },
            validator_count: {
                let opts = Opts::new("validator_count", "number_of_validators");
                IntGauge::with_opts(opts)?
            },
            justified_epoch: {
                let opts = Opts::new("justified_epoch", "state_justified_epoch");
                IntGauge::with_opts(opts)?
            },
            finalized_epoch: {
                let opts = Opts::new("finalized_epoch", "state_finalized_epoch");
                IntGauge::with_opts(opts)?
            },
        })
    }

    /// Registry this instance with the `registry`.
    pub fn register(&self, registry: &Registry) -> Result<(), prometheus::Error> {
        registry.register(Box::new(self.present_slot.clone()))?;
        registry.register(Box::new(self.best_slot.clone()))?;
        registry.register(Box::new(self.validator_count.clone()))?;

        Ok(())
    }

    /// Update the metrics in `self` to the latest values.
    pub fn update<T: BeaconChainTypes>(&self, beacon_chain: &BeaconChain<T>) {
        let state = &beacon_chain.head().beacon_state;

        let present_slot = beacon_chain
            .slot_clock
            .present_slot()
            .unwrap_or_else(|_| None)
            .unwrap_or_else(|| Slot::new(0));
        self.present_slot.set(present_slot.as_u64() as i64);

        self.best_slot.set(state.slot.as_u64() as i64);
        self.validator_count.set(state.validator_registry.len() as i64);
        self.justified_epoch.set(state.current_justified_epoch.as_u64() as i64);
        self.finalized_epoch.set(state.finalized_epoch.as_u64() as i64);
    }
}
<commit_msg>Add extra validator balances metric<commit_after>use beacon_chain::{BeaconChain, BeaconChainTypes};
use prometheus::{IntGauge, Opts, Registry};
use slot_clock::SlotClock;
use types::Slot;

// If set to `true` will iterate and sum the balances of all validators in the state for each
// scrape.
const SHOULD_SUM_VALIDATOR_BALANCES: bool = true;

pub struct LocalMetrics {
    present_slot: IntGauge,
    best_slot: IntGauge,
    validator_count: IntGauge,
    justified_epoch: IntGauge,
    finalized_epoch: IntGauge,
    validator_balances_sum: IntGauge,
}

impl LocalMetrics {
    /// Create a new instance.
    pub fn new() -> Result<Self, prometheus::Error> {
        Ok(Self {
            present_slot: {
                let opts = Opts::new("present_slot", "slot_at_time_of_scrape");
                IntGauge::with_opts(opts)?
            },
            best_slot: {
                let opts = Opts::new("best_slot", "slot_of_block_at_chain_head");
                IntGauge::with_opts(opts)?
            },
            validator_count: {
                let opts = Opts::new("validator_count", "number_of_validators");
                IntGauge::with_opts(opts)?
            },
            justified_epoch: {
                let opts = Opts::new("justified_epoch", "state_justified_epoch");
                IntGauge::with_opts(opts)?
            },
            finalized_epoch: {
                let opts = Opts::new("finalized_epoch", "state_finalized_epoch");
                IntGauge::with_opts(opts)?
            },
            validator_balances_sum: {
                let opts = Opts::new("validator_balances_sum", "sum_of_all_validator_balances");
                IntGauge::with_opts(opts)?
            },
        })
    }

    /// Registry this instance with the `registry`.
    pub fn register(&self, registry: &Registry) -> Result<(), prometheus::Error> {
        registry.register(Box::new(self.present_slot.clone()))?;
        registry.register(Box::new(self.best_slot.clone()))?;
        registry.register(Box::new(self.validator_count.clone()))?;
        registry.register(Box::new(self.finalized_epoch.clone()))?;
        registry.register(Box::new(self.justified_epoch.clone()))?;
        registry.register(Box::new(self.validator_balances_sum.clone()))?;

        Ok(())
    }

    /// Update the metrics in `self` to the latest values.
    pub fn update<T: BeaconChainTypes>(&self, beacon_chain: &BeaconChain<T>) {
        let state = &beacon_chain.head().beacon_state;

        let present_slot = beacon_chain
            .slot_clock
            .present_slot()
            .unwrap_or_else(|_| None)
            .unwrap_or_else(|| Slot::new(0));
        self.present_slot.set(present_slot.as_u64() as i64);

        self.best_slot.set(state.slot.as_u64() as i64);
        self.validator_count.set(state.validator_registry.len() as i64);
        self.justified_epoch.set(state.current_justified_epoch.as_u64() as i64);
        self.finalized_epoch.set(state.finalized_epoch.as_u64() as i64);
        if SHOULD_SUM_VALIDATOR_BALANCES {
            self.validator_balances_sum.set(state.validator_balances.iter().sum::<u64>() as i64);
        }
    }
}
<|endoftext|>