<commit_before>use rustc_serialize::json;
use hyper;
use hyper::{Client};
use url::form_urlencoded;

use std::io::Read;
use std::borrow::Cow;

use text_view::view_as_text;
use text_view::insert_annotation;
use token::Token;
use token::Data;
use error::{Error, Result, Source};

/// Represents a grammar error from language tool
///
/// Note: lots of fields are missing 
#[derive(RustcDecodable, RustcEncodable, Debug)]
pub struct GrammarError {
    pub message: String,
    pub offset: usize,
    pub length: usize,
    pub short_message: Option<String>,
    pub issue_type: Option<String>,
}

/// Contains a list of matches to errors
///
/// Corresponds to the JSON that LanguageTool-server sends back
///
/// Note: lots of fields are missing
#[derive(RustcDecodable, RustcEncodable, Debug)]
pub struct GrammarCheck {
    pub matches: Vec<GrammarError>,
}

impl GrammarCheck {
    /// Send a query to LanguageTools server and get back a list of errors
    pub fn new(text: &str, port: usize, lang: &str) -> Result<GrammarCheck> {
        let query: String = form_urlencoded::Serializer::new(String::new())
            .append_pair("language", lang)
            .append_pair("text", text)
            .finish();
        
        let client = Client::new();
        
        let mut res = try!(client.post(&format!("http://localhost:{}/v2/check", port))
                           .body(&query)
                           .send()
                           .map_err(|e| Error::default(Source::empty(),
                                                       format!("could not send request to server: {}", e))));

        if res.status != hyper::Ok {
            return Err(Error::default(Source::empty(),
                                      format!("server didn't respond with a OK status code")));
        }
        
        let mut s = String::new();
        try!(res.read_to_string(&mut s)
             .map_err(|e| Error::default(Source::empty(),
                                         format!("could not read response: {}", e))));
        let reponse: GrammarCheck = try!(json::decode(&s).map_err(|e| Error::default(Source::empty(),
                                                                                     format!("could not decode JSON: {}", e))));
        Ok(reponse)
    }
}

fn escape_query<'a>(s: &str) -> Cow<'a, str> {
    let mut res = String::with_capacity(s.len());
    for c in s.chars() {
        match c {
            '&' => res.push_str("%26"),
            _ => res.push(c),
        }
    }
    Cow::Owned(res)
}

/// Check the grammar in a chapter
///
/// This modifies the AST
pub fn check_grammar(tokens: &mut Vec<Token>, lang: &str) -> Result<()> {
    let input = view_as_text(tokens);
    let check = try!(GrammarCheck::new(&input, 8081, lang));
    
    for error in check.matches {
        insert_annotation(tokens, &Data::GrammarError(error.message.clone()), error.offset, error.length);
    }
    Ok(())
}

<commit_msg>Remove now useless escape_query function<commit_after>use rustc_serialize::json;
use hyper;
use hyper::{Client};
use url::form_urlencoded;

use std::io::Read;
use std::borrow::Cow;

use text_view::view_as_text;
use text_view::insert_annotation;
use token::Token;
use token::Data;
use error::{Error, Result, Source};

/// Represents a grammar error from language tool
///
/// Note: lots of fields are missing 
#[derive(RustcDecodable, RustcEncodable, Debug)]
pub struct GrammarError {
    pub message: String,
    pub offset: usize,
    pub length: usize,
    pub short_message: Option<String>,
    pub issue_type: Option<String>,
}

/// Contains a list of matches to errors
///
/// Corresponds to the JSON that LanguageTool-server sends back
///
/// Note: lots of fields are missing
#[derive(RustcDecodable, RustcEncodable, Debug)]
pub struct GrammarCheck {
    pub matches: Vec<GrammarError>,
}

impl GrammarCheck {
    /// Send a query to LanguageTools server and get back a list of errors
    pub fn new(text: &str, port: usize, lang: &str) -> Result<GrammarCheck> {
        let query: String = form_urlencoded::Serializer::new(String::new())
            .append_pair("language", lang)
            .append_pair("text", text)
            .finish();
        
        let client = Client::new();
        
        let mut res = try!(client.post(&format!("http://localhost:{}/v2/check", port))
                           .body(&query)
                           .send()
                           .map_err(|e| Error::default(Source::empty(),
                                                       format!("could not send request to server: {}", e))));

        if res.status != hyper::Ok {
            return Err(Error::default(Source::empty(),
                                      format!("server didn't respond with a OK status code")));
        }
        
        let mut s = String::new();
        try!(res.read_to_string(&mut s)
             .map_err(|e| Error::default(Source::empty(),
                                         format!("could not read response: {}", e))));
        let reponse: GrammarCheck = try!(json::decode(&s).map_err(|e| Error::default(Source::empty(),
                                                                                     format!("could not decode JSON: {}", e))));
        Ok(reponse)
    }
}


/// Check the grammar in a chapter
///
/// This modifies the AST
pub fn check_grammar(tokens: &mut Vec<Token>, lang: &str) -> Result<()> {
    let input = view_as_text(tokens);
    let check = try!(GrammarCheck::new(&input, 8081, lang));
    
    for error in check.matches {
        insert_annotation(tokens, &Data::GrammarError(error.message.clone()), error.offset, error.length);
    }
    Ok(())
}


{'fraction_non_alphanumeric': 0.1418054338299737, 'fraction_numerical': 0.0021034180543382996, 'mean_word_length': 2.180602006688963, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 24, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 16, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7357916', 'n_tokens_mistral': 1611, 'n_tokens_neox': 1622, 'n_words': 446}