<commit_before>//! Utility functions for assisting with conversion of headers from and to the HTTP text form.

use std::io::IoResult;
use rfc2616::is_token;

/// Normalise an HTTP header name.
///
/// Rules:
///
/// - The first character is capitalised
/// - Any character immediately following `-` (HYPHEN-MINUS) is capitalised
/// - All other characters are made lowercase
///
/// This will fail if passed a non-ASCII name.
///
/// # Examples
///
/// ~~~ .{rust}
/// # use http::headers::serialization_utils::normalise_header_name;
/// assert_eq!(normalise_header_name(&String::from_str("foo-bar")), String::from_str("Foo-Bar"));
/// assert_eq!(normalise_header_name(&String::from_str("FOO-BAR")), String::from_str("Foo-Bar"));
/// ~~~
pub fn normalise_header_name(name: &String) -> String {
    let mut result: String = String::with_capacity(name.len());
    let mut capitalise = true;
    for c in name[].chars() {
        let c = match capitalise {
            true => c.to_ascii().to_uppercase(),
            false => c.to_ascii().to_lowercase(),
        };
        result.push(c.to_char());
        // ASCII 45 is '-': in that case, capitalise the next char
        capitalise = c.to_byte() == 45;
    }
    result
}

/// Split a value on commas, as is common for HTTP headers.
///
/// This does not handle quoted-strings intelligently.
///
/// # Examples
///
/// ~~~ .{rust}
/// # use http::headers::serialization_utils::comma_split;
/// assert_eq!(
///     comma_split(" en;q=0.8, en_AU, text/html"),
///     vec![String::from_str("en;q=0.8"), String::from_str("en_AU"), String::from_str("text/html")]
/// )
/// ~~~
pub fn comma_split(value: &str) -> Vec<String> {
    value.split(',').map(|w| String::from_str(w.trim_left())).collect()
}

pub fn comma_split_iter<'a>(value: &'a str)
        -> ::std::iter::Map<'a, &'a str, &'a str, ::std::str::CharSplits<'a, char>> {
    value.split(',').map(|w| w.trim_left())
}

pub trait WriterUtil: Writer {
    fn write_maybe_quoted_string(&mut self, s: &String) -> IoResult<()> {
        if is_token(s) {
            self.write(s.as_bytes())
        } else {
            self.write_quoted_string(s)
        }
    }

    fn write_quoted_string(&mut self, s: &String) -> IoResult<()> {
        try!(self.write(b"\""));
        for b in s.as_bytes().iter() {
            if *b == b'\\' || *b == b'"' {
                try!(self.write(b"\\"));
            }
            // XXX This doesn't seem right.
            try!(self.write(&[*b]));
        }
        self.write(b"\"")
    }

    fn write_parameter(&mut self, k: &str, v: &String) -> IoResult<()> {
        try!(self.write(k.as_bytes()));
        try!(self.write(b"="));
        self.write_maybe_quoted_string(v)
    }

    fn write_parameters(&mut self, parameters: &[(String, String)]) -> IoResult<()> {
        for &(ref k, ref v) in parameters.iter() {
            try!(self.write(b";"));
            try!(self.write_parameter(k[], v));
        }
        Ok(())
    }

    fn write_quality(&mut self, quality: Option<f64>) -> IoResult<()> {
        // TODO: remove second and third decimal places if zero, and use a better quality type anyway
        match quality {
            Some(qvalue) => write!(&mut *self, ";q={:0.3f}", qvalue),
            None => Ok(()),
        }
    }

    #[inline]
    fn write_token(&mut self, token: &String) -> IoResult<()> {
        assert!(is_token(token));
        self.write(token.as_bytes())
    }
}

impl<W: Writer> WriterUtil for W { }

/// Join a vector of values with commas, as is common for HTTP headers.
///
/// # Examples
///
/// ~~~ .{rust}
/// # use http::headers::serialization_utils::comma_join;
/// assert_eq!(
///     comma_join([String::from_str("en;q=0.8"), String::from_str("en_AU"), String::from_str("text/html")]),
///     String::from_str("en;q=0.8, en_AU, text/html")
/// )
/// ~~~
#[inline]
pub fn comma_join(values: &[String]) -> String {
    let mut out = String::new();
    let mut iter = values.iter();
    match iter.next() {
        Some(s) => out.push_str(s[]),<commit_msg>Rust update: no more `[T, ..n]` -> `&[T]` coercion<commit_after>//! Utility functions for assisting with conversion of headers from and to the HTTP text form.

use std::io::IoResult;
use rfc2616::is_token;

/// Normalise an HTTP header name.
///
/// Rules:
///
/// - The first character is capitalised
/// - Any character immediately following `-` (HYPHEN-MINUS) is capitalised
/// - All other characters are made lowercase
///
/// This will fail if passed a non-ASCII name.
///
/// # Examples
///
/// ~~~ .{rust}
/// # use http::headers::serialization_utils::normalise_header_name;
/// assert_eq!(normalise_header_name(&String::from_str("foo-bar")), String::from_str("Foo-Bar"));
/// assert_eq!(normalise_header_name(&String::from_str("FOO-BAR")), String::from_str("Foo-Bar"));
/// ~~~
pub fn normalise_header_name(name: &String) -> String {
    let mut result: String = String::with_capacity(name.len());
    let mut capitalise = true;
    for c in name[].chars() {
        let c = match capitalise {
            true => c.to_ascii().to_uppercase(),
            false => c.to_ascii().to_lowercase(),
        };
        result.push(c.to_char());
        // ASCII 45 is '-': in that case, capitalise the next char
        capitalise = c.to_byte() == 45;
    }
    result
}

/// Split a value on commas, as is common for HTTP headers.
///
/// This does not handle quoted-strings intelligently.
///
/// # Examples
///
/// ~~~ .{rust}
/// # use http::headers::serialization_utils::comma_split;
/// assert_eq!(
///     comma_split(" en;q=0.8, en_AU, text/html"),
///     vec![String::from_str("en;q=0.8"), String::from_str("en_AU"), String::from_str("text/html")]
/// )
/// ~~~
pub fn comma_split(value: &str) -> Vec<String> {
    value.split(',').map(|w| String::from_str(w.trim_left())).collect()
}

pub fn comma_split_iter<'a>(value: &'a str)
        -> ::std::iter::Map<'a, &'a str, &'a str, ::std::str::CharSplits<'a, char>> {
    value.split(',').map(|w| w.trim_left())
}

pub trait WriterUtil: Writer {
    fn write_maybe_quoted_string(&mut self, s: &String) -> IoResult<()> {
        if is_token(s) {
            self.write(s.as_bytes())
        } else {
            self.write_quoted_string(s)
        }
    }

    fn write_quoted_string(&mut self, s: &String) -> IoResult<()> {
        try!(self.write(b"\""));
        for b in s.as_bytes().iter() {
            if *b == b'\\' || *b == b'"' {
                try!(self.write(b"\\"));
            }
            // XXX This doesn't seem right.
            try!(self.write(&[*b]));
        }
        self.write(b"\"")
    }

    fn write_parameter(&mut self, k: &str, v: &String) -> IoResult<()> {
        try!(self.write(k.as_bytes()));
        try!(self.write(b"="));
        self.write_maybe_quoted_string(v)
    }

    fn write_parameters(&mut self, parameters: &[(String, String)]) -> IoResult<()> {
        for &(ref k, ref v) in parameters.iter() {
            try!(self.write(b";"));
            try!(self.write_parameter(k[], v));
        }
        Ok(())
    }

    fn write_quality(&mut self, quality: Option<f64>) -> IoResult<()> {
        // TODO: remove second and third decimal places if zero, and use a better quality type anyway
        match quality {
            Some(qvalue) => write!(&mut *self, ";q={:0.3f}", qvalue),
            None => Ok(()),
        }
    }

    #[inline]
    fn write_token(&mut self, token: &String) -> IoResult<()> {
        assert!(is_token(token));
        self.write(token.as_bytes())
    }
}

impl<W: Writer> WriterUtil for W { }

/// Join a vector of values with commas, as is common for HTTP headers.
///
/// # Examples
///
/// ~~~ .{rust}
/// # use http::headers::serialization_utils::comma_join;
/// assert_eq!(
///     comma_join(&[String::from_str("en;q=0.8"), String::from_str("en_AU"), String::from_str("text/html")]),
///     String::from_str("en;q=0.8, en_AU, text/html")
/// )
/// ~~~
#[inline]
pub fn comma_join(values: &[String]) -> String {
    let mut out = String::new();
    let mut iter = values.iter();
    match iter.next() {
        Some(s) => out.push_str(s[]),
{'fraction_non_alphanumeric': 0.2033439881976887, 'fraction_numerical': 0.004917629702483403, 'mean_word_length': 2.5868606701940036, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 58, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7306687', 'n_tokens_mistral': 2783, 'n_tokens_neox': 2734, 'n_words': 618}