<commit_before>use std::cmp::max;
use std::fmt::{Display, Error, Formatter};
use std::num::Int;
use std::rc::Rc;

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Tree<T> {
    Tip,
    Node(Rc<Tree<T>>, T, Rc<Tree<T>>),
}

use tree::Tree::{Node, Tip};

impl<T: Display + PartialEq + Clone> Display for Tree<T> {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        fn depth<T>(t: &Tree<T>) -> usize {
            match *t {
                Tip => 0,
                Node(ref l, _, ref r) => 1 + max(depth(l), depth(r))
            }
        }

        fn print_whitespace(n: usize, f: &mut Formatter) -> Result<(), Error> {
            for _ in (0..n) {
                try!(write!(f, " "));
            }
            Result::Ok(())
        }

        fn aux<T: Clone + Display + PartialEq>(nodes: Vec<Tree<T>>, level: usize, max_level: usize, f: &mut Formatter) -> Result<(), Error> {
            if nodes.iter().all(|n| *n == Tip) { return Result::Ok(()) }

            let floor = max_level - level;
            let edge_lines = 2.pow(max(floor - 1, 0)) as usize;
            let first_spaces = 2.pow(floor) - 1 as usize;
            let between_spaces = 2.pow(floor + 1) - 1 as usize;

            try!(print_whitespace(first_spaces, f));

            let new_nodes =
                nodes.iter().fold(vec![], |mut acc, n| {
                    let ret =
                        match *n {
                            Node(ref l, ref v, ref r) => {
                                write!(f, "{}", v);
                                acc.push((**l).clone());
                                acc.push((**r).clone());
                                acc
                            },
                            Tip => {
                                write!(f, " ");
                                acc.push(Tip);
                                acc.push(Tip);
                                acc
                            }
                        };

                    print_whitespace(between_spaces, f);

                    ret
                });

            try!(writeln!(f, ""));

            for i in (1..(edge_lines + 1)) {
                for node in nodes.iter() {
                    try!(print_whitespace(first_spaces - i, f));
                    match *node {
                        Tip => { try!(print_whitespace(edge_lines + edge_lines + i + 1, f)); },
                        Node(ref l, _, ref r) => {
                            if **l != Tip { try!(write!(f, "/")); }
                            else { try!(write!(f, " ")); }

                            try!(print_whitespace(i + i - 1, f));

                            if **r != Tip { try!(write!(f, "\\")); }
                            else { try!(write!(f, " ")); }

                            try!(print_whitespace(edge_lines + edge_lines - 1, f));
                        }
                    }
                }

                try!(writeln!(f, ""));
            }

            aux(new_nodes, level + 1, max_level, f)
        }

        aux(vec![(*self).clone()], 1, depth(self), f)
    }
}

impl<T: Ord> Tree<T> {
    // Exercise 2.2:
    // only performs at most d + 1 comparisons, where d is the depth of the tree
    pub fn member2(&self, x: T) -> bool {
        match *self {
            Tip => false,
            Node(_, ref v, _) => {
                fn member_aux<T: Ord>(t: &Tree<T>, x: T, c: &T) -> bool {
                    match *t {
                        Tip => x == *c,
                        Node(ref l, ref v, ref r) =>
                            if x < *v {
                                member_aux(l, x, c)
                            } else {
                                member_aux(r, x, v)
                            }
                    }
                }

                member_aux(self, x, v)
            },
        }
    }
}
<commit_msg>"reuse" heap's pretty-print in tree<commit_after>use std::cmp::max;
use std::fmt::{Display, Error, Formatter};
use std::num::Int;
use std::rc::Rc;

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Tree<T> {
    Tip,
    Node(Rc<Tree<T>>, T, Rc<Tree<T>>),
}

use tree::Tree::{Node, Tip};

impl<T: Display> Display for Tree<T> {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        fn aux<T: Display>(f: &mut Formatter, t: &Tree<T>, right: bool, indent: &str) -> Result<(), Error> {
            match *t {
                Node(ref l, ref x, ref r) => {
                    try!(aux(f, r, true, &(indent.to_string() + if right { "        " } else { " |      " })));

                    try!(write!(f, "{}", indent));
                    try!(if right { write!(f, "{}", " /") } else { write!(f, "{}", " \\") });
                    try!(write!(f, "{}", "----- "));

                    try!(writeln!(f, "({})", x));

                    aux(f, l, false, &(indent.to_string() + if right { " |      " } else { "        " }))
                },
                Tip => {
                    try!(write!(f, "{}", indent));
                    try!(if right { write!(f, "{}", " /") } else { write!(f, "{}", " \\") });
                    try!(write!(f, "{}", "----- "));

                    writeln!(f, "{}", "()")
                }
            }
        }

        match *self {
            Node(ref l, ref x, ref r) => {
                try!(aux(f, r, true, ""));
                try!(writeln!(f, "({})", x));
                aux(f, l, false, "")
            },
            Tip => Result::Ok(())
        }
    }
}

impl<T: Ord> Tree<T> {
    // Exercise 2.2:
    // only performs at most d + 1 comparisons, where d is the depth of the tree
    pub fn member2(&self, x: T) -> bool {
        match *self {
            Tip => false,
            Node(_, ref v, _) => {
                fn member_aux<T: Ord>(t: &Tree<T>, x: T, c: &T) -> bool {
                    match *t {
                        Tip => x == *c,
                        Node(ref l, ref v, ref r) =>
                            if x < *v {
                                member_aux(l, x, c)
                            } else {
                                member_aux(r, x, v)
                            }
                    }
                }

                member_aux(self, x, v)
            },
        }
    }
}
<|endoftext|>


<commit_before>/// Validates an over-the-air callsign. APRS-IS is more forgiving.
///
/// Valid callsigns are between 4 and 7 alphanumeric characters with at most a single hyphen `-`
/// separating the callsign and the SSID suffix. This function validates the callsign's length,
/// composition and SSID.
///
/// # Examples
///
/// ```rust
/// # extern crate aprs;
/// # use aprs::util::valid_callsign;
/// # fn main() {
/// assert_eq!(valid_callsign("W2GMD"), true);
/// assert_eq!(valid_callsign("W2GMD-2"), true);
/// assert_eq!(valid_callsign("W2GMD*"), true);
/// # }
/// ```
///
/// ```rust
/// # extern crate aprs;
/// # use aprs::util::valid_callsign;
/// # fn main() {
/// assert_eq!(valid_callsign("W2GMD--1"), false);
/// assert_eq!(valid_callsign("W2GMD- 1"), false);
/// assert_eq!(valid_callsign("W2*MD-8"), false);
/// # }
/// ```
///
pub fn valid_callsign(callsign: &str) -> bool {
    let mut callsign: &str = callsign.trim_matches(|x| x == '*' || (x as char).is_whitespace());
    let mut ssid: &str = "0";

    // Ensure single separator between callsign and SSID
    match callsign.matches('-').count() {
        0 => {},
        1 => {
            let call_components: Vec<&str> = callsign.split('-').collect();
            callsign = call_components[0];
            ssid = call_components[1];
        },
        _ => {return false;},
    }
    // Validate callsign
    match callsign.len() {
        4...6 => {},
        _ => {return false;},
    }
    for chr in callsign.chars() {
        if !chr.is_alphanumeric() {
            return false;
        }
    }
    // Validate SSID
    match ssid.len() {
        1...2 => {},
        _ => {return false;}
    }
    for chr in ssid.chars() {
        if !chr.is_numeric() {
            return false;
        }
    }
    match ssid.parse::<usize>()  {
        Err(e) => {return false;},
        Ok(s) => {
            match s {
                0...15 => {},
                _ => {return false;},
            }
        },
    }
    // Callsign and SSID have passed all checks, so return true
    true
}
<commit_msg>util: module doc, tweak allowable callsign size (some aprs paths are three characters like qAL).<commit_after>//! Utilities for validating data.

/// Validates an over-the-air callsign. APRS-IS is more forgiving.
///
/// Valid callsigns are between 4 and 7 alphanumeric characters with at most a single hyphen `-`
/// separating the callsign and the SSID suffix. This function validates the callsign's length,
/// composition and SSID.
///
/// # Examples
///
/// ```rust
/// # extern crate aprs;
/// # use aprs::util::valid_callsign;
/// # fn main() {
/// assert_eq!(valid_callsign("W2GMD"), true);
/// assert_eq!(valid_callsign("W2GMD-2"), true);
/// assert_eq!(valid_callsign("W2GMD*"), true);
/// # }
/// ```
///
/// ```rust
/// # extern crate aprs;
/// # use aprs::util::valid_callsign;
/// # fn main() {
/// assert_eq!(valid_callsign("W2GMD--1"), false);
/// assert_eq!(valid_callsign("W2GMD- 1"), false);
/// assert_eq!(valid_callsign("W2*MD-8"), false);
/// # }
/// ```
///
pub fn valid_callsign(callsign: &str) -> bool {
    let mut callsign: &str = callsign.trim_matches(|x| x == '*' || (x as char).is_whitespace());
    let mut ssid: &str = "0";

    // Ensure single separator between callsign and SSID
    match callsign.matches('-').count() {
        0 => {},
        1 => {
            let call_components: Vec<&str> = callsign.split('-').collect();
            callsign = call_components[0];
            ssid = call_components[1];
        },
        _ => {return false;},
    }
    // Validate callsign
    match callsign.len() {
        3...6 => {},
        _ => {return false;},
    }
    for chr in callsign.chars() {
        if !chr.is_alphanumeric() {
            return false;
        }
    }
    // Validate SSID
    match ssid.len() {
        1...2 => {},
        _ => {return false;}
    }
    for chr in ssid.chars() {
        if !chr.is_numeric() {
            return false;
        }
    }
    match ssid.parse::<usize>()  {
        Err(e) => {return false;},
        Ok(s) => {
            match s {
                0...15 => {},
                _ => {return false;},
            }
        },
    }
    // Callsign and SSID have passed all checks, so return true
    true
}
<|endoftext|>


<commit_before>use std::borrow::Cow;

pub fn find_longest_common_prefix<T: Clone + Eq>(among: &[Vec<T>]) -> Option<Vec<T>> {
    if among.len() == 0 {
        return None;
    } else if among.len() == 1 {
        return Some(among[0].clone());
    }

    for s in among {
        if s.len() == 0 {
            return None;
        }
    }

    let shortest_word = among.iter().min_by_key(|x| x.len()).unwrap();

    let mut end = shortest_word.len();
    while end > 0 {
        let prefix = &shortest_word[..end];

        let mut failed = false;
        for s in among {
            if !s.starts_with(prefix) {
                failed = true;
                break;
            }
        }

        if !failed {
            return Some(prefix.into());
        }

        end -= 1;
    }

    None
}

fn is_ascii_letter(c: char) -> bool {
    (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
}

fn can_finish_code(c: char) -> bool {
    if c == 'P' {
        // dsc code
        return false;
    }

    c == '?' || is_ascii_letter(c)
}

pub fn remove_codes(input: &str) -> Cow<str> {
    if input.contains('\x1B') {
        Cow::Owned(input.split('\x1B')
            .flat_map(|x| x.chars().skip_while(|&c| !can_finish_code(c)).skip(1))
            .collect())
    } else {
        Cow::Borrowed(input)
    }
}
<commit_msg>Fix parsing in remove_codes to remove OSC<commit_after>use std::borrow::Cow;

pub fn find_longest_common_prefix<T: Clone + Eq>(among: &[Vec<T>]) -> Option<Vec<T>> {
    if among.len() == 0 {
        return None;
    } else if among.len() == 1 {
        return Some(among[0].clone());
    }

    for s in among {
        if s.len() == 0 {
            return None;
        }
    }

    let shortest_word = among.iter().min_by_key(|x| x.len()).unwrap();

    let mut end = shortest_word.len();
    while end > 0 {
        let prefix = &shortest_word[..end];

        let mut failed = false;
        for s in among {
            if !s.starts_with(prefix) {
                failed = true;
                break;
            }
        }

        if !failed {
            return Some(prefix.into());
        }

        end -= 1;
    }

    None
}

pub enum AnsiState {
    Norm,
    Esc,
    Csi,
    Osc,
}

pub fn remove_codes(input: &str) -> Cow<str> {
    if input.contains('\x1B') {
        let mut clean = String::new();

        let mut s = AnsiState::Norm;
        for c in input.chars() {
            match s {
                AnsiState::Norm => match c {
                    '\x1B' => s = AnsiState::Esc,
                    _ => clean.push(c),
                },
                AnsiState::Esc => match c {
                    '[' => s = AnsiState::Csi,
                    ']' => s = AnsiState::Osc,
                    _ => s = AnsiState::Norm,
                },
                AnsiState::Csi => match c {
                    'A' ... 'Z' | 'a' ... 'z' => s = AnsiState::Norm,
                    _ => (),
                },
                AnsiState::Osc => match c {
                    '\x07' => s = AnsiState::Norm,
                    _ => (),
                }
            }
        }

        Cow::Owned(clean)
    } else {
        Cow::Borrowed(input)
    }
}
<|endoftext|>


<commit_before>use std::fs::File;
use std::io::BufReader;
use std::collections::HashMap;
use std::borrow::Cow;

use zip::read::{ZipFile, ZipArchive};
use zip::result::ZipError;
use quick_xml::{XmlReader, Event, AsStr};

use {DataType, ExcelReader, Range, Cell};
use vba::VbaProject;
use errors::*;

/// A struct representing xml zipped excel file
/// Xlsx, Xlsm, Xlam
pub struct Xlsx {
    zip: ZipArchive<File>,
}

impl Xlsx {
    fn xml_reader<'a>(&'a mut self,
                      path: &str)
                      -> Option<Result<XmlReader<BufReader<ZipFile<'a>>>>> {
        match self.zip.by_name(path) {
            Ok(f) => {
                Some(Ok(XmlReader::from_reader(BufReader::new(f))
                    .with_check(false)
                    .trim_text(false)))
            }
            Err(ZipError::FileNotFound) => None,
            Err(e) => return Some(Err(e.into())),
        }
    }
}

impl ExcelReader for Xlsx {
    fn new(f: File) -> Result<Self> {
        Ok(Xlsx { zip: ZipArchive::new(f)? })
    }

    fn has_vba(&mut self) -> bool {
        self.zip.by_name("xl/vbaProject.bin").is_ok()
    }

    fn vba_project(&mut self) -> Result<Cow<VbaProject>> {
        let mut f = self.zip.by_name("xl/vbaProject.bin")?;
        let len = f.size() as usize;
        VbaProject::new(&mut f, len).map(|v| Cow::Owned(v))
    }

    fn read_shared_strings(&mut self) -> Result<Vec<String>> {
        let mut xml = match self.xml_reader("xl/sharedStrings.xml") {
            None => return Ok(Vec::new()),
            Some(x) => x?,
        };
        let mut strings = Vec::new();
        let mut rich_buffer: Option<String> = None;
        while let Some(res_event) = xml.next() {
            match res_event {
                Ok(Event::Start(ref e)) if e.name() == b"r" => {
                    if let None = rich_buffer {
                        // use a buffer since richtext has multiples <r> and <t> for the same cell
                        rich_buffer = Some(String::new());
                    }
                }
                Ok(Event::End(ref e)) if e.name() == b"si" => {
                    if let Some(s) = rich_buffer {
                        strings.push(s);
                        rich_buffer = None;
                    }
                }
                Ok(Event::Start(ref e)) if e.name() == b"t" => {
                    let value = xml.read_text_unescaped(b"t")?;
                    if let Some(ref mut s) = rich_buffer {
                        s.push_str(&value);
                    } else {
                        strings.push(value);
                    }
                }
                Err(e) => return Err(e.into()),
                _ => (),
            }
        }
        Ok(strings)
    }

    fn read_sheets_names(&mut self,
                         relationships: &HashMap<Vec<u8>, String>)
                         -> Result<Vec<(String, String)>> {
        let xml = match self.xml_reader("xl/workbook.xml") {
            None => return Ok(Vec::new()),
            Some(x) => x?,
        };
        let mut sheets = Vec::new();
        for res_event in xml {
            match res_event {
                Ok(Event::Start(ref e)) if e.name() == b"sheet" => {
                    let mut name = String::new();
                    let mut path = String::new();
                    for a in e.unescaped_attributes() {
                        match a? {
                            (b"name", v) => name = v.as_str()?.to_string(),
                            (b"r:id", v) => path = format!("xl/{}", relationships[&*v]),
                            _ => (),
                        }
                    }
                    sheets.push((name, path));
                }
                Err(e) => return Err(e.into()),
                _ => (),
            }
        }
        Ok(sheets)
    }

    fn read_relationships(&mut self) -> Result<HashMap<Vec<u8>, String>> {
        let xml = match self.xml_reader("xl/_rels/workbook.xml.rels") {
            None => return Err("Cannot find relationships file".into()),
            Some(x) => x?,
        };
        let mut relationships = HashMap::new();
        for res_event in xml {
            match res_event {
                Ok(Event::Start(ref e)) if e.name() == b"Relationship" => {
                    let mut id = Vec::new();
                    let mut target = String::new();
                    for a in e.attributes() {
                        match a? {
                            (b"Id", v) => id.extend_from_slice(v),
                            (b"Target", v) => target = v.as_str()?.to_string(),
                            _ => (),
                        }
                    }
                    relationships.insert(id, target);
                }
                Err(e) => return Err(e.into()),
                _ => (),
            }
        }
        Ok(relationships)
    }

    fn read_worksheet_range(&mut self, path: &str, strings: &[String]) -> Result<Range> {
        let mut xml = match self.xml_reader(path) {
            None => return Err(format!("Cannot find {} path", path).into()),
            Some(x) => x?,
        };
        let mut cells = Vec::new();
        'xml: while let Some(res_event) = xml.next() {
            match res_event {
                Err(e) => return Err(e.into()),
                Ok(Event::Start(ref e)) => {
                    match e.name() {
                        b"dimension" => {
                            for a in e.attributes() {
                                if let (b"ref", rdim) = a? {
                                    let (start, end) = get_dimension(rdim)?;
                                    cells.reserve(((end.0 - start.0 + 1) * (end.1 - start.1 + 1)) as
                                                 usize);
                                    continue 'xml;
                                }
                            }
                            return Err(format!("Expecting dimension, got {:?}", e).into());
                        }
                        b"sheetData" => read_sheet_data(&mut xml, strings, &mut cells)?,
                        _ => (),
                    }
                }
                _ => (),
            }
        }
        Ok(Range::from_sparse(cells))
    }
}

/// read sheetData node
fn read_sheet_data(xml: &mut XmlReader<BufReader<ZipFile>>,
                   strings: &[String],
                   cells: &mut Vec<Cell>)
                   -> Result<()> {
    while let Some(res_event) = xml.next() {
        match res_event {
            Err(e) => return Err(e.into()),
            Ok(Event::Start(ref c_element)) if c_element.name() == b"c" => {
                let pos = match c_element.attributes()
                    .filter_map(|a| match a {
                        Err(e) => Some(Err(e.into())),
                        Ok((b"r", v)) => Some(get_row_column(v)),
                        _ => None,
                    })
                    .next() {
                    Some(v) => v?,
                    None => return Err("Cell without a 'r' reference tag".into()),
                };
                loop {
                    match xml.next() {
                        Some(Err(e)) => return Err(e.into()),
                        Some(Ok(Event::Start(ref e))) => {
                            match e.name() {
                                b"v" => {
                                    // value
                                    let v = xml.read_text_unescaped(b"v")?;
                                    let value = match c_element.attributes()
                                        .filter_map(|a| a.ok())
                                        .find(|&(k, _)| k == b"t") {
                                        Some((_, b"s")) => {
                                            // shared string
                                            let idx: usize = v.parse()?;
                                            DataType::String(strings[idx].clone())
                                        }
                                        Some((_, b"str")) => {
                                            // regular string
                                            DataType::String(v)
                                        }
                                        Some((_, b"b")) => {
                                            // boolean
                                            DataType::Bool(v != "0")
                                        }
                                        Some((_, b"e")) => {
                                            // error
                                            DataType::Error(v.parse()?)
                                        }
                                        _ => v.parse().map(DataType::Float)?,
                                    };
                                    cells.push(Cell::new(pos, value));
                                    break;
                                }
                                b"f" => (), // formula, ignore
                                _name => return Err("not v or f node".into()),
                            }
                        }
                        Some(Ok(Event::End(ref e))) if e.name() == b"c" => break,
                        None => return Err("End of xml".into()),
                        _ => (),
                    }
                }
            }
            Ok(Event::End(ref e)) if e.name() == b"sheetData" => return Ok(()),
            _ => (),
        }
    }
    Err("Could not find </sheetData>".into())
}

/// converts a text representation (e.g. "A6:G67") of a dimension into integers
/// - top left (row, column),
/// - bottom right (row, column)
fn get_dimension(dimension: &[u8]) -> Result<((u32, u32), (u32, u32))> {
    let parts: Vec<_> = dimension.split(|c| *c == b':')
        .map(|s| get_row_column(s))
        .collect::<Result<Vec<_>>>()?;

    match parts.len() {
        0 => Err("dimension cannot be empty".into()),
        1 => Ok((parts[0], parts[0])),
        2 => Ok((parts[0], parts[1])),
        len => Err(format!("range dimension has 0 or 1 ':', got {}", len).into()),
    }
}

/// converts a text range name into its position (row, column) (0 based index)
fn get_row_column(range: &[u8]) -> Result<(u32, u32)> {
    let (mut row, mut col) = (0, 0);
    let mut pow = 1;
    let mut readrow = true;
    for c in range.iter().rev() {
        match *c {
            c @ b'0'...b'9' => {
                if readrow {
                    row += ((c - b'0') as u32) * pow;
                    pow *= 10;
                } else {
                    return Err(format!("Numeric character are only allowed \
                        at the end of the range: {:x}",
                                       c)
                        .into());
                }
            }
            c @ b'A'...b'Z' => {
                if readrow {
                    pow = 1;
                    readrow = false;
                }
                col += ((c - b'A') as u32 + 1) * pow;
                pow *= 26;
            }
            c @ b'a'...b'z' => {
                if readrow {
                    pow = 1;
                    readrow = false;
                }
                col += ((c - b'a') as u32 + 1) * pow;
                pow *= 26;
            }
            _ => return Err(format!("Expecting alphanumeric character, got {:x}", c).into()),
        }
    }
    Ok((row - 1, col - 1))
}

#[test]
fn test_dimensions() {
    assert_eq!(get_row_column(b"A1").unwrap(), (0, 0));
    assert_eq!(get_row_column(b"C107").unwrap(), (106, 2));
    assert_eq!(get_dimension(b"C2:D35").unwrap(), ((1, 2), (34, 3)));
}
<commit_msg>Strip leading "xl/" path when looking up rels<commit_after>use std::fs::File;
use std::io::BufReader;
use std::collections::HashMap;
use std::borrow::Cow;

use zip::read::{ZipFile, ZipArchive};
use zip::result::ZipError;
use quick_xml::{XmlReader, Event, AsStr};

use {DataType, ExcelReader, Range, Cell};
use vba::VbaProject;
use errors::*;

/// A struct representing xml zipped excel file
/// Xlsx, Xlsm, Xlam
pub struct Xlsx {
    zip: ZipArchive<File>,
}

impl Xlsx {
    fn xml_reader<'a>(&'a mut self,
                      path: &str)
                      -> Option<Result<XmlReader<BufReader<ZipFile<'a>>>>> {
        match self.zip.by_name(path) {
            Ok(f) => {
                Some(Ok(XmlReader::from_reader(BufReader::new(f))
                    .with_check(false)
                    .trim_text(false)))
            }
            Err(ZipError::FileNotFound) => None,
            Err(e) => return Some(Err(e.into())),
        }
    }
}

impl ExcelReader for Xlsx {
    fn new(f: File) -> Result<Self> {
        Ok(Xlsx { zip: ZipArchive::new(f)? })
    }

    fn has_vba(&mut self) -> bool {
        self.zip.by_name("xl/vbaProject.bin").is_ok()
    }

    fn vba_project(&mut self) -> Result<Cow<VbaProject>> {
        let mut f = self.zip.by_name("xl/vbaProject.bin")?;
        let len = f.size() as usize;
        VbaProject::new(&mut f, len).map(|v| Cow::Owned(v))
    }

    fn read_shared_strings(&mut self) -> Result<Vec<String>> {
        let mut xml = match self.xml_reader("xl/sharedStrings.xml") {
            None => return Ok(Vec::new()),
            Some(x) => x?,
        };
        let mut strings = Vec::new();
        let mut rich_buffer: Option<String> = None;
        while let Some(res_event) = xml.next() {
            match res_event {
                Ok(Event::Start(ref e)) if e.name() == b"r" => {
                    if let None = rich_buffer {
                        // use a buffer since richtext has multiples <r> and <t> for the same cell
                        rich_buffer = Some(String::new());
                    }
                }
                Ok(Event::End(ref e)) if e.name() == b"si" => {
                    if let Some(s) = rich_buffer {
                        strings.push(s);
                        rich_buffer = None;
                    }
                }
                Ok(Event::Start(ref e)) if e.name() == b"t" => {
                    let value = xml.read_text_unescaped(b"t")?;
                    if let Some(ref mut s) = rich_buffer {
                        s.push_str(&value);
                    } else {
                        strings.push(value);
                    }
                }
                Err(e) => return Err(e.into()),
                _ => (),
            }
        }
        Ok(strings)
    }

    fn read_sheets_names(&mut self,
                         relationships: &HashMap<Vec<u8>, String>)
                         -> Result<Vec<(String, String)>> {
        let xml = match self.xml_reader("xl/workbook.xml") {
            None => return Ok(Vec::new()),
            Some(x) => x?,
        };
        let mut sheets = Vec::new();
        for res_event in xml {
            match res_event {
                Ok(Event::Start(ref e)) if e.name() == b"sheet" => {
                    let mut name = String::new();
                    let mut path = String::new();
                    for a in e.unescaped_attributes() {
                        match a? {
                            (b"name", v) => name = v.as_str()?.to_string(),
                            (b"r:id", v) => {
                                let r = &relationships[&*v][..];
                                // target may have pre-prended "/xl/" or "xl/" path;
                                // strip if present
                                let r = if r.starts_with("/xl/") {
                                    &r[4..]
                                } else if r.starts_with("xl/") {
                                    &r[3..]
                                } else {
                                    r
                                };
                                path = format!("xl/{}", r);
                            }
                            _ => (),
                        }
                    }
                    sheets.push((name, path));
                }
                Err(e) => return Err(e.into()),
                _ => (),
            }
        }
        Ok(sheets)
    }

    fn read_relationships(&mut self) -> Result<HashMap<Vec<u8>, String>> {
        let xml = match self.xml_reader("xl/_rels/workbook.xml.rels") {
            None => return Err("Cannot find relationships file".into()),
            Some(x) => x?,
        };
        let mut relationships = HashMap::new();
        for res_event in xml {
            match res_event {
                Ok(Event::Start(ref e)) if e.name() == b"Relationship" => {
                    let mut id = Vec::new();
                    let mut target = String::new();
                    for a in e.attributes() {
                        match a? {
                            (b"Id", v) => id.extend_from_slice(v),
                            (b"Target", v) => target = v.as_str()?.to_string(),
                            _ => (),
                        }
                    }
                    relationships.insert(id, target);
                }
                Err(e) => return Err(e.into()),
                _ => (),
            }
        }
        Ok(relationships)
    }

    fn read_worksheet_range(&mut self, path: &str, strings: &[String]) -> Result<Range> {
        let mut xml = match self.xml_reader(path) {
            None => return Err(format!("Cannot find {} path", path).into()),
            Some(x) => x?,
        };
        let mut cells = Vec::new();
        'xml: while let Some(res_event) = xml.next() {
            match res_event {
                Err(e) => return Err(e.into()),
                Ok(Event::Start(ref e)) => {
                    match e.name() {
                        b"dimension" => {
                            for a in e.attributes() {
                                if let (b"ref", rdim) = a? {
                                    let (start, end) = get_dimension(rdim)?;
                                    cells.reserve(((end.0 - start.0 + 1) * (end.1 - start.1 + 1)) as
                                                 usize);
                                    continue 'xml;
                                }
                            }
                            return Err(format!("Expecting dimension, got {:?}", e).into());
                        }
                        b"sheetData" => read_sheet_data(&mut xml, strings, &mut cells)?,
                        _ => (),
                    }
                }
                _ => (),
            }
        }
        Ok(Range::from_sparse(cells))
    }
}

/// read sheetData node
fn read_sheet_data(xml: &mut XmlReader<BufReader<ZipFile>>,
                   strings: &[String],
                   cells: &mut Vec<Cell>)
                   -> Result<()> {
    while let Some(res_event) = xml.next() {
        match res_event {
            Err(e) => return Err(e.into()),
            Ok(Event::Start(ref c_element)) if c_element.name() == b"c" => {
                let pos = match c_element.attributes()
                    .filter_map(|a| match a {
                        Err(e) => Some(Err(e.into())),
                        Ok((b"r", v)) => Some(get_row_column(v)),
                        _ => None,
                    })
                    .next() {
                    Some(v) => v?,
                    None => return Err("Cell without a 'r' reference tag".into()),
                };
                loop {
                    match xml.next() {
                        Some(Err(e)) => return Err(e.into()),
                        Some(Ok(Event::Start(ref e))) => {
                            match e.name() {
                                b"v" => {
                                    // value
                                    let v = xml.read_text_unescaped(b"v")?;
                                    let value = match c_element.attributes()
                                        .filter_map(|a| a.ok())
                                        .find(|&(k, _)| k == b"t") {
                                        Some((_, b"s")) => {
                                            // shared string
                                            let idx: usize = v.parse()?;
                                            DataType::String(strings[idx].clone())
                                        }
                                        Some((_, b"str")) => {
                                            // regular string
                                            DataType::String(v)
                                        }
                                        Some((_, b"b")) => {
                                            // boolean
                                            DataType::Bool(v != "0")
                                        }
                                        Some((_, b"e")) => {
                                            // error
                                            DataType::Error(v.parse()?)
                                        }
                                        _ => v.parse().map(DataType::Float)?,
                                    };
                                    cells.push(Cell::new(pos, value));
                                    break;
                                }
                                b"f" => (), // formula, ignore
                                _name => return Err("not v or f node".into()),
                            }
                        }
                        Some(Ok(Event::End(ref e))) if e.name() == b"c" => break,
                        None => return Err("End of xml".into()),
                        _ => (),
                    }
                }
            }
            Ok(Event::End(ref e)) if e.name() == b"sheetData" => return Ok(()),
            _ => (),
        }
    }
    Err("Could not find </sheetData>".into())
}

/// converts a text representation (e.g. "A6:G67") of a dimension into integers
/// - top left (row, column),
/// - bottom right (row, column)
fn get_dimension(dimension: &[u8]) -> Result<((u32, u32), (u32, u32))> {
    let parts: Vec<_> = dimension.split(|c| *c == b':')
        .map(|s| get_row_column(s))
        .collect::<Result<Vec<_>>>()?;

    match parts.len() {
        0 => Err("dimension cannot be empty".into()),
        1 => Ok((parts[0], parts[0])),
        2 => Ok((parts[0], parts[1])),
        len => Err(format!("range dimension has 0 or 1 ':', got {}", len).into()),
    }
}

/// converts a text range name into its position (row, column) (0 based index)
fn get_row_column(range: &[u8]) -> Result<(u32, u32)> {
    let (mut row, mut col) = (0, 0);
    let mut pow = 1;
    let mut readrow = true;
    for c in range.iter().rev() {
        match *c {
            c @ b'0'...b'9' => {
                if readrow {
                    row += ((c - b'0') as u32) * pow;
                    pow *= 10;
                } else {
                    return Err(format!("Numeric character are only allowed \
                        at the end of the range: {:x}",
                                       c)
                        .into());
                }
            }
            c @ b'A'...b'Z' => {
                if readrow {
                    pow = 1;
                    readrow = false;
                }
                col += ((c - b'A') as u32 + 1) * pow;
                pow *= 26;
            }
            c @ b'a'...b'z' => {
                if readrow {
                    pow = 1;
                    readrow = false;
                }
                col += ((c - b'a') as u32 + 1) * pow;
                pow *= 26;
            }
            _ => return Err(format!("Expecting alphanumeric character, got {:x}", c).into()),
        }
    }
    Ok((row - 1, col - 1))
}

#[test]
fn test_dimensions() {
    assert_eq!(get_row_column(b"A1").unwrap(), (0, 0));
    assert_eq!(get_row_column(b"C107").unwrap(), (106, 2));
    assert_eq!(get_dimension(b"C2:D35").unwrap(), ((1, 2), (34, 3)));
}
<|endoftext|>


<commit_before>extern crate tvdb;
use tvdb::v2::Tvdb;

#[test]
fn basic() {
    let t = Tvdb::new("0629B785CE550C8D");
    t.login().unwrap();
    println!("{:?}", t.search("scrubs"));
    panic!();
}
<commit_msg>Remove testing panic!() in test<commit_after>extern crate tvdb;
use tvdb::v2::Tvdb;

#[test]
fn basic() {
    let t = Tvdb::new("0629B785CE550C8D");
    t.login().unwrap();
    let sr = t.search("scrubs").expect();
    for s in sr.data.iter() {
        println!("{}: ID {}", s.seriesName, s.id);
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate error_chain;
extern crate futures;
extern crate hyper;
extern crate hyper_tls;
extern crate serde_json;
extern crate tokio_core;
extern crate url;
extern crate telegram_bot_raw;

use std::sync::Arc;
use std::cmp::max;
use std::collections::VecDeque;

use futures::{Future, Stream, Poll, Async};
use futures::future::{result};
use hyper::{Body, Method};
use hyper::client::Client;
use hyper::header::ContentType;
use hyper_tls::HttpsConnector;
use tokio_core::reactor::Handle;
use url::Url;

use telegram_bot_raw::{GetUpdates, Request, Response, ResponseParameters, Update, Integer};

const TELEGRAM_URL: &'static str = "https://api.telegram.org/";
const TELEGRAM_LONG_POLL_TIMEOUT: usize = 5;

error_chain! {
    foreign_links {
        Url(url::ParseError);
        Hyper(hyper::Error);
        Json(serde_json::Error);
    }

    errors {
        TelegramError {
            description: String,
            parameters: Option<ResponseParameters>
        }
    }
}

pub struct TelegramFuture<T> {
    inner: Box<Future<Item=T, Error=Error>>
}

impl<T> Future for TelegramFuture<T> {
    type Item = T;
    type Error = Error;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        self.inner.poll()
    }
}

pub struct UpdatesStream {
    bot: Bot,
    last_update: Integer,
    buffer: VecDeque<Update>,
    current_request: Option<TelegramFuture<Vec<Update>>>,
    timeout: usize,
}

impl Stream for UpdatesStream {
    type Item = Update;
    type Error = Error;

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        if let Some(value) = self.buffer.pop_front() {
            return Ok(Async::Ready(Some(value)))
        }

        let result = match self.current_request {
            None => None,
            Some(ref mut current_request) => {
                let polled_update = current_request.poll();
                match polled_update {
                    Ok(Async::NotReady) => return Ok(Async::NotReady),
                    Ok(Async::Ready(updates)) => {
                        if updates.is_empty() {
                            None
                        } else {
                            for update in updates.iter() {
                                self.last_update = max(update.id, self.last_update);
                            }
                            Some(updates)
                        }
                    },
                    Err(err) => return Err(err)
                }
            }
        };

        match result {
            None => {
                let request = self.bot.send(GetUpdates {
                    offset: Some(self.last_update + 1),
                    limit: None,
                    timeout: Some(self.timeout as Integer),
                    allowed_updates: Vec::new(),
                });

                self.current_request = Some(request);
                self.poll()
            },
            Some(mut updates) => {
                self.current_request = None;
                // Updates are guarantied to be not empty
                self.buffer.extend(updates.drain(1..));
                let result = updates.pop().unwrap();
                return Ok(Async::Ready(Some(result)))
            }
        }
    }
}

impl UpdatesStream {
    fn new(bot: &Bot) -> Self {
        UpdatesStream {
            bot: bot.clone(),
            last_update: 0,
            buffer: VecDeque::new(),
            current_request: None,
            timeout: TELEGRAM_LONG_POLL_TIMEOUT,
        }
    }
}

#[derive(Debug, Clone)]
pub struct Bot {
    inner: Arc<BotInner>,
}

#[derive(Debug, Clone)]
struct BotInner {
    token: String,
    client: Client<HttpsConnector>,
}

impl Bot {
    pub fn from_token(handle: &Handle, token: &str) -> Result<Self> {
        let connector = HttpsConnector::new(1, handle);
        let config = Client::configure().connector(connector);

        Ok(Bot {
            inner: Arc::new(BotInner {
                token: token.to_string(),
                client: config.build(handle),
            }),
        })
    }

    pub fn stream(&self) -> UpdatesStream {
        UpdatesStream::new(self)
    }

    pub fn send<Req>(&self, request: Req) -> TelegramFuture<Req::Response>
        where Req: Request + 'static, <Req as Request>::Response: std::marker::Send + 'static {

        let url = result(url(&self.inner.token, request.name()));
        let body = futures::lazy(move || {
            serde_json::to_vec(&request).map(Body::from)
        }).map_err(From::from);

        let bot = self.clone();
        let response = url.join(body).and_then(move |(url, body)| {
            let mut http_request = hyper::client::Request::new(Method::Post, url);
            http_request.set_body(body);
            http_request.headers_mut().set(ContentType::json());

            bot.inner.client.request(http_request).map_err(From::from)
        });

        let bytes = response.and_then(|response| {
            response.body().map_err(From::from)
                .fold(vec![], |mut result, chunk| -> Result<Vec<u8>> {
                    result.extend_from_slice(&chunk);
                    Ok(result)
            })
        });

        let future = bytes.and_then(|bytes| {
            result(serde_json::from_slice(&bytes).map_err(From::from).and_then(|value| {
                match value {
                    Response::Success {result} => Ok(result),
                    Response::Error { description, parameters } => {
                        Err(ErrorKind::TelegramError {
                            description: description,
                            parameters: parameters
                        }.into())
                    },
                }
            }))
        });

        TelegramFuture {
            inner: Box::new(future)
        }
    }
}

fn url(token: &str, method: &str) -> Result<Url> {
    Url::parse(&format!("{}bot{}/{}", TELEGRAM_URL, token, method)).map_err(From::from)
}
<commit_msg>feat: add must_use pragma for stream and future<commit_after>#[macro_use]
extern crate error_chain;
extern crate futures;
extern crate hyper;
extern crate hyper_tls;
extern crate serde_json;
extern crate tokio_core;
extern crate url;
extern crate telegram_bot_raw;

use std::sync::Arc;
use std::cmp::max;
use std::collections::VecDeque;

use futures::{Future, Stream, Poll, Async};
use futures::future::{result};
use hyper::{Body, Method};
use hyper::client::Client;
use hyper::header::ContentType;
use hyper_tls::HttpsConnector;
use tokio_core::reactor::Handle;
use url::Url;

use telegram_bot_raw::{GetUpdates, Request, Response, ResponseParameters, Update, Integer};

const TELEGRAM_URL: &'static str = "https://api.telegram.org/";
const TELEGRAM_LONG_POLL_TIMEOUT: usize = 5;

error_chain! {
    foreign_links {
        Url(url::ParseError);
        Hyper(hyper::Error);
        Json(serde_json::Error);
    }

    errors {
        TelegramError {
            description: String,
            parameters: Option<ResponseParameters>
        }
    }
}

#[must_use = "futures do nothing unless polled"]
pub struct TelegramFuture<T> {
    inner: Box<Future<Item=T, Error=Error>>
}

impl<T> Future for TelegramFuture<T> {
    type Item = T;
    type Error = Error;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        self.inner.poll()
    }
}

#[must_use = "streams do nothing unless polled"]
pub struct UpdatesStream {
    bot: Bot,
    last_update: Integer,
    buffer: VecDeque<Update>,
    current_request: Option<TelegramFuture<Vec<Update>>>,
    timeout: usize,
}

impl Stream for UpdatesStream {
    type Item = Update;
    type Error = Error;

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        if let Some(value) = self.buffer.pop_front() {
            return Ok(Async::Ready(Some(value)))
        }

        let result = match self.current_request {
            None => None,
            Some(ref mut current_request) => {
                let polled_update = current_request.poll();
                match polled_update {
                    Ok(Async::NotReady) => return Ok(Async::NotReady),
                    Ok(Async::Ready(updates)) => {
                        if updates.is_empty() {
                            None
                        } else {
                            for update in updates.iter() {
                                self.last_update = max(update.id, self.last_update);
                            }
                            Some(updates)
                        }
                    },
                    Err(err) => return Err(err)
                }
            }
        };

        match result {
            None => {
                let request = self.bot.send(GetUpdates {
                    offset: Some(self.last_update + 1),
                    limit: None,
                    timeout: Some(self.timeout as Integer),
                    allowed_updates: Vec::new(),
                });

                self.current_request = Some(request);
                self.poll()
            },
            Some(mut updates) => {
                self.current_request = None;
                // Updates are guarantied to be not empty
                self.buffer.extend(updates.drain(1..));
                let result = updates.pop().unwrap();
                return Ok(Async::Ready(Some(result)))
            }
        }
    }
}

impl UpdatesStream {
    fn new(bot: &Bot) -> Self {
        UpdatesStream {
            bot: bot.clone(),
            last_update: 0,
            buffer: VecDeque::new(),
            current_request: None,
            timeout: TELEGRAM_LONG_POLL_TIMEOUT,
        }
    }
}

#[derive(Debug, Clone)]
pub struct Bot {
    inner: Arc<BotInner>,
}

#[derive(Debug, Clone)]
struct BotInner {
    token: String,
    client: Client<HttpsConnector>,
}

impl Bot {
    pub fn from_token(handle: &Handle, token: &str) -> Result<Self> {
        let connector = HttpsConnector::new(1, handle);
        let config = Client::configure().connector(connector);

        Ok(Bot {
            inner: Arc::new(BotInner {
                token: token.to_string(),
                client: config.build(handle),
            }),
        })
    }

    pub fn stream(&self) -> UpdatesStream {
        UpdatesStream::new(self)
    }

    pub fn send<Req>(&self, request: Req) -> TelegramFuture<Req::Response>
        where Req: Request + 'static, <Req as Request>::Response: std::marker::Send + 'static {

        let url = result(url(&self.inner.token, request.name()));
        let body = futures::lazy(move || {
            serde_json::to_vec(&request).map(Body::from)
        }).map_err(From::from);

        let bot = self.clone();
        let response = url.join(body).and_then(move |(url, body)| {
            let mut http_request = hyper::client::Request::new(Method::Post, url);
            http_request.set_body(body);
            http_request.headers_mut().set(ContentType::json());

            bot.inner.client.request(http_request).map_err(From::from)
        });

        let bytes = response.and_then(|response| {
            response.body().map_err(From::from)
                .fold(vec![], |mut result, chunk| -> Result<Vec<u8>> {
                    result.extend_from_slice(&chunk);
                    Ok(result)
            })
        });

        let future = bytes.and_then(|bytes| {
            result(serde_json::from_slice(&bytes).map_err(From::from).and_then(|value| {
                match value {
                    Response::Success {result} => Ok(result),
                    Response::Error { description, parameters } => {
                        Err(ErrorKind::TelegramError {
                            description: description,
                            parameters: parameters
                        }.into())
                    },
                }
            }))
        });

        TelegramFuture {
            inner: Box::new(future)
        }
    }
}

fn url(token: &str, method: &str) -> Result<Url> {
    Url::parse(&format!("{}bot{}/{}", TELEGRAM_URL, token, method)).map_err(From::from)
}
<|endoftext|>


<commit_before>//! cargo_check provides the functionality needed to run `cargo check` or
//! another compatible command (f.x. clippy) in a background thread and provide
//! LSP diagnostics based on the output of the command.
use cargo_metadata::Message;
use crossbeam_channel::{never, select, unbounded, Receiver, RecvError, Sender};
use lsp_types::{
    CodeAction, CodeActionOrCommand, Diagnostic, Url, WorkDoneProgress, WorkDoneProgressBegin,
    WorkDoneProgressEnd, WorkDoneProgressReport,
};
use std::{
    io::{BufRead, BufReader},
    path::{Path, PathBuf},
    process::{Child, Command, Stdio},
    thread::JoinHandle,
    time::Instant,
};

mod conv;

use crate::conv::{map_rust_diagnostic_to_lsp, MappedRustDiagnostic};

pub use crate::conv::url_from_path_with_drive_lowercasing;

#[derive(Clone, Debug)]
pub struct CheckOptions {
    pub enable: bool,
    pub args: Vec<String>,
    pub command: String,
    pub all_targets: bool,
}

/// CheckWatcher wraps the shared state and communication machinery used for
/// running `cargo check` (or other compatible command) and providing
/// diagnostics based on the output.
/// The spawned thread is shut down when this struct is dropped.
#[derive(Debug)]
pub struct CheckWatcher {
    pub task_recv: Receiver<CheckTask>,
    cmd_send: Option<Sender<CheckCommand>>,
    handle: Option<JoinHandle<()>>,
}

impl CheckWatcher {
    pub fn new(options: &CheckOptions, workspace_root: PathBuf) -> CheckWatcher {
        let options = options.clone();

        let (task_send, task_recv) = unbounded::<CheckTask>();
        let (cmd_send, cmd_recv) = unbounded::<CheckCommand>();
        let handle = std::thread::spawn(move || {
            let mut check = CheckWatcherThread::new(options, workspace_root);
            check.run(&task_send, &cmd_recv);
        });
        CheckWatcher { task_recv, cmd_send: Some(cmd_send), handle: Some(handle) }
    }

    /// Returns a CheckWatcher that doesn't actually do anything
    pub fn dummy() -> CheckWatcher {
        CheckWatcher { task_recv: never(), cmd_send: None, handle: None }
    }

    /// Schedule a re-start of the cargo check worker.
    pub fn update(&self) {
        if let Some(cmd_send) = &self.cmd_send {
            cmd_send.send(CheckCommand::Update).unwrap();
        }
    }
}

impl std::ops::Drop for CheckWatcher {
    fn drop(&mut self) {
        if let Some(handle) = self.handle.take() {
            // Take the sender out of the option
            let recv = self.cmd_send.take();

            // Dropping the sender finishes the thread loop
            drop(recv);

            // Join the thread, it should finish shortly. We don't really care
            // whether it panicked, so it is safe to ignore the result
            let _ = handle.join();
        }
    }
}

#[derive(Debug)]
pub enum CheckTask {
    /// Request a clearing of all cached diagnostics from the check watcher
    ClearDiagnostics,

    /// Request adding a diagnostic with fixes included to a file
    AddDiagnostic { url: Url, diagnostic: Diagnostic, fixes: Vec<CodeActionOrCommand> },

    /// Request check progress notification to client
    Status(WorkDoneProgress),
}

pub enum CheckCommand {
    /// Request re-start of check thread
    Update,
}

struct CheckWatcherThread {
    options: CheckOptions,
    workspace_root: PathBuf,
    watcher: WatchThread,
    last_update_req: Option<Instant>,
}

impl CheckWatcherThread {
    fn new(options: CheckOptions, workspace_root: PathBuf) -> CheckWatcherThread {
        CheckWatcherThread {
            options,
            workspace_root,
            watcher: WatchThread::dummy(),
            last_update_req: None,
        }
    }

    fn run(&mut self, task_send: &Sender<CheckTask>, cmd_recv: &Receiver<CheckCommand>) {
        loop {
            select! {
                recv(&cmd_recv) -> cmd => match cmd {
                    Ok(cmd) => self.handle_command(cmd),
                    Err(RecvError) => {
                        // Command channel has closed, so shut down
                        break;
                    },
                },
                recv(self.watcher.message_recv) -> msg => match msg {
                    Ok(msg) => self.handle_message(msg, task_send),
                    Err(RecvError) => {
                        // Watcher finished, replace it with a never channel to
                        // avoid busy-waiting.
                        std::mem::replace(&mut self.watcher.message_recv, never());
                    },
                }
            };

            if self.should_recheck() {
                self.last_update_req.take();
                task_send.send(CheckTask::ClearDiagnostics).unwrap();

                // Replace with a dummy watcher first so we drop the original and wait for completion
                std::mem::replace(&mut self.watcher, WatchThread::dummy());

                // Then create the actual new watcher
                self.watcher = WatchThread::new(&self.options, &self.workspace_root);
            }
        }
    }

    fn should_recheck(&mut self) -> bool {
        if let Some(_last_update_req) = &self.last_update_req {
            // We currently only request an update on save, as we need up to
            // date source on disk for cargo check to do it's magic, so we
            // don't really need to debounce the requests at this point.
            return true;
        }
        false
    }

    fn handle_command(&mut self, cmd: CheckCommand) {
        match cmd {
            CheckCommand::Update => self.last_update_req = Some(Instant::now()),
        }
    }

    fn handle_message(&self, msg: CheckEvent, task_send: &Sender<CheckTask>) {
        match msg {
            CheckEvent::Begin => {
                task_send
                    .send(CheckTask::Status(WorkDoneProgress::Begin(WorkDoneProgressBegin {
                        title: "Running 'cargo check'".to_string(),
                        cancellable: Some(false),
                        message: None,
                        percentage: None,
                    })))
                    .unwrap();
            }

            CheckEvent::End => {
                task_send
                    .send(CheckTask::Status(WorkDoneProgress::End(WorkDoneProgressEnd {
                        message: None,
                    })))
                    .unwrap();
            }

            CheckEvent::Msg(Message::CompilerArtifact(msg)) => {
                task_send
                    .send(CheckTask::Status(WorkDoneProgress::Report(WorkDoneProgressReport {
                        cancellable: Some(false),
                        message: Some(msg.target.name),
                        percentage: None,
                    })))
                    .unwrap();
            }

            CheckEvent::Msg(Message::CompilerMessage(msg)) => {
                let map_result = map_rust_diagnostic_to_lsp(&msg.message, &self.workspace_root);
                if map_result.is_empty() {
                    return;
                }

                for MappedRustDiagnostic { location, diagnostic, fixes } in map_result {
                    let fixes = fixes
                        .into_iter()
                        .map(|fix| {
                            CodeAction { diagnostics: Some(vec![diagnostic.clone()]), ..fix }.into()
                        })
                        .collect();

                    task_send
                        .send(CheckTask::AddDiagnostic { url: location.uri, diagnostic, fixes })
                        .unwrap();
                }
            }

            CheckEvent::Msg(Message::BuildScriptExecuted(_msg)) => {}
            CheckEvent::Msg(Message::Unknown) => {}
        }
    }
}

#[derive(Debug)]
pub struct DiagnosticWithFixes {
    diagnostic: Diagnostic,
    fixes: Vec<CodeAction>,
}

/// WatchThread exists to wrap around the communication needed to be able to
/// run `cargo check` without blocking. Currently the Rust standard library
/// doesn't provide a way to read sub-process output without blocking, so we
/// have to wrap sub-processes output handling in a thread and pass messages
/// back over a channel.
/// The correct way to dispose of the thread is to drop it, on which the
/// sub-process will be killed, and the thread will be joined.
struct WatchThread {
    handle: Option<JoinHandle<()>>,
    message_recv: Receiver<CheckEvent>,
}

enum CheckEvent {
    Begin,
    Msg(cargo_metadata::Message),
    End,
}

pub fn run_cargo(
    args: &[String],
    current_dir: Option<&Path>,
    on_message: &mut dyn FnMut(cargo_metadata::Message) -> bool,
) -> Child {
    let mut command = Command::new("cargo");
    if let Some(current_dir) = current_dir {
        command.current_dir(current_dir);
    }

    let mut child = command
        .args(args)
        .stdout(Stdio::piped())
        .stderr(Stdio::null())
        .stdin(Stdio::null())
        .spawn()
        .expect("couldn't launch cargo");

    // We manually read a line at a time, instead of using serde's
    // stream deserializers, because the deserializer cannot recover
    // from an error, resulting in it getting stuck, because we try to
    // be resillient against failures.
    //
    // Because cargo only outputs one JSON object per line, we can
    // simply skip a line if it doesn't parse, which just ignores any
    // erroneus output.
    let stdout = BufReader::new(child.stdout.take().unwrap());
    for line in stdout.lines() {
        let line = match line {
            Ok(line) => line,
            Err(err) => {
                log::error!("Couldn't read line from cargo: {}", err);
                continue;
            }
        };

        let message = serde_json::from_str::<cargo_metadata::Message>(&line);
        let message = match message {
            Ok(message) => message,
            Err(err) => {
                log::error!("Invalid json from cargo check, ignoring ({}): {:?} ", err, line);
                continue;
            }
        };

        if !on_message(message) {
            break;
        }
    }

    child
}

impl WatchThread {
    fn dummy() -> WatchThread {
        WatchThread { handle: None, message_recv: never() }
    }

    fn new(options: &CheckOptions, workspace_root: &Path) -> WatchThread {
        let mut args: Vec<String> = vec![
            options.command.clone(),
            "--workspace".to_string(),
            "--message-format=json".to_string(),
            "--manifest-path".to_string(),
            format!("{}/Cargo.toml", workspace_root.display()),
        ];
        if options.all_targets {
            args.push("--all-targets".to_string());
        }
        args.extend(options.args.iter().cloned());

        let (message_send, message_recv) = unbounded();
        let workspace_root = workspace_root.to_owned();
        let handle = if options.enable {
            Some(std::thread::spawn(move || {
                // If we trigger an error here, we will do so in the loop instead,
                // which will break out of the loop, and continue the shutdown
                let _ = message_send.send(CheckEvent::Begin);

                let mut child = run_cargo(&args, Some(&workspace_root), &mut |message| {
                    // Skip certain kinds of messages to only spend time on what's useful
                    match &message {
                        Message::CompilerArtifact(artifact) if artifact.fresh => return true,
                        Message::BuildScriptExecuted(_) => return true,
                        Message::Unknown => return true,
                        _ => {}
                    }

                    match message_send.send(CheckEvent::Msg(message)) {
                        Ok(()) => {}
                        Err(_err) => {
                            // The send channel was closed, so we want to shutdown
                            return false;
                        }
                    };

                    true
                });

                // We can ignore any error here, as we are already in the progress
                // of shutting down.
                let _ = message_send.send(CheckEvent::End);

                // It is okay to ignore the result, as it only errors if the process is already dead
                let _ = child.kill();

                // Again, we don't care about the exit status so just ignore the result
                let _ = child.wait();
            }))
        } else {
            None
        };
        WatchThread { handle, message_recv }
    }
}

impl std::ops::Drop for WatchThread {
    fn drop(&mut self) {
        if let Some(handle) = self.handle.take() {
            // Replace our reciever with dummy one, so we can drop and close the
            // one actually communicating with the thread
            let recv = std::mem::replace(&mut self.message_recv, never());

            // Dropping the original reciever initiates thread sub-process shutdown
            drop(recv);

            // Join the thread, it should finish shortly. We don't really care
            // whether it panicked, so it is safe to ignore the result
            let _ = handle.join();
        }
    }
}
<commit_msg>ra_cargo_watch: log more errors<commit_after>//! cargo_check provides the functionality needed to run `cargo check` or
//! another compatible command (f.x. clippy) in a background thread and provide
//! LSP diagnostics based on the output of the command.
use cargo_metadata::Message;
use crossbeam_channel::{never, select, unbounded, Receiver, RecvError, Sender};
use lsp_types::{
    CodeAction, CodeActionOrCommand, Diagnostic, Url, WorkDoneProgress, WorkDoneProgressBegin,
    WorkDoneProgressEnd, WorkDoneProgressReport,
};
use std::{
    io::{BufRead, BufReader},
    path::{Path, PathBuf},
    process::{Child, Command, Stdio},
    thread::JoinHandle,
    time::Instant,
};

mod conv;

use crate::conv::{map_rust_diagnostic_to_lsp, MappedRustDiagnostic};

pub use crate::conv::url_from_path_with_drive_lowercasing;

#[derive(Clone, Debug)]
pub struct CheckOptions {
    pub enable: bool,
    pub args: Vec<String>,
    pub command: String,
    pub all_targets: bool,
}

/// CheckWatcher wraps the shared state and communication machinery used for
/// running `cargo check` (or other compatible command) and providing
/// diagnostics based on the output.
/// The spawned thread is shut down when this struct is dropped.
#[derive(Debug)]
pub struct CheckWatcher {
    pub task_recv: Receiver<CheckTask>,
    cmd_send: Option<Sender<CheckCommand>>,
    handle: Option<JoinHandle<()>>,
}

impl CheckWatcher {
    pub fn new(options: &CheckOptions, workspace_root: PathBuf) -> CheckWatcher {
        let options = options.clone();

        let (task_send, task_recv) = unbounded::<CheckTask>();
        let (cmd_send, cmd_recv) = unbounded::<CheckCommand>();
        let handle = std::thread::spawn(move || {
            let mut check = CheckWatcherThread::new(options, workspace_root);
            check.run(&task_send, &cmd_recv);
        });
        CheckWatcher { task_recv, cmd_send: Some(cmd_send), handle: Some(handle) }
    }

    /// Returns a CheckWatcher that doesn't actually do anything
    pub fn dummy() -> CheckWatcher {
        CheckWatcher { task_recv: never(), cmd_send: None, handle: None }
    }

    /// Schedule a re-start of the cargo check worker.
    pub fn update(&self) {
        if let Some(cmd_send) = &self.cmd_send {
            cmd_send.send(CheckCommand::Update).unwrap();
        }
    }
}

impl std::ops::Drop for CheckWatcher {
    fn drop(&mut self) {
        if let Some(handle) = self.handle.take() {
            // Take the sender out of the option
            let recv = self.cmd_send.take();

            // Dropping the sender finishes the thread loop
            drop(recv);

            // Join the thread, it should finish shortly. We don't really care
            // whether it panicked, so it is safe to ignore the result
            let _ = handle.join();
        }
    }
}

#[derive(Debug)]
pub enum CheckTask {
    /// Request a clearing of all cached diagnostics from the check watcher
    ClearDiagnostics,

    /// Request adding a diagnostic with fixes included to a file
    AddDiagnostic { url: Url, diagnostic: Diagnostic, fixes: Vec<CodeActionOrCommand> },

    /// Request check progress notification to client
    Status(WorkDoneProgress),
}

pub enum CheckCommand {
    /// Request re-start of check thread
    Update,
}

struct CheckWatcherThread {
    options: CheckOptions,
    workspace_root: PathBuf,
    watcher: WatchThread,
    last_update_req: Option<Instant>,
}

impl CheckWatcherThread {
    fn new(options: CheckOptions, workspace_root: PathBuf) -> CheckWatcherThread {
        CheckWatcherThread {
            options,
            workspace_root,
            watcher: WatchThread::dummy(),
            last_update_req: None,
        }
    }

    fn run(&mut self, task_send: &Sender<CheckTask>, cmd_recv: &Receiver<CheckCommand>) {
        loop {
            select! {
                recv(&cmd_recv) -> cmd => match cmd {
                    Ok(cmd) => self.handle_command(cmd),
                    Err(RecvError) => {
                        // Command channel has closed, so shut down
                        break;
                    },
                },
                recv(self.watcher.message_recv) -> msg => match msg {
                    Ok(msg) => self.handle_message(msg, task_send),
                    Err(RecvError) => {
                        // Watcher finished, replace it with a never channel to
                        // avoid busy-waiting.
                        std::mem::replace(&mut self.watcher.message_recv, never());
                    },
                }
            };

            if self.should_recheck() {
                self.last_update_req.take();
                task_send.send(CheckTask::ClearDiagnostics).unwrap();

                // Replace with a dummy watcher first so we drop the original and wait for completion
                std::mem::replace(&mut self.watcher, WatchThread::dummy());

                // Then create the actual new watcher
                self.watcher = WatchThread::new(&self.options, &self.workspace_root);
            }
        }
    }

    fn should_recheck(&mut self) -> bool {
        if let Some(_last_update_req) = &self.last_update_req {
            // We currently only request an update on save, as we need up to
            // date source on disk for cargo check to do it's magic, so we
            // don't really need to debounce the requests at this point.
            return true;
        }
        false
    }

    fn handle_command(&mut self, cmd: CheckCommand) {
        match cmd {
            CheckCommand::Update => self.last_update_req = Some(Instant::now()),
        }
    }

    fn handle_message(&self, msg: CheckEvent, task_send: &Sender<CheckTask>) {
        match msg {
            CheckEvent::Begin => {
                task_send
                    .send(CheckTask::Status(WorkDoneProgress::Begin(WorkDoneProgressBegin {
                        title: "Running 'cargo check'".to_string(),
                        cancellable: Some(false),
                        message: None,
                        percentage: None,
                    })))
                    .unwrap();
            }

            CheckEvent::End => {
                task_send
                    .send(CheckTask::Status(WorkDoneProgress::End(WorkDoneProgressEnd {
                        message: None,
                    })))
                    .unwrap();
            }

            CheckEvent::Msg(Message::CompilerArtifact(msg)) => {
                task_send
                    .send(CheckTask::Status(WorkDoneProgress::Report(WorkDoneProgressReport {
                        cancellable: Some(false),
                        message: Some(msg.target.name),
                        percentage: None,
                    })))
                    .unwrap();
            }

            CheckEvent::Msg(Message::CompilerMessage(msg)) => {
                let map_result = map_rust_diagnostic_to_lsp(&msg.message, &self.workspace_root);
                if map_result.is_empty() {
                    return;
                }

                for MappedRustDiagnostic { location, diagnostic, fixes } in map_result {
                    let fixes = fixes
                        .into_iter()
                        .map(|fix| {
                            CodeAction { diagnostics: Some(vec![diagnostic.clone()]), ..fix }.into()
                        })
                        .collect();

                    task_send
                        .send(CheckTask::AddDiagnostic { url: location.uri, diagnostic, fixes })
                        .unwrap();
                }
            }

            CheckEvent::Msg(Message::BuildScriptExecuted(_msg)) => {}
            CheckEvent::Msg(Message::Unknown) => {}
        }
    }
}

#[derive(Debug)]
pub struct DiagnosticWithFixes {
    diagnostic: Diagnostic,
    fixes: Vec<CodeAction>,
}

/// WatchThread exists to wrap around the communication needed to be able to
/// run `cargo check` without blocking. Currently the Rust standard library
/// doesn't provide a way to read sub-process output without blocking, so we
/// have to wrap sub-processes output handling in a thread and pass messages
/// back over a channel.
/// The correct way to dispose of the thread is to drop it, on which the
/// sub-process will be killed, and the thread will be joined.
struct WatchThread {
    handle: Option<JoinHandle<()>>,
    message_recv: Receiver<CheckEvent>,
}

enum CheckEvent {
    Begin,
    Msg(cargo_metadata::Message),
    End,
}

pub fn run_cargo(
    args: &[String],
    current_dir: Option<&Path>,
    on_message: &mut dyn FnMut(cargo_metadata::Message) -> bool,
) -> Child {
    let mut command = Command::new("cargo");
    if let Some(current_dir) = current_dir {
        command.current_dir(current_dir);
    }

    let mut child = command
        .args(args)
        .stdout(Stdio::piped())
        .stderr(Stdio::piped())
        .stdin(Stdio::null())
        .spawn()
        .expect("couldn't launch cargo");

    // We manually read a line at a time, instead of using serde's
    // stream deserializers, because the deserializer cannot recover
    // from an error, resulting in it getting stuck, because we try to
    // be resillient against failures.
    //
    // Because cargo only outputs one JSON object per line, we can
    // simply skip a line if it doesn't parse, which just ignores any
    // erroneus output.
    let stdout = BufReader::new(child.stdout.take().unwrap());
    for line in stdout.lines() {
        let line = match line {
            Ok(line) => line,
            Err(err) => {
                log::error!("Couldn't read line from cargo: {}", err);
                continue;
            }
        };

        let message = serde_json::from_str::<cargo_metadata::Message>(&line);
        let message = match message {
            Ok(message) => message,
            Err(err) => {
                log::error!("Invalid json from cargo check, ignoring ({}): {:?} ", err, line);
                continue;
            }
        };

        if !on_message(message) {
            break;
        }
    }

    child
}

impl WatchThread {
    fn dummy() -> WatchThread {
        WatchThread { handle: None, message_recv: never() }
    }

    fn new(options: &CheckOptions, workspace_root: &Path) -> WatchThread {
        let mut args: Vec<String> = vec![
            options.command.clone(),
            "--workspace".to_string(),
            "--message-format=json".to_string(),
            "--manifest-path".to_string(),
            format!("{}/Cargo.toml", workspace_root.display()),
        ];
        if options.all_targets {
            args.push("--all-targets".to_string());
        }
        args.extend(options.args.iter().cloned());

        let (message_send, message_recv) = unbounded();
        let workspace_root = workspace_root.to_owned();
        let handle = if options.enable {
            Some(std::thread::spawn(move || {
                // If we trigger an error here, we will do so in the loop instead,
                // which will break out of the loop, and continue the shutdown
                let _ = message_send.send(CheckEvent::Begin);

                let mut child = run_cargo(&args, Some(&workspace_root), &mut |message| {
                    // Skip certain kinds of messages to only spend time on what's useful
                    match &message {
                        Message::CompilerArtifact(artifact) if artifact.fresh => return true,
                        Message::BuildScriptExecuted(_) => return true,
                        Message::Unknown => return true,
                        _ => {}
                    }

                    match message_send.send(CheckEvent::Msg(message)) {
                        Ok(()) => {}
                        Err(_err) => {
                            // The send channel was closed, so we want to shutdown
                            return false;
                        }
                    };

                    true
                });

                // We can ignore any error here, as we are already in the progress
                // of shutting down.
                let _ = message_send.send(CheckEvent::End);

                // It is okay to ignore the result, as it only errors if the process is already dead
                let _ = child.kill();

                // Again, we are resilient to errors, so we don't try to panic here
                match child.wait_with_output() {
                    Ok(output) => match output.status.code() {
                        Some(0) | None => {}
                        Some(exit_code) => {
                            let output =
                                std::str::from_utf8(&output.stderr).unwrap_or("<bad utf8 output>");

                            if !output.contains("could not compile") {
                                log::error!("Cargo failed with exit code {} {}", exit_code, output);
                            }
                        }
                    },
                    Err(err) => log::error!("Cargo io error: {:?}", err),
                }
            }))
        } else {
            None
        };
        WatchThread { handle, message_recv }
    }
}

impl std::ops::Drop for WatchThread {
    fn drop(&mut self) {
        if let Some(handle) = self.handle.take() {
            // Replace our reciever with dummy one, so we can drop and close the
            // one actually communicating with the thread
            let recv = std::mem::replace(&mut self.message_recv, never());

            // Dropping the original reciever initiates thread sub-process shutdown
            drop(recv);

            // Join the thread, it should finish shortly. We don't really care
            // whether it panicked, so it is safe to ignore the result
            let _ = handle.join();
        }
    }
}
<|endoftext|>