<commit_before>//! Module for parsing ISO Base Media Format aka video/mp4 streams.
//! Internal unit tests.

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

use std::io::Cursor;
use super::*;
extern crate test_assembler;
use self::test_assembler::*;

enum BoxSize {
    Short(u32),
    Long(u64),
    UncheckedShort(u32),
    UncheckedLong(u64),
}

fn make_box_raw<F>(size: BoxSize, name: &[u8; 4], func: F) -> Cursor<Vec<u8>>
    where F: Fn(Section) -> Section
{
    let mut section = Section::new();
    section = match size {
        BoxSize::Short(size) | BoxSize::UncheckedShort(size) => section.B32(size),
        BoxSize::Long(_) | BoxSize::UncheckedLong(_) => section.B32(1),
    };
    section = section.append_bytes(name);
    section = match size {
        // The spec allows the 32-bit size to be 0 to indicate unknown
        // length streams.  It's not clear if this is valid when using a
        // 64-bit size, so prohibit it for now.
        BoxSize::Long(size) => {
            assert!(size > 0);
            section.B64(size)
        }
        BoxSize::UncheckedLong(size) => section.B64(size),
        _ => section,
    };
    section = func(section);
    match size {
        BoxSize::Short(size) => {
            if size > 0 {
                assert_eq!(size as u64, section.size())
            }
        }
        BoxSize::Long(size) => assert_eq!(size, section.size()),
        _ => (),
    }
    Cursor::new(section.get_contents().unwrap())
}

fn make_box<F>(size: u32, name: &[u8; 4], func: F) -> Cursor<Vec<u8>>
    where F: Fn(Section) -> Section
{
    make_box_raw(BoxSize::Short(size), name, func)
}

fn make_fullbox<F>(size: u32, name: &[u8; 4], version: u8, func: F) -> Cursor<Vec<u8>>
    where F: Fn(Section) -> Section
{
    make_box_raw(BoxSize::Short(size), name, |s| {
        func(s.B8(version)
              .B8(0)
              .B8(0)
              .B8(0))
    })
}

#[test]
fn read_box_header_short() {
    let mut stream = make_box_raw(BoxSize::Short(8), b"test", |s| s);
    let parsed = read_box_header(&mut stream).unwrap();
    assert_eq!(parsed.name, FourCC(*b"test"));
    assert_eq!(parsed.size, 8);
}

#[test]
fn read_box_header_long() {
    let mut stream = make_box_raw(BoxSize::Long(16), b"test", |s| s);
    let parsed = read_box_header(&mut stream).unwrap();
    assert_eq!(parsed.name, FourCC(*b"test"));
    assert_eq!(parsed.size, 16);
}

#[test]
fn read_box_header_short_unknown_size() {
    let mut stream = make_box_raw(BoxSize::Short(0), b"test", |s| s);
    match read_box_header(&mut stream) {
        Err(Error::Unsupported) => (),
        _ => panic!("unexpected result reading box with unknown size"),
    };
}

#[test]
fn read_box_header_short_invalid_size() {
    let mut stream = make_box_raw(BoxSize::UncheckedShort(2), b"test", |s| s);
    match read_box_header(&mut stream) {
        Err(Error::InvalidData) => (),
        _ => panic!("unexpected result reading box with invalid size"),
    };
}

#[test]
fn read_box_header_long_invalid_size() {
    let mut stream = make_box_raw(BoxSize::UncheckedLong(2), b"test", |s| s);
    match read_box_header(&mut stream) {
        Err(Error::InvalidData) => (),
        _ => panic!("unexpected result reading box with invalid size"),
    };
}

#[test]
fn read_ftyp() {
    let mut stream = make_box(24, b"ftyp", |s| {
        s.append_bytes(b"mp42")
         .B32(0) // minor version
         .append_bytes(b"isom")
         .append_bytes(b"mp42")
    });
    let header = read_box_header(&mut stream).unwrap();
    let parsed = super::read_ftyp(&mut stream, &header).unwrap();
    assert_eq!(parsed.header.name, FourCC(*b"ftyp"));
    assert_eq!(parsed.header.size, 24);
    assert_eq!(parsed.major_brand, FourCC(*b"mp42"));
    assert_eq!(parsed.minor_version, 0);
    assert_eq!(parsed.compatible_brands.len(), 2);
    assert_eq!(parsed.compatible_brands[0], FourCC(*b"isom"));
    assert_eq!(parsed.compatible_brands[1], FourCC(*b"mp42"));
}

#[test]
#[ignore]
fn read_truncated_ftyp() {
    // We declare a 24 byte box, but only write 20 bytes.
    let mut stream = make_box_raw(BoxSize::UncheckedShort(24), b"ftyp", |s| {
        s.append_bytes(b"mp42")
            .B32(0) // minor version
            .append_bytes(b"isom")
    });
    let mut context = MediaContext::new();
    context.trace(true);
    match read_mp4(&mut stream, &mut context) {
        Err(Error::UnexpectedEOF) => (),
        Ok(_) => assert!(false, "expected an error result"),
        _ => assert!(false, "expected a different error result"),
    }
}

#[test]
fn read_elst_v0() {
    let mut stream = make_fullbox(28, b"elst", 0, |s| {
        s.B32(1) // list count
          // first entry
         .B32(1234) // duration
         .B32(5678) // time
         .B16(12) // rate integer
         .B16(34) // rate fraction
    });
    let header = read_box_header(&mut stream).unwrap();
    let parsed = super::read_elst(&mut stream, &header).unwrap();
    assert_eq!(parsed.header.name, FourCC(*b"elst"));
    assert_eq!(parsed.header.size, 28);
    assert_eq!(parsed.edits.len(), 1);
    assert_eq!(parsed.edits[0].segment_duration, 1234);
    assert_eq!(parsed.edits[0].media_time, 5678);
    assert_eq!(parsed.edits[0].media_rate_integer, 12);
    assert_eq!(parsed.edits[0].media_rate_fraction, 34);
}

#[test]
fn read_elst_v1() {
    let mut stream = make_fullbox(56, b"elst", 1, |s| {
        s.B32(2) // list count
         // first entry
         .B64(1234) // duration
         .B64(5678) // time
         .B16(12) // rate integer
         .B16(34) // rate fraction
         // second entry
         .B64(1234) // duration
         .B64(5678) // time
         .B16(12) // rate integer
         .B16(34) // rate fraction
    });
    let header = read_box_header(&mut stream).unwrap();
    let parsed = super::read_elst(&mut stream, &header).unwrap();
    assert_eq!(parsed.header.name, FourCC(*b"elst"));
    assert_eq!(parsed.header.size, 56);
    assert_eq!(parsed.edits.len(), 2);
    assert_eq!(parsed.edits[1].segment_duration, 1234);
    assert_eq!(parsed.edits[1].media_time, 5678);
    assert_eq!(parsed.edits[1].media_rate_integer, 12);
    assert_eq!(parsed.edits[1].media_rate_fraction, 34);
}

#[test]
fn read_mdhd_v0() {
    let mut stream = make_fullbox(32, b"mdhd", 0, |s| {
        s.B32(0)
         .B32(0)
         .B32(1234) // timescale
         .B32(5678) // duration
         .B32(0)
    });
    let header = read_box_header(&mut stream).unwrap();
    let parsed = super::read_mdhd(&mut stream, &header).unwrap();
    assert_eq!(parsed.header.name, FourCC(*b"mdhd"));
    assert_eq!(parsed.header.size, 32);
    assert_eq!(parsed.timescale, 1234);
    assert_eq!(parsed.duration, 5678);
}

#[test]
fn read_mdhd_v1() {
    let mut stream = make_fullbox(44, b"mdhd", 1, |s| {
        s.B64(0)
         .B64(0)
         .B32(1234) // timescale
         .B64(5678) // duration
         .B32(0)
    });
    let header = read_box_header(&mut stream).unwrap();
    let parsed = super::read_mdhd(&mut stream, &header).unwrap();
    assert_eq!(parsed.header.name, FourCC(*b"mdhd"));
    assert_eq!(parsed.header.size, 44);
    assert_eq!(parsed.timescale, 1234);
    assert_eq!(parsed.duration, 5678);
}

#[test]
fn read_mdhd_unknown_duration() {
    let mut stream = make_fullbox(32, b"mdhd", 0, |s| {
        s.B32(0)
         .B32(0)
         .B32(1234) // timescale
         .B32(::std::u32::MAX) // duration
         .B32(0)
    });
    let header = read_box_header(&mut stream).unwrap();
    let parsed = super::read_mdhd(&mut stream, &header).unwrap();
    assert_eq!(parsed.header.name, FourCC(*b"mdhd"));
    assert_eq!(parsed.header.size, 32);
    assert_eq!(parsed.timescale, 1234);
    assert_eq!(parsed.duration, ::std::u64::MAX);
}

#[test]
fn read_mdhd_invalid_timescale() {
    let mut stream = make_fullbox(44, b"mdhd", 1, |s| {
        s.B64(0)
         .B64(0)
         .B32(0) // timescale
         .B64(5678) // duration
         .B32(0)
    });
    let header = read_box_header(&mut stream).unwrap();
    let r = super::parse_mdhd(&mut stream, &header, &mut super::Track::new(0));
    assert_eq!(r.is_err(), true);
}

#[test]
fn read_mvhd_v0() {
    let mut stream = make_fullbox(108, b"mvhd", 0, |s| {
        s.B32(0)
         .B32(0)
         .B32(1234)
         .B32(5678)
         .append_repeated(0, 80)
    });
    let header = read_box_header(&mut stream).unwrap();
    let parsed = super::read_mvhd(&mut stream, &header).unwrap();
    assert_eq!(parsed.header.name, FourCC(*b"mvhd"));
    assert_eq!(parsed.header.size, 108);
    assert_eq!(parsed.timescale, 1234);
    assert_eq!(parsed.duration, 5678);
}

#[test]
fn read_mvhd_v1() {
    let mut stream = make_fullbox(120, b"mvhd", 1, |s| {
        s.B64(0)
         .B64(0)
         .B32(1234)
         .B64(5678)
         .append_repeated(0, 80)
    });
    let header = read_box_header(&mut stream).unwrap();
    let parsed = super::read_mvhd(&mut stream, &header).unwrap();
    assert_eq!(parsed.header.name, FourCC(*b"mvhd"));
    assert_eq!(parsed.header.size, 120);
    assert_eq!(parsed.timescale, 1234);
    assert_eq!(parsed.duration, 5678);
}

#[test]
fn read_mvhd_invalid_timescale() {
    let mut stream = make_fullbox(120, b"mvhd", 1, |s| {
        s.B64(0)
         .B64(0)
         .B32(0)
         .B64(5678)
         .append_repeated(0, 80)
    });
    let header = read_box_header(&mut stream).unwrap();
    let r = super::parse_mvhd(&mut stream, &header);
    assert_eq!(r.is_err(), true);
}

#[test]
fn read_mvhd_unknown_duration() {
    let mut stream = make_fullbox(108, b"mvhd", 0, |s| {
        s.B32(0)
         .B32(0)
         .B32(1234)
         .B32(::std::u32::MAX)
         .append_repeated(0, 80)
    });
    let header = read_box_header(&mut stream).unwrap();
    let parsed = super::read_mvhd(&mut stream, &header).unwrap();
    assert_eq!(parsed.header.name, FourCC(*b"mvhd"));
    assert_eq!(parsed.header.size, 108);
    assert_eq!(parsed.timescale, 1234);
    assert_eq!(parsed.duration, ::std::u64::MAX);
}
<commit_msg>Mark ignored test as xfail.<commit_after>//! Module for parsing ISO Base Media Format aka video/mp4 streams.
//! Internal unit tests.

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

use std::io::Cursor;
use super::*;
extern crate test_assembler;
use self::test_assembler::*;

enum BoxSize {
    Short(u32),
    Long(u64),
    UncheckedShort(u32),
    UncheckedLong(u64),
}

fn make_box_raw<F>(size: BoxSize, name: &[u8; 4], func: F) -> Cursor<Vec<u8>>
    where F: Fn(Section) -> Section
{
    let mut section = Section::new();
    section = match size {
        BoxSize::Short(size) | BoxSize::UncheckedShort(size) => section.B32(size),
        BoxSize::Long(_) | BoxSize::UncheckedLong(_) => section.B32(1),
    };
    section = section.append_bytes(name);
    section = match size {
        // The spec allows the 32-bit size to be 0 to indicate unknown
        // length streams.  It's not clear if this is valid when using a
        // 64-bit size, so prohibit it for now.
        BoxSize::Long(size) => {
            assert!(size > 0);
            section.B64(size)
        }
        BoxSize::UncheckedLong(size) => section.B64(size),
        _ => section,
    };
    section = func(section);
    match size {
        BoxSize::Short(size) => {
            if size > 0 {
                assert_eq!(size as u64, section.size())
            }
        }
        BoxSize::Long(size) => assert_eq!(size, section.size()),
        _ => (),
    }
    Cursor::new(section.get_contents().unwrap())
}

fn make_box<F>(size: u32, name: &[u8; 4], func: F) -> Cursor<Vec<u8>>
    where F: Fn(Section) -> Section
{
    make_box_raw(BoxSize::Short(size), name, func)
}

fn make_fullbox<F>(size: u32, name: &[u8; 4], version: u8, func: F) -> Cursor<Vec<u8>>
    where F: Fn(Section) -> Section
{
    make_box_raw(BoxSize::Short(size), name, |s| {
        func(s.B8(version)
              .B8(0)
              .B8(0)
              .B8(0))
    })
}

#[test]
fn read_box_header_short() {
    let mut stream = make_box_raw(BoxSize::Short(8), b"test", |s| s);
    let parsed = read_box_header(&mut stream).unwrap();
    assert_eq!(parsed.name, FourCC(*b"test"));
    assert_eq!(parsed.size, 8);
}

#[test]
fn read_box_header_long() {
    let mut stream = make_box_raw(BoxSize::Long(16), b"test", |s| s);
    let parsed = read_box_header(&mut stream).unwrap();
    assert_eq!(parsed.name, FourCC(*b"test"));
    assert_eq!(parsed.size, 16);
}

#[test]
fn read_box_header_short_unknown_size() {
    let mut stream = make_box_raw(BoxSize::Short(0), b"test", |s| s);
    match read_box_header(&mut stream) {
        Err(Error::Unsupported) => (),
        _ => panic!("unexpected result reading box with unknown size"),
    };
}

#[test]
fn read_box_header_short_invalid_size() {
    let mut stream = make_box_raw(BoxSize::UncheckedShort(2), b"test", |s| s);
    match read_box_header(&mut stream) {
        Err(Error::InvalidData) => (),
        _ => panic!("unexpected result reading box with invalid size"),
    };
}

#[test]
fn read_box_header_long_invalid_size() {
    let mut stream = make_box_raw(BoxSize::UncheckedLong(2), b"test", |s| s);
    match read_box_header(&mut stream) {
        Err(Error::InvalidData) => (),
        _ => panic!("unexpected result reading box with invalid size"),
    };
}

#[test]
fn read_ftyp() {
    let mut stream = make_box(24, b"ftyp", |s| {
        s.append_bytes(b"mp42")
         .B32(0) // minor version
         .append_bytes(b"isom")
         .append_bytes(b"mp42")
    });
    let header = read_box_header(&mut stream).unwrap();
    let parsed = super::read_ftyp(&mut stream, &header).unwrap();
    assert_eq!(parsed.header.name, FourCC(*b"ftyp"));
    assert_eq!(parsed.header.size, 24);
    assert_eq!(parsed.major_brand, FourCC(*b"mp42"));
    assert_eq!(parsed.minor_version, 0);
    assert_eq!(parsed.compatible_brands.len(), 2);
    assert_eq!(parsed.compatible_brands[0], FourCC(*b"isom"));
    assert_eq!(parsed.compatible_brands[1], FourCC(*b"mp42"));
}

#[test]
#[should_panic]
fn read_truncated_ftyp() {
    // We declare a 24 byte box, but only write 20 bytes.
    let mut stream = make_box_raw(BoxSize::UncheckedShort(24), b"ftyp", |s| {
        s.append_bytes(b"mp42")
            .B32(0) // minor version
            .append_bytes(b"isom")
    });
    let mut context = MediaContext::new();
    context.trace(true);
    match read_mp4(&mut stream, &mut context) {
        Err(Error::UnexpectedEOF) => (),
        Ok(_) => assert!(false, "expected an error result"),
        _ => assert!(false, "expected a different error result"),
    }
}

#[test]
fn read_elst_v0() {
    let mut stream = make_fullbox(28, b"elst", 0, |s| {
        s.B32(1) // list count
          // first entry
         .B32(1234) // duration
         .B32(5678) // time
         .B16(12) // rate integer
         .B16(34) // rate fraction
    });
    let header = read_box_header(&mut stream).unwrap();
    let parsed = super::read_elst(&mut stream, &header).unwrap();
    assert_eq!(parsed.header.name, FourCC(*b"elst"));
    assert_eq!(parsed.header.size, 28);
    assert_eq!(parsed.edits.len(), 1);
    assert_eq!(parsed.edits[0].segment_duration, 1234);
    assert_eq!(parsed.edits[0].media_time, 5678);
    assert_eq!(parsed.edits[0].media_rate_integer, 12);
    assert_eq!(parsed.edits[0].media_rate_fraction, 34);
}

#[test]
fn read_elst_v1() {
    let mut stream = make_fullbox(56, b"elst", 1, |s| {
        s.B32(2) // list count
         // first entry
         .B64(1234) // duration
         .B64(5678) // time
         .B16(12) // rate integer
         .B16(34) // rate fraction
         // second entry
         .B64(1234) // duration
         .B64(5678) // time
         .B16(12) // rate integer
         .B16(34) // rate fraction
    });
    let header = read_box_header(&mut stream).unwrap();
    let parsed = super::read_elst(&mut stream, &header).unwrap();
    assert_eq!(parsed.header.name, FourCC(*b"elst"));
    assert_eq!(parsed.header.size, 56);
    assert_eq!(parsed.edits.len(), 2);
    assert_eq!(parsed.edits[1].segment_duration, 1234);
    assert_eq!(parsed.edits[1].media_time, 5678);
    assert_eq!(parsed.edits[1].media_rate_integer, 12);
    assert_eq!(parsed.edits[1].media_rate_fraction, 34);
}

#[test]
fn read_mdhd_v0() {
    let mut stream = make_fullbox(32, b"mdhd", 0, |s| {
        s.B32(0)
         .B32(0)
         .B32(1234) // timescale
         .B32(5678) // duration
         .B32(0)
    });
    let header = read_box_header(&mut stream).unwrap();
    let parsed = super::read_mdhd(&mut stream, &header).unwrap();
    assert_eq!(parsed.header.name, FourCC(*b"mdhd"));
    assert_eq!(parsed.header.size, 32);
    assert_eq!(parsed.timescale, 1234);
    assert_eq!(parsed.duration, 5678);
}

#[test]
fn read_mdhd_v1() {
    let mut stream = make_fullbox(44, b"mdhd", 1, |s| {
        s.B64(0)
         .B64(0)
         .B32(1234) // timescale
         .B64(5678) // duration
         .B32(0)
    });
    let header = read_box_header(&mut stream).unwrap();
    let parsed = super::read_mdhd(&mut stream, &header).unwrap();
    assert_eq!(parsed.header.name, FourCC(*b"mdhd"));
    assert_eq!(parsed.header.size, 44);
    assert_eq!(parsed.timescale, 1234);
    assert_eq!(parsed.duration, 5678);
}

#[test]
fn read_mdhd_unknown_duration() {
    let mut stream = make_fullbox(32, b"mdhd", 0, |s| {
        s.B32(0)
         .B32(0)
         .B32(1234) // timescale
         .B32(::std::u32::MAX) // duration
         .B32(0)
    });
    let header = read_box_header(&mut stream).unwrap();
    let parsed = super::read_mdhd(&mut stream, &header).unwrap();
    assert_eq!(parsed.header.name, FourCC(*b"mdhd"));
    assert_eq!(parsed.header.size, 32);
    assert_eq!(parsed.timescale, 1234);
    assert_eq!(parsed.duration, ::std::u64::MAX);
}

#[test]
fn read_mdhd_invalid_timescale() {
    let mut stream = make_fullbox(44, b"mdhd", 1, |s| {
        s.B64(0)
         .B64(0)
         .B32(0) // timescale
         .B64(5678) // duration
         .B32(0)
    });
    let header = read_box_header(&mut stream).unwrap();
    let r = super::parse_mdhd(&mut stream, &header, &mut super::Track::new(0));
    assert_eq!(r.is_err(), true);
}

#[test]
fn read_mvhd_v0() {
    let mut stream = make_fullbox(108, b"mvhd", 0, |s| {
        s.B32(0)
         .B32(0)
         .B32(1234)
         .B32(5678)
         .append_repeated(0, 80)
    });
    let header = read_box_header(&mut stream).unwrap();
    let parsed = super::read_mvhd(&mut stream, &header).unwrap();
    assert_eq!(parsed.header.name, FourCC(*b"mvhd"));
    assert_eq!(parsed.header.size, 108);
    assert_eq!(parsed.timescale, 1234);
    assert_eq!(parsed.duration, 5678);
}

#[test]
fn read_mvhd_v1() {
    let mut stream = make_fullbox(120, b"mvhd", 1, |s| {
        s.B64(0)
         .B64(0)
         .B32(1234)
         .B64(5678)
         .append_repeated(0, 80)
    });
    let header = read_box_header(&mut stream).unwrap();
    let parsed = super::read_mvhd(&mut stream, &header).unwrap();
    assert_eq!(parsed.header.name, FourCC(*b"mvhd"));
    assert_eq!(parsed.header.size, 120);
    assert_eq!(parsed.timescale, 1234);
    assert_eq!(parsed.duration, 5678);
}

#[test]
fn read_mvhd_invalid_timescale() {
    let mut stream = make_fullbox(120, b"mvhd", 1, |s| {
        s.B64(0)
         .B64(0)
         .B32(0)
         .B64(5678)
         .append_repeated(0, 80)
    });
    let header = read_box_header(&mut stream).unwrap();
    let r = super::parse_mvhd(&mut stream, &header);
    assert_eq!(r.is_err(), true);
}

#[test]
fn read_mvhd_unknown_duration() {
    let mut stream = make_fullbox(108, b"mvhd", 0, |s| {
        s.B32(0)
         .B32(0)
         .B32(1234)
         .B32(::std::u32::MAX)
         .append_repeated(0, 80)
    });
    let header = read_box_header(&mut stream).unwrap();
    let parsed = super::read_mvhd(&mut stream, &header).unwrap();
    assert_eq!(parsed.header.name, FourCC(*b"mvhd"));
    assert_eq!(parsed.header.size, 108);
    assert_eq!(parsed.timescale, 1234);
    assert_eq!(parsed.duration, ::std::u64::MAX);
}
<|endoftext|>


<commit_before>use std::collections::HashSet;
use std::iter::Iterator;

static TWO_CHAR_TITLES: [&'static str; 4] = [
    "mr",
    "ms",
    "sr",
    "dr",
];

lazy_static! {
    static ref TITLE_PARTS: HashSet<&'static str> = {
        let s: HashSet<&'static str> = [
            "aunt",
            "auntie",
            "attaché",
            "dame",
            "marchioness",
            "marquess",
            "marquis",
            "marquise",
            "king",
            "king's",
            "queen",
            "queen's",
            "1lt",
            "1st",
            "1sgt",
            "1stlt",
            "1stsgt",
            "2lt",
            "2nd",
            "2ndlt",
            "a1c",
            "abbess",
            "abbot",
            "academic",
            "acolyte",
            "adept",
            "adjutant",
            "adm",
            "admiral",
            "advocate",
            "akhoond",
            "air",
            "ald",
            "alderman",
            "almoner",
            "ambassador",
            "amn",
            "analytics",
            "appellate",
            "apprentice",
            "arbitrator",
            "archbishop",
            "archdeacon",
            "archdruid",
            "archduchess",
            "archduke",
            "arhat",
            "assistant",
            "assoc",
            "associate",
            "asst",
            "attache",
            "attorney",
            "ayatollah",
            "baba",
            "bailiff",
            "banner",
            "bard",
            "baron",
            "barrister",
            "bearer",
            "bench",
            "bgen",
            "bishop",
            "blessed",
            "bodhisattva",
            "brig",
            "brigadier",
            "briggen",
            "brother",
            "buddha",
            "burgess",
            "business",
            "bwana",
            "canon",
            "capt",
            "captain",
            "cardinal",
            "chargé",
            "catholicos",
            "ccmsgt",
            "cdr",
            "ceo",
            "cfo",
            "chair",
            "chairs",
            "chancellor",
            "chaplain",
            "chief",
            "chieftain",
            "civil",
            "clerk",
            "cmd",
            "cmsaf",
            "cmsgt",
            "co-chair",
            "co-chairs",
            "coach",
            "col",
            "colonel",
            "commander",
            "commander-in-chief",
            "commodore",
            "comptroller",
            "controller",
            "corporal",
            "corporate",
            "councillor",
            "courtier",
            "cpl",
            "cpo",
            "cpt",
            "credit",
            "criminal",
            "csm",
            "curator",
            "customs",
            "cwo",
            "cwo-2",
            "cwo-3",
            "cwo-4",
            "cwo-5",
            "cwo2",
            "cwo3",
            "cwo4",
            "cwo5",
            "d'affaires",
            "deacon",
            "delegate",
            "deputy",
            "designated",
            "det",
            "dir",
            "director",
            "discovery",
            "district",
            "division",
            "docent",
            "docket",
            "doctor",
            "doyen",
            "dpty",
            "druid",
            "duke",
            "dutchess",
            "edmi",
            "edohen",
            "effendi",
            "ekegbian",
            "elder",
            "elerunwon",
            "emperor",
            "empress",
            "ens",
            "envoy",
            "exec",
            "executive",
            "fadm",
            "family",
            "father",
            "federal",
            "field",
            "financial",
            "first",
            "flag",
            "flying",
            "flight",
            "flt",
            "foreign",
            "forester",
            "friar",
            "gen",
            "general",
            "generalissimo",
            "gentiluomo",
            "giani",
            "goodman",
            "goodwife",
            "governor",
            "grand",
            "group",
            "guru",
            "gyani",
            "gysgt",
            "hajji",
            "headman",
            "her",
            "hereditary",
            "high",
            "his",
            "hon",
            "honorable",
            "honourable",
            "imam",
            "information",
            "insp",
            "intelligence",
            "intendant",
            "journeyman",
            "judge",
            "judicial",
            "justice",
            "junior",
            "kingdom",
            "knowledge",
            "lady",
            "lama",
            "lamido",
            "law",
            "lcdr",
            "lcpl",
            "leader",
            "lieutenant",
            "lord",
            "leut",
            "lieut",
            "ltc",
            "ltcol",
            "ltg",
            "ltgen",
            "ltjg",
            "madam",
            "madame",
            "mag",
            "mag-judge",
            "mag/judge",
            "magistrate",
            "magistrate-judge",
            "maharajah",
            "maharani",
            "mahdi",
            "maid",
            "maj",
            "majesty",
            "majgen",
            "major",
            "manager",
            "marcher",
            "marketing",
            "marshal",
            "master",
            "matriarch",
            "matron",
            "mayor",
            "mcpo",
            "mcpoc",
            "mcpon",
            "member",
            "metropolitan",
            "mgr",
            "mgysgt",
            "minister",
            "miss",
            "misses",
            "mister",
            "mme",
            "monsignor",
            "most",
            "mother",
            "mpco-cg",
            "mrs",
            "msg",
            "msgr",
            "msgt",
            "mufti",
            "mullah",
            "municipal",
            "murshid",
            "nanny",
            "national",
            "nurse",
            "officer",
            "operating",
            "pastor",
            "patriarch",
            "petty",
            "pfc",
            "pharaoh",
            "pilot",
            "pir",
            "po1",
            "po2",
            "po3",
            "police",
            "political",
            "pope",
            "prefect",
            "prelate",
            "premier",
            "pres",
            "presbyter",
            "president",
            "presiding",
            "priest",
            "priestess",
            "primate",
            "prime",
            "prin",
            "prince",
            "princess",
            "principal",
            "prior",
            "private",
            "pro",
            "prof",
            "professor",
            "provost",
            "pslc",
            "pte",
            "pursuivant",
            "pv2",
            "pvt",
            "rabbi",
            "radm",
            "rangatira",
            "ranger",
            "rdml",
            "rear",
            "rebbe",
            "registrar",
            "rep",
            "representative",
            "resident",
            "rev",
            "revenue",
            "reverend",
            "reverand",
            "revd",
            "right",
            "risk",
            "royal",
            "saint",
            "sargent",
            "sargeant",
            "saoshyant",
            "scpo",
            "secretary",
            "security",
            "seigneur",
            "senator",
            "senior",
            "senior-judge",
            "sergeant",
            "servant",
            "sfc",
            "sgm",
            "sgt",
            "sgtmaj",
            "sgtmajmc",
            "shehu",
            "sheikh",
            "sheriff",
            "siddha",
            "sir",
            "sister",
            "sma",
            "smsgt",
            "solicitor",
            "spc",
            "speaker",
            "special",
            "sra",
            "ssg",
            "ssgt",
            "staff",
            "state",
            "states",
            "strategy",
            "subaltern",
            "subedar",
            "sultan",
            "sultana",
            "superior",
            "supreme",
            "surgeon",
            "swordbearer",
            "sysselmann",
            "tax",
            "technical",
            "timi",
            "tirthankar",
            "treasurer",
            "tsar",
            "tsarina",
            "tsgt",
            "uncle",
            "united",
            "vadm",
            "vardapet",
            "venerable",
            "verderer",
            "very",
            "vicar",
            "vice",
            "viscount",
            "vizier",
            "warden",
            "warrant",
            "wing",
            "wo-1",
            "wo-2",
            "wo-3",
            "wo-4",
            "wo-5",
            "wo1",
            "wo2",
            "wo3",
            "wo4",
            "wo5",
            "woodman",
        ].iter().cloned().collect();
        s
    };
}

pub fn is_title(words: &[&str]) -> bool {
    if words.is_empty() {
        return false;
    }

    for word in words {
        for part in word.split('.') {
            // Allow any word with 1 or 2 characters as part of a title...
            if part.len() >= 3 {
                let key: &str = &part.to_lowercase();
                if !TITLE_PARTS.contains(key) {
                    return false;
                }
            }
        }
    }

    // ... but don't allow 1 or 2-character words as the whole or final piece of
    // a title, except a set of very-common two-character title abbreviations,
    // because otherwise we are more likely dealing with initials
    let last = words.last().unwrap();
    if last.len() < 3 && (last.len() == 1 || !TWO_CHAR_TITLES.contains(last)) {
        return false;
    }

    true
}
<commit_msg>Add conjunctions to accepted title-part words<commit_after>use std::collections::HashSet;
use std::iter::Iterator;

static TWO_CHAR_TITLES: [&'static str; 4] = [
    "mr",
    "ms",
    "sr",
    "dr",
];

lazy_static! {
    static ref TITLE_PARTS: HashSet<&'static str> = {
        let s: HashSet<&'static str> = [
            "aunt",
            "auntie",
            "attaché",
            "dame",
            "marchioness",
            "marquess",
            "marquis",
            "marquise",
            "king",
            "king's",
            "queen",
            "queen's",
            "1lt",
            "1st",
            "1sgt",
            "1stlt",
            "1stsgt",
            "2lt",
            "2nd",
            "2ndlt",
            "a1c",
            "abbess",
            "abbot",
            "academic",
            "acolyte",
            "adept",
            "adjutant",
            "adm",
            "admiral",
            "advocate",
            "akhoond",
            "air",
            "ald",
            "alderman",
            "almoner",
            "ambassador",
            "amn",
            "analytics",
            "appellate",
            "apprentice",
            "arbitrator",
            "archbishop",
            "archdeacon",
            "archdruid",
            "archduchess",
            "archduke",
            "arhat",
            "assistant",
            "assoc",
            "associate",
            "asst",
            "attache",
            "attorney",
            "ayatollah",
            "baba",
            "bailiff",
            "banner",
            "bard",
            "baron",
            "barrister",
            "bearer",
            "bench",
            "bgen",
            "bishop",
            "blessed",
            "bodhisattva",
            "brig",
            "brigadier",
            "briggen",
            "brother",
            "buddha",
            "burgess",
            "business",
            "bwana",
            "canon",
            "capt",
            "captain",
            "cardinal",
            "chargé",
            "catholicos",
            "ccmsgt",
            "cdr",
            "ceo",
            "cfo",
            "chair",
            "chairs",
            "chancellor",
            "chaplain",
            "chief",
            "chieftain",
            "civil",
            "clerk",
            "cmd",
            "cmsaf",
            "cmsgt",
            "co-chair",
            "co-chairs",
            "coach",
            "col",
            "colonel",
            "commander",
            "commander-in-chief",
            "commodore",
            "comptroller",
            "controller",
            "corporal",
            "corporate",
            "councillor",
            "courtier",
            "cpl",
            "cpo",
            "cpt",
            "credit",
            "criminal",
            "csm",
            "curator",
            "customs",
            "cwo",
            "cwo-2",
            "cwo-3",
            "cwo-4",
            "cwo-5",
            "cwo2",
            "cwo3",
            "cwo4",
            "cwo5",
            "d'affaires",
            "deacon",
            "delegate",
            "deputy",
            "designated",
            "det",
            "dir",
            "director",
            "discovery",
            "district",
            "division",
            "docent",
            "docket",
            "doctor",
            "doyen",
            "dpty",
            "druid",
            "duke",
            "dutchess",
            "edmi",
            "edohen",
            "effendi",
            "ekegbian",
            "elder",
            "elerunwon",
            "emperor",
            "empress",
            "ens",
            "envoy",
            "exec",
            "executive",
            "fadm",
            "family",
            "father",
            "federal",
            "field",
            "financial",
            "first",
            "flag",
            "flying",
            "flight",
            "flt",
            "foreign",
            "forester",
            "friar",
            "gen",
            "general",
            "generalissimo",
            "gentiluomo",
            "giani",
            "goodman",
            "goodwife",
            "governor",
            "grand",
            "group",
            "guru",
            "gyani",
            "gysgt",
            "hajji",
            "headman",
            "her",
            "hereditary",
            "high",
            "his",
            "hon",
            "honorable",
            "honourable",
            "imam",
            "information",
            "insp",
            "intelligence",
            "intendant",
            "journeyman",
            "judge",
            "judicial",
            "justice",
            "junior",
            "kingdom",
            "knowledge",
            "lady",
            "lama",
            "lamido",
            "law",
            "lcdr",
            "lcpl",
            "leader",
            "lieutenant",
            "lord",
            "leut",
            "lieut",
            "ltc",
            "ltcol",
            "ltg",
            "ltgen",
            "ltjg",
            "madam",
            "madame",
            "mag",
            "mag-judge",
            "mag/judge",
            "magistrate",
            "magistrate-judge",
            "maharajah",
            "maharani",
            "mahdi",
            "maid",
            "maj",
            "majesty",
            "majgen",
            "major",
            "manager",
            "marcher",
            "marketing",
            "marshal",
            "master",
            "matriarch",
            "matron",
            "mayor",
            "mcpo",
            "mcpoc",
            "mcpon",
            "member",
            "metropolitan",
            "mgr",
            "mgysgt",
            "minister",
            "miss",
            "misses",
            "mister",
            "mme",
            "monsignor",
            "most",
            "mother",
            "mpco-cg",
            "mrs",
            "msg",
            "msgr",
            "msgt",
            "mufti",
            "mullah",
            "municipal",
            "murshid",
            "nanny",
            "national",
            "nurse",
            "officer",
            "operating",
            "pastor",
            "patriarch",
            "petty",
            "pfc",
            "pharaoh",
            "pilot",
            "pir",
            "po1",
            "po2",
            "po3",
            "police",
            "political",
            "pope",
            "prefect",
            "prelate",
            "premier",
            "pres",
            "presbyter",
            "president",
            "presiding",
            "priest",
            "priestess",
            "primate",
            "prime",
            "prin",
            "prince",
            "princess",
            "principal",
            "prior",
            "private",
            "pro",
            "prof",
            "professor",
            "provost",
            "pslc",
            "pte",
            "pursuivant",
            "pv2",
            "pvt",
            "rabbi",
            "radm",
            "rangatira",
            "ranger",
            "rdml",
            "rear",
            "rebbe",
            "registrar",
            "rep",
            "representative",
            "resident",
            "rev",
            "revenue",
            "reverend",
            "reverand",
            "revd",
            "right",
            "risk",
            "royal",
            "saint",
            "sargent",
            "sargeant",
            "saoshyant",
            "scpo",
            "secretary",
            "security",
            "seigneur",
            "senator",
            "senior",
            "senior-judge",
            "sergeant",
            "servant",
            "sfc",
            "sgm",
            "sgt",
            "sgtmaj",
            "sgtmajmc",
            "shehu",
            "sheikh",
            "sheriff",
            "siddha",
            "sir",
            "sister",
            "sma",
            "smsgt",
            "solicitor",
            "spc",
            "speaker",
            "special",
            "sra",
            "ssg",
            "ssgt",
            "staff",
            "state",
            "states",
            "strategy",
            "subaltern",
            "subedar",
            "sultan",
            "sultana",
            "superior",
            "supreme",
            "surgeon",
            "swordbearer",
            "sysselmann",
            "tax",
            "technical",
            "timi",
            "tirthankar",
            "treasurer",
            "tsar",
            "tsarina",
            "tsgt",
            "uncle",
            "united",
            "vadm",
            "vardapet",
            "venerable",
            "verderer",
            "very",
            "vicar",
            "vice",
            "viscount",
            "vizier",
            "warden",
            "warrant",
            "wing",
            "wo-1",
            "wo-2",
            "wo-3",
            "wo-4",
            "wo-5",
            "wo1",
            "wo2",
            "wo3",
            "wo4",
            "wo5",
            "woodman",
            "and",
            "the",
            "und",
        ].iter().cloned().collect();
        s
    };
}

pub fn is_title(words: &[&str]) -> bool {
    if words.is_empty() {
        return false;
    }

    for word in words {
        for part in word.split('.') {
            // Allow any word with 1 or 2 characters as part of a title...
            if part.len() >= 3 {
                let key: &str = &part.to_lowercase();
                if !TITLE_PARTS.contains(key) {
                    return false;
                }
            }
        }
    }

    // ... but don't allow 1 or 2-character words as the whole or final piece of
    // a title, except a set of very-common two-character title abbreviations,
    // because otherwise we are more likely dealing with initials
    let last = words.last().unwrap();
    if last.len() < 3 && (last.len() == 1 || !TWO_CHAR_TITLES.contains(last)) {
        return false;
    }

    true
}
<|endoftext|>


<commit_before>//! Custom input types
//!

use traits::{AsBytes, AtEof, Compare, CompareResult, ExtendInto, FindSubstring, FindToken, InputIter, InputLength, InputTake, Offset,
             ParseTo, Slice};

use std::str::{self, CharIndices, Chars, FromStr};
use std::ops::{Deref, Range, RangeFrom, RangeFull, RangeTo};
use std::iter::{Enumerate, Map};
use std::slice::Iter;

/// Holds a complete String, for which the `at_eof` method always returns true
///
/// This means that this input type will completely avoid nom's streaming features
/// and `Incomplete` results.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct CompleteStr<'a>(pub &'a str);

impl<'a> Deref for CompleteStr<'a> {
  type Target = str;

  fn deref(&self) -> &str {
    self.0
  }
}

impl<'a> AtEof for CompleteStr<'a> {
  fn at_eof(&self) -> bool {
    true
  }
}

impl<'a> Slice<Range<usize>> for CompleteStr<'a> {
  fn slice(&self, range: Range<usize>) -> Self {
    CompleteStr(self.0.slice(range))
  }
}

impl<'a> Slice<RangeTo<usize>> for CompleteStr<'a> {
  fn slice(&self, range: RangeTo<usize>) -> Self {
    CompleteStr(self.0.slice(range))
  }
}

impl<'a> Slice<RangeFrom<usize>> for CompleteStr<'a> {
  fn slice(&self, range: RangeFrom<usize>) -> Self {
    CompleteStr(self.0.slice(range))
  }
}

impl<'a> Slice<RangeFull> for CompleteStr<'a> {
  fn slice(&self, range: RangeFull) -> Self {
    CompleteStr(self.0.slice(range))
  }
}

impl<'a> InputIter for CompleteStr<'a> {
  type Item = char;
  type RawItem = char;
  type Iter = CharIndices<'a>;
  type IterElem = Chars<'a>;

  fn iter_indices(&self) -> Self::Iter {
    self.0.iter_indices()
  }
  fn iter_elements(&self) -> Self::IterElem {
    self.0.iter_elements()
  }
  fn position<P>(&self, predicate: P) -> Option<usize>
  where
    P: Fn(Self::RawItem) -> bool,
  {
    self.0.position(predicate)
  }
  fn slice_index(&self, count: usize) -> Option<usize> {
    self.0.slice_index(count)
  }
}

impl<'a> InputTake for CompleteStr<'a> {
  fn take(&self, count: usize) -> Self {
    CompleteStr(self.0.take(count))
  }

  fn take_split(&self, count: usize) -> (Self, Self) {
    let (left, right) = self.0.take_split(count);
    (CompleteStr(left), CompleteStr(right))
  }
}

impl<'a> InputLength for CompleteStr<'a> {
  fn input_len(&self) -> usize {
    self.0.input_len()
  }
}

impl<'a, 'b> Compare<&'b str> for CompleteStr<'a> {
  fn compare(&self, t: &'b str) -> CompareResult {
    self.0.compare(t)
  }
  fn compare_no_case(&self, t: &'b str) -> CompareResult {
    self.0.compare_no_case(t)
  }
}

impl<'a, 'b> FindSubstring<&'b str> for CompleteStr<'a> {
  fn find_substring(&self, substr: &'b str) -> Option<usize> {
    self.0.find_substring(substr)
  }
}

impl<'a> FindToken<char> for CompleteStr<'a> {
  fn find_token(&self, token: char) -> bool {
    self.0.find_token(token)
  }
}

impl<'a> FindToken<u8> for CompleteStr<'a> {
  fn find_token(&self, token: u8) -> bool {
    self.0.find_token(token)
  }
}

impl<'a, 'b> FindToken<&'a u8> for CompleteStr<'b> {
  fn find_token(&self, token: &u8) -> bool {
    self.0.find_token(token)
  }
}

impl<'a, R: FromStr> ParseTo<R> for CompleteStr<'a> {
  fn parse_to(&self) -> Option<R> {
    self.0.parse().ok()
  }
}

impl<'a> Offset for CompleteStr<'a> {
  fn offset(&self, second: &CompleteStr<'a>) -> usize {
    self.0.offset(second.0)
  }
}

impl<'a> AsBytes for CompleteStr<'a> {
  fn as_bytes(&self) -> &[u8] {
    AsBytes::as_bytes(self.0)
  }
}

#[cfg(feature = "std")]
impl<'a> ExtendInto for CompleteStr<'a> {
  type Item = char;
  type Extender = String;

  #[inline]
  fn new_builder(&self) -> String {
    String::new()
  }
  #[inline]
  fn extend_into(&self, acc: &mut String) {
    acc.extend(self.0.chars());
  }
}

/// Holds a complete String, for which the `at_eof` method always returns true
///
/// This means that this input type will completely avoid nom's streaming features
/// and `Incomplete` results.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct CompleteByteSlice<'a>(pub &'a [u8]);

impl<'a> Deref for CompleteByteSlice<'a> {
  type Target = [u8];

  fn deref(&self) -> &[u8] {
    self.0
  }
}

impl<'a> AtEof for CompleteByteSlice<'a> {
  fn at_eof(&self) -> bool {
    true
  }
}

impl<'a> Slice<Range<usize>> for CompleteByteSlice<'a> {
  fn slice(&self, range: Range<usize>) -> Self {
    CompleteByteSlice(self.0.slice(range))
  }
}

impl<'a> Slice<RangeTo<usize>> for CompleteByteSlice<'a> {
  fn slice(&self, range: RangeTo<usize>) -> Self {
    CompleteByteSlice(self.0.slice(range))
  }
}

impl<'a> Slice<RangeFrom<usize>> for CompleteByteSlice<'a> {
  fn slice(&self, range: RangeFrom<usize>) -> Self {
    CompleteByteSlice(self.0.slice(range))
  }
}

impl<'a> Slice<RangeFull> for CompleteByteSlice<'a> {
  fn slice(&self, range: RangeFull) -> Self {
    CompleteByteSlice(self.0.slice(range))
  }
}

impl<'a> InputIter for CompleteByteSlice<'a> {
  type Item = u8;
  type RawItem = u8;
  type Iter = Enumerate<Self::IterElem>;
  type IterElem = Map<Iter<'a, Self::Item>, fn(&u8) -> u8>; //Iter<'a, Self::RawItem>;

  fn iter_indices(&self) -> Self::Iter {
    self.0.iter_indices()
  }
  fn iter_elements(&self) -> Self::IterElem {
    self.0.iter_elements()
  }
  fn position<P>(&self, predicate: P) -> Option<usize>
  where
    P: Fn(Self::RawItem) -> bool,
  {
    self.0.position(predicate)
  }
  fn slice_index(&self, count: usize) -> Option<usize> {
    self.0.slice_index(count)
  }
}

impl<'a> InputTake for CompleteByteSlice<'a> {
  fn take(&self, count: usize) -> Self {
    CompleteByteSlice(self.0.take(count))
  }

  fn take_split(&self, count: usize) -> (Self, Self) {
    let (left, right) = self.0.take_split(count);
    (CompleteByteSlice(left), CompleteByteSlice(right))
  }
}

impl<'a> InputLength for CompleteByteSlice<'a> {
  fn input_len(&self) -> usize {
    self.0.input_len()
  }
}

impl<'a, 'b> Compare<&'b [u8]> for CompleteByteSlice<'a> {
  fn compare(&self, t: &'b [u8]) -> CompareResult {
    self.0.compare(t)
  }
  fn compare_no_case(&self, t: &'b [u8]) -> CompareResult {
    self.0.compare_no_case(t)
  }
}

impl<'a, 'b> Compare<&'b str> for CompleteByteSlice<'a> {
  fn compare(&self, t: &'b str) -> CompareResult {
    self.0.compare(t)
  }
  fn compare_no_case(&self, t: &'b str) -> CompareResult {
    self.0.compare_no_case(t)
  }
}

impl<'a, 'b> FindSubstring<&'b [u8]> for CompleteByteSlice<'a> {
  fn find_substring(&self, substr: &'b [u8]) -> Option<usize> {
    self.0.find_substring(substr)
  }
}

impl<'a, 'b> FindSubstring<&'b str> for CompleteByteSlice<'a> {
  fn find_substring(&self, substr: &'b str) -> Option<usize> {
    self.0.find_substring(substr)
  }
}

impl<'a> FindToken<char> for CompleteByteSlice<'a> {
  fn find_token(&self, token: char) -> bool {
    self.0.find_token(token)
  }
}

impl<'a> FindToken<u8> for CompleteByteSlice<'a> {
  fn find_token(&self, token: u8) -> bool {
    self.0.find_token(token)
  }
}

impl<'a, 'b> FindToken<&'a u8> for CompleteByteSlice<'b> {
  fn find_token(&self, token: &u8) -> bool {
    self.0.find_token(token)
  }
}

impl<'a, R: FromStr> ParseTo<R> for CompleteByteSlice<'a> {
  fn parse_to(&self) -> Option<R> {
    self.0.parse_to()
  }
}

impl<'a> Offset for CompleteByteSlice<'a> {
  fn offset(&self, second: &CompleteByteSlice<'a>) -> usize {
    self.0.offset(second.0)
  }
}

impl<'a> AsBytes for CompleteByteSlice<'a> {
  fn as_bytes(&self) -> &[u8] {
    self.0.as_bytes()
  }
}

#[cfg(feature = "std")]
impl<'a> super::util::HexDisplay for CompleteByteSlice<'a> {
  fn to_hex(&self, chunk_size: usize) -> String {
    self.0.to_hex(chunk_size)
  }

  fn to_hex_from(&self, chunk_size: usize, from: usize) -> String {
    self.0.to_hex_from(chunk_size, from)
  }
}

#[derive(Clone, Copy, Debug, PartialEq, Hash)]
pub struct Input<T> {
  pub inner: T,
  pub at_eof: bool,
}

impl<T> AtEof for Input<T> {
  fn at_eof(&self) -> bool {
    self.at_eof
  }
}

impl<T: Slice<Range<usize>>> Slice<Range<usize>> for Input<T> {
  fn slice(&self, range: Range<usize>) -> Self {
    Input {
      inner: self.inner.slice(range),
      at_eof: self.at_eof,
    }
  }
}

impl<T: Slice<RangeTo<usize>>> Slice<RangeTo<usize>> for Input<T> {
  fn slice(&self, range: RangeTo<usize>) -> Self {
    Input {
      inner: self.inner.slice(range),
      at_eof: self.at_eof,
    }
  }
}

impl<T: Slice<RangeFrom<usize>>> Slice<RangeFrom<usize>> for Input<T> {
  fn slice(&self, range: RangeFrom<usize>) -> Self {
    Input {
      inner: self.inner.slice(range),
      at_eof: self.at_eof,
    }
  }
}

impl<T: Slice<RangeFull>> Slice<RangeFull> for Input<T> {
  fn slice(&self, range: RangeFull) -> Self {
    Input {
      inner: self.inner.slice(range),
      at_eof: self.at_eof,
    }
  }
}

impl<T: InputIter> InputIter for Input<T> {
  type Item = <T as InputIter>::Item;
  type RawItem = <T as InputIter>::RawItem;
  type Iter = <T as InputIter>::Iter;
  type IterElem = <T as InputIter>::IterElem;

  fn iter_indices(&self) -> Self::Iter {
    self.inner.iter_indices()
  }
  fn iter_elements(&self) -> Self::IterElem {
    self.inner.iter_elements()
  }
  fn position<P>(&self, predicate: P) -> Option<usize>
  where
    P: Fn(Self::RawItem) -> bool,
  {
    self.inner.position(predicate)
  }
  fn slice_index(&self, count: usize) -> Option<usize> {
    self.inner.slice_index(count)
  }
}

impl<T: InputTake> InputTake for Input<T> {
  fn take(&self, count: usize) -> Self {
    Input {
      inner: self.inner.take(count),
      at_eof: self.at_eof,
    }
  }

  fn take_split(&self, count: usize) -> (Self, Self) {
    let (left, right) = self.inner.take_split(count);
    (
      Input {
        inner: left,
        at_eof: self.at_eof,
      },
      Input {
        inner: right,
        at_eof: self.at_eof,
      },
    )
  }
}

impl<T: InputLength> InputLength for Input<T> {
  fn input_len(&self) -> usize {
    self.inner.input_len()
  }
}

impl<'b, T: Compare<&'b str>> Compare<&'b str> for Input<T> {
  fn compare(&self, t: &'b str) -> CompareResult {
    self.inner.compare(t)
  }
  fn compare_no_case(&self, t: &'b str) -> CompareResult {
    self.inner.compare_no_case(t)
  }
}

impl<'b, T: FindSubstring<&'b str>> FindSubstring<&'b str> for Input<T> {
  fn find_substring(&self, substr: &'b str) -> Option<usize> {
    self.inner.find_substring(substr)
  }
}

impl<T: FindToken<char>> FindToken<char> for Input<T> {
  fn find_token(&self, token: char) -> bool {
    self.inner.find_token(token)
  }
}

impl<T: FindToken<u8>> FindToken<u8> for Input<T> {
  fn find_token(&self, token: u8) -> bool {
    self.inner.find_token(token)
  }
}

impl<'a, T: FindToken<&'a u8>> FindToken<&'a u8> for Input<T> {
  fn find_token(&self, token: &'a u8) -> bool {
    self.inner.find_token(token)
  }
}

impl<'a, R: FromStr, T: ParseTo<R>> ParseTo<R> for Input<T> {
  fn parse_to(&self) -> Option<R> {
    self.inner.parse_to()
  }
}

impl<T: Offset> Offset for Input<T> {
  fn offset(&self, second: &Input<T>) -> usize {
    self.inner.offset(&second.inner)
  }
}

impl<T: AsBytes> AsBytes for Input<T> {
  fn as_bytes(&self) -> &[u8] {
    AsBytes::as_bytes(&self.inner)
  }
}
<commit_msg>Implement Display on CompleteStr<commit_after>//! Custom input types
//!

use traits::{AsBytes, AtEof, Compare, CompareResult, ExtendInto, FindSubstring, FindToken, InputIter, InputLength, InputTake, Offset,
             ParseTo, Slice};

use std::str::{self, CharIndices, Chars, FromStr};
use std::fmt::Display;
use std::ops::{Deref, Range, RangeFrom, RangeFull, RangeTo};
use std::iter::{Enumerate, Map};
use std::slice::Iter;

/// Holds a complete String, for which the `at_eof` method always returns true
///
/// This means that this input type will completely avoid nom's streaming features
/// and `Incomplete` results.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct CompleteStr<'a>(pub &'a str);

impl<'a> Display for CompleteStr<'a> {
  fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
    self.0.fmt(f)
  }
}

impl<'a> Deref for CompleteStr<'a> {
  type Target = str;

  fn deref(&self) -> &str {
    self.0
  }
}

impl<'a> AtEof for CompleteStr<'a> {
  fn at_eof(&self) -> bool {
    true
  }
}

impl<'a> Slice<Range<usize>> for CompleteStr<'a> {
  fn slice(&self, range: Range<usize>) -> Self {
    CompleteStr(self.0.slice(range))
  }
}

impl<'a> Slice<RangeTo<usize>> for CompleteStr<'a> {
  fn slice(&self, range: RangeTo<usize>) -> Self {
    CompleteStr(self.0.slice(range))
  }
}

impl<'a> Slice<RangeFrom<usize>> for CompleteStr<'a> {
  fn slice(&self, range: RangeFrom<usize>) -> Self {
    CompleteStr(self.0.slice(range))
  }
}

impl<'a> Slice<RangeFull> for CompleteStr<'a> {
  fn slice(&self, range: RangeFull) -> Self {
    CompleteStr(self.0.slice(range))
  }
}

impl<'a> InputIter for CompleteStr<'a> {
  type Item = char;
  type RawItem = char;
  type Iter = CharIndices<'a>;
  type IterElem = Chars<'a>;

  fn iter_indices(&self) -> Self::Iter {
    self.0.iter_indices()
  }
  fn iter_elements(&self) -> Self::IterElem {
    self.0.iter_elements()
  }
  fn position<P>(&self, predicate: P) -> Option<usize>
  where
    P: Fn(Self::RawItem) -> bool,
  {
    self.0.position(predicate)
  }
  fn slice_index(&self, count: usize) -> Option<usize> {
    self.0.slice_index(count)
  }
}

impl<'a> InputTake for CompleteStr<'a> {
  fn take(&self, count: usize) -> Self {
    CompleteStr(self.0.take(count))
  }

  fn take_split(&self, count: usize) -> (Self, Self) {
    let (left, right) = self.0.take_split(count);
    (CompleteStr(left), CompleteStr(right))
  }
}

impl<'a> InputLength for CompleteStr<'a> {
  fn input_len(&self) -> usize {
    self.0.input_len()
  }
}

impl<'a, 'b> Compare<&'b str> for CompleteStr<'a> {
  fn compare(&self, t: &'b str) -> CompareResult {
    self.0.compare(t)
  }
  fn compare_no_case(&self, t: &'b str) -> CompareResult {
    self.0.compare_no_case(t)
  }
}

impl<'a, 'b> FindSubstring<&'b str> for CompleteStr<'a> {
  fn find_substring(&self, substr: &'b str) -> Option<usize> {
    self.0.find_substring(substr)
  }
}

impl<'a> FindToken<char> for CompleteStr<'a> {
  fn find_token(&self, token: char) -> bool {
    self.0.find_token(token)
  }
}

impl<'a> FindToken<u8> for CompleteStr<'a> {
  fn find_token(&self, token: u8) -> bool {
    self.0.find_token(token)
  }
}

impl<'a, 'b> FindToken<&'a u8> for CompleteStr<'b> {
  fn find_token(&self, token: &u8) -> bool {
    self.0.find_token(token)
  }
}

impl<'a, R: FromStr> ParseTo<R> for CompleteStr<'a> {
  fn parse_to(&self) -> Option<R> {
    self.0.parse().ok()
  }
}

impl<'a> Offset for CompleteStr<'a> {
  fn offset(&self, second: &CompleteStr<'a>) -> usize {
    self.0.offset(second.0)
  }
}

impl<'a> AsBytes for CompleteStr<'a> {
  fn as_bytes(&self) -> &[u8] {
    AsBytes::as_bytes(self.0)
  }
}

#[cfg(feature = "std")]
impl<'a> ExtendInto for CompleteStr<'a> {
  type Item = char;
  type Extender = String;

  #[inline]
  fn new_builder(&self) -> String {
    String::new()
  }
  #[inline]
  fn extend_into(&self, acc: &mut String) {
    acc.extend(self.0.chars());
  }
}

/// Holds a complete String, for which the `at_eof` method always returns true
///
/// This means that this input type will completely avoid nom's streaming features
/// and `Incomplete` results.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct CompleteByteSlice<'a>(pub &'a [u8]);

impl<'a> Deref for CompleteByteSlice<'a> {
  type Target = [u8];

  fn deref(&self) -> &[u8] {
    self.0
  }
}

impl<'a> AtEof for CompleteByteSlice<'a> {
  fn at_eof(&self) -> bool {
    true
  }
}

impl<'a> Slice<Range<usize>> for CompleteByteSlice<'a> {
  fn slice(&self, range: Range<usize>) -> Self {
    CompleteByteSlice(self.0.slice(range))
  }
}

impl<'a> Slice<RangeTo<usize>> for CompleteByteSlice<'a> {
  fn slice(&self, range: RangeTo<usize>) -> Self {
    CompleteByteSlice(self.0.slice(range))
  }
}

impl<'a> Slice<RangeFrom<usize>> for CompleteByteSlice<'a> {
  fn slice(&self, range: RangeFrom<usize>) -> Self {
    CompleteByteSlice(self.0.slice(range))
  }
}

impl<'a> Slice<RangeFull> for CompleteByteSlice<'a> {
  fn slice(&self, range: RangeFull) -> Self {
    CompleteByteSlice(self.0.slice(range))
  }
}

impl<'a> InputIter for CompleteByteSlice<'a> {
  type Item = u8;
  type RawItem = u8;
  type Iter = Enumerate<Self::IterElem>;
  type IterElem = Map<Iter<'a, Self::Item>, fn(&u8) -> u8>; //Iter<'a, Self::RawItem>;

  fn iter_indices(&self) -> Self::Iter {
    self.0.iter_indices()
  }
  fn iter_elements(&self) -> Self::IterElem {
    self.0.iter_elements()
  }
  fn position<P>(&self, predicate: P) -> Option<usize>
  where
    P: Fn(Self::RawItem) -> bool,
  {
    self.0.position(predicate)
  }
  fn slice_index(&self, count: usize) -> Option<usize> {
    self.0.slice_index(count)
  }
}

impl<'a> InputTake for CompleteByteSlice<'a> {
  fn take(&self, count: usize) -> Self {
    CompleteByteSlice(self.0.take(count))
  }

  fn take_split(&self, count: usize) -> (Self, Self) {
    let (left, right) = self.0.take_split(count);
    (CompleteByteSlice(left), CompleteByteSlice(right))
  }
}

impl<'a> InputLength for CompleteByteSlice<'a> {
  fn input_len(&self) -> usize {
    self.0.input_len()
  }
}

impl<'a, 'b> Compare<&'b [u8]> for CompleteByteSlice<'a> {
  fn compare(&self, t: &'b [u8]) -> CompareResult {
    self.0.compare(t)
  }
  fn compare_no_case(&self, t: &'b [u8]) -> CompareResult {
    self.0.compare_no_case(t)
  }
}

impl<'a, 'b> Compare<&'b str> for CompleteByteSlice<'a> {
  fn compare(&self, t: &'b str) -> CompareResult {
    self.0.compare(t)
  }
  fn compare_no_case(&self, t: &'b str) -> CompareResult {
    self.0.compare_no_case(t)
  }
}

impl<'a, 'b> FindSubstring<&'b [u8]> for CompleteByteSlice<'a> {
  fn find_substring(&self, substr: &'b [u8]) -> Option<usize> {
    self.0.find_substring(substr)
  }
}

impl<'a, 'b> FindSubstring<&'b str> for CompleteByteSlice<'a> {
  fn find_substring(&self, substr: &'b str) -> Option<usize> {
    self.0.find_substring(substr)
  }
}

impl<'a> FindToken<char> for CompleteByteSlice<'a> {
  fn find_token(&self, token: char) -> bool {
    self.0.find_token(token)
  }
}

impl<'a> FindToken<u8> for CompleteByteSlice<'a> {
  fn find_token(&self, token: u8) -> bool {
    self.0.find_token(token)
  }
}

impl<'a, 'b> FindToken<&'a u8> for CompleteByteSlice<'b> {
  fn find_token(&self, token: &u8) -> bool {
    self.0.find_token(token)
  }
}

impl<'a, R: FromStr> ParseTo<R> for CompleteByteSlice<'a> {
  fn parse_to(&self) -> Option<R> {
    self.0.parse_to()
  }
}

impl<'a> Offset for CompleteByteSlice<'a> {
  fn offset(&self, second: &CompleteByteSlice<'a>) -> usize {
    self.0.offset(second.0)
  }
}

impl<'a> AsBytes for CompleteByteSlice<'a> {
  fn as_bytes(&self) -> &[u8] {
    self.0.as_bytes()
  }
}

#[cfg(feature = "std")]
impl<'a> super::util::HexDisplay for CompleteByteSlice<'a> {
  fn to_hex(&self, chunk_size: usize) -> String {
    self.0.to_hex(chunk_size)
  }

  fn to_hex_from(&self, chunk_size: usize, from: usize) -> String {
    self.0.to_hex_from(chunk_size, from)
  }
}

#[derive(Clone, Copy, Debug, PartialEq, Hash)]
pub struct Input<T> {
  pub inner: T,
  pub at_eof: bool,
}

impl<T> AtEof for Input<T> {
  fn at_eof(&self) -> bool {
    self.at_eof
  }
}

impl<T: Slice<Range<usize>>> Slice<Range<usize>> for Input<T> {
  fn slice(&self, range: Range<usize>) -> Self {
    Input {
      inner: self.inner.slice(range),
      at_eof: self.at_eof,
    }
  }
}

impl<T: Slice<RangeTo<usize>>> Slice<RangeTo<usize>> for Input<T> {
  fn slice(&self, range: RangeTo<usize>) -> Self {
    Input {
      inner: self.inner.slice(range),
      at_eof: self.at_eof,
    }
  }
}

impl<T: Slice<RangeFrom<usize>>> Slice<RangeFrom<usize>> for Input<T> {
  fn slice(&self, range: RangeFrom<usize>) -> Self {
    Input {
      inner: self.inner.slice(range),
      at_eof: self.at_eof,
    }
  }
}

impl<T: Slice<RangeFull>> Slice<RangeFull> for Input<T> {
  fn slice(&self, range: RangeFull) -> Self {
    Input {
      inner: self.inner.slice(range),
      at_eof: self.at_eof,
    }
  }
}

impl<T: InputIter> InputIter for Input<T> {
  type Item = <T as InputIter>::Item;
  type RawItem = <T as InputIter>::RawItem;
  type Iter = <T as InputIter>::Iter;
  type IterElem = <T as InputIter>::IterElem;

  fn iter_indices(&self) -> Self::Iter {
    self.inner.iter_indices()
  }
  fn iter_elements(&self) -> Self::IterElem {
    self.inner.iter_elements()
  }
  fn position<P>(&self, predicate: P) -> Option<usize>
  where
    P: Fn(Self::RawItem) -> bool,
  {
    self.inner.position(predicate)
  }
  fn slice_index(&self, count: usize) -> Option<usize> {
    self.inner.slice_index(count)
  }
}

impl<T: InputTake> InputTake for Input<T> {
  fn take(&self, count: usize) -> Self {
    Input {
      inner: self.inner.take(count),
      at_eof: self.at_eof,
    }
  }

  fn take_split(&self, count: usize) -> (Self, Self) {
    let (left, right) = self.inner.take_split(count);
    (
      Input {
        inner: left,
        at_eof: self.at_eof,
      },
      Input {
        inner: right,
        at_eof: self.at_eof,
      },
    )
  }
}

impl<T: InputLength> InputLength for Input<T> {
  fn input_len(&self) -> usize {
    self.inner.input_len()
  }
}

impl<'b, T: Compare<&'b str>> Compare<&'b str> for Input<T> {
  fn compare(&self, t: &'b str) -> CompareResult {
    self.inner.compare(t)
  }
  fn compare_no_case(&self, t: &'b str) -> CompareResult {
    self.inner.compare_no_case(t)
  }
}

impl<'b, T: FindSubstring<&'b str>> FindSubstring<&'b str> for Input<T> {
  fn find_substring(&self, substr: &'b str) -> Option<usize> {
    self.inner.find_substring(substr)
  }
}

impl<T: FindToken<char>> FindToken<char> for Input<T> {
  fn find_token(&self, token: char) -> bool {
    self.inner.find_token(token)
  }
}

impl<T: FindToken<u8>> FindToken<u8> for Input<T> {
  fn find_token(&self, token: u8) -> bool {
    self.inner.find_token(token)
  }
}

impl<'a, T: FindToken<&'a u8>> FindToken<&'a u8> for Input<T> {
  fn find_token(&self, token: &'a u8) -> bool {
    self.inner.find_token(token)
  }
}

impl<'a, R: FromStr, T: ParseTo<R>> ParseTo<R> for Input<T> {
  fn parse_to(&self) -> Option<R> {
    self.inner.parse_to()
  }
}

impl<T: Offset> Offset for Input<T> {
  fn offset(&self, second: &Input<T>) -> usize {
    self.inner.offset(&second.inner)
  }
}

impl<T: AsBytes> AsBytes for Input<T> {
  fn as_bytes(&self) -> &[u8] {
    AsBytes::as_bytes(&self.inner)
  }
}
<|endoftext|>


<commit_before>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::borrow::Cow;

use syntax::{abi, ptr};
use syntax::ast::{self, Attribute, CrateSugar, MetaItem, MetaItemKind, NestedMetaItem,
                  NestedMetaItemKind, Path, Visibility, VisibilityKind};
use syntax::codemap::{BytePos, Span, NO_EXPANSION};

use config::Color;
use rewrite::RewriteContext;
use shape::Shape;

// When we get scoped annotations, we should have rustfmt::skip.
const SKIP_ANNOTATION: &str = "rustfmt_skip";

// Computes the length of a string's last line, minus offset.
pub fn extra_offset(text: &str, shape: Shape) -> usize {
    match text.rfind('\n') {
        // 1 for newline character
        Some(idx) => text.len()
            .checked_sub(idx + 1 + shape.used_width())
            .unwrap_or(0),
        None => text.len(),
    }
}

// Uses Cow to avoid allocating in the common cases.
pub fn format_visibility(vis: &Visibility) -> Cow<'static, str> {
    match vis.node {
        VisibilityKind::Public => Cow::from("pub "),
        VisibilityKind::Inherited => Cow::from(""),
        VisibilityKind::Crate(CrateSugar::PubCrate) => Cow::from("pub(crate) "),
        VisibilityKind::Crate(CrateSugar::JustCrate) => Cow::from("crate "),
        VisibilityKind::Restricted { ref path, .. } => {
            let Path { ref segments, .. } = **path;
            let mut segments_iter = segments.iter().map(|seg| seg.identifier.name.to_string());
            if path.is_global() {
                segments_iter
                    .next()
                    .expect("Non-global path in pub(restricted)?");
            }
            let is_keyword = |s: &str| s == "self" || s == "super";
            let path = segments_iter.collect::<Vec<_>>().join("::");
            let in_str = if is_keyword(&path) { "" } else { "in " };

            Cow::from(format!("pub({}{}) ", in_str, path))
        }
    }
}

#[inline]
pub fn format_constness(constness: ast::Constness) -> &'static str {
    match constness {
        ast::Constness::Const => "const ",
        ast::Constness::NotConst => "",
    }
}

#[inline]
pub fn format_defaultness(defaultness: ast::Defaultness) -> &'static str {
    match defaultness {
        ast::Defaultness::Default => "default ",
        ast::Defaultness::Final => "",
    }
}

#[inline]
pub fn format_unsafety(unsafety: ast::Unsafety) -> &'static str {
    match unsafety {
        ast::Unsafety::Unsafe => "unsafe ",
        ast::Unsafety::Normal => "",
    }
}

#[inline]
pub fn format_mutability(mutability: ast::Mutability) -> &'static str {
    match mutability {
        ast::Mutability::Mutable => "mut ",
        ast::Mutability::Immutable => "",
    }
}

#[inline]
pub fn format_abi(abi: abi::Abi, explicit_abi: bool, is_mod: bool) -> Cow<'static, str> {
    if abi == abi::Abi::Rust && !is_mod {
        Cow::from("")
    } else if abi == abi::Abi::C && !explicit_abi {
        Cow::from("extern ")
    } else {
        Cow::from(format!("extern {} ", abi))
    }
}

#[inline]
// Transform `Vec<syntax::ptr::P<T>>` into `Vec<&T>`
pub fn ptr_vec_to_ref_vec<T>(vec: &[ptr::P<T>]) -> Vec<&T> {
    vec.iter().map(|x| &**x).collect::<Vec<_>>()
}

#[inline]
pub fn filter_attributes(attrs: &[ast::Attribute], style: ast::AttrStyle) -> Vec<ast::Attribute> {
    attrs
        .iter()
        .filter(|a| a.style == style)
        .cloned()
        .collect::<Vec<_>>()
}

#[inline]
pub fn inner_attributes(attrs: &[ast::Attribute]) -> Vec<ast::Attribute> {
    filter_attributes(attrs, ast::AttrStyle::Inner)
}

#[inline]
pub fn outer_attributes(attrs: &[ast::Attribute]) -> Vec<ast::Attribute> {
    filter_attributes(attrs, ast::AttrStyle::Outer)
}

#[inline]
pub fn last_line_contains_single_line_comment(s: &str) -> bool {
    s.lines().last().map_or(false, |l| l.contains("//"))
}

#[inline]
pub fn is_attributes_extendable(attrs_str: &str) -> bool {
    !attrs_str.contains('\n') && !last_line_contains_single_line_comment(attrs_str)
}

// The width of the first line in s.
#[inline]
pub fn first_line_width(s: &str) -> usize {
    match s.find('\n') {
        Some(n) => n,
        None => s.len(),
    }
}

// The width of the last line in s.
#[inline]
pub fn last_line_width(s: &str) -> usize {
    match s.rfind('\n') {
        Some(n) => s.len() - n - 1,
        None => s.len(),
    }
}

// The total used width of the last line.
#[inline]
pub fn last_line_used_width(s: &str, offset: usize) -> usize {
    if s.contains('\n') {
        last_line_width(s)
    } else {
        offset + s.len()
    }
}

#[inline]
pub fn trimmed_last_line_width(s: &str) -> usize {
    match s.rfind('\n') {
        Some(n) => s[(n + 1)..].trim().len(),
        None => s.trim().len(),
    }
}

#[inline]
pub fn last_line_extendable(s: &str) -> bool {
    if s.ends_with("\"#") {
        return true;
    }
    for c in s.chars().rev() {
        match c {
            '(' | ')' | ']' | '}' | '?' | '>' => continue,
            '\n' => break,
            _ if c.is_whitespace() => continue,
            _ => return false,
        }
    }
    true
}

#[inline]
fn is_skip(meta_item: &MetaItem) -> bool {
    match meta_item.node {
        MetaItemKind::Word => meta_item.name == SKIP_ANNOTATION,
        MetaItemKind::List(ref l) => {
            meta_item.name == "cfg_attr" && l.len() == 2 && is_skip_nested(&l[1])
        }
        _ => false,
    }
}

#[inline]
fn is_skip_nested(meta_item: &NestedMetaItem) -> bool {
    match meta_item.node {
        NestedMetaItemKind::MetaItem(ref mi) => is_skip(mi),
        NestedMetaItemKind::Literal(_) => false,
    }
}

#[inline]
pub fn contains_skip(attrs: &[Attribute]) -> bool {
    attrs
        .iter()
        .any(|a| a.meta().map_or(false, |a| is_skip(&a)))
}

#[inline]
pub fn semicolon_for_expr(context: &RewriteContext, expr: &ast::Expr) -> bool {
    match expr.node {
        ast::ExprKind::Ret(..) | ast::ExprKind::Continue(..) | ast::ExprKind::Break(..) => {
            context.config.trailing_semicolon()
        }
        _ => false,
    }
}

#[inline]
pub fn semicolon_for_stmt(context: &RewriteContext, stmt: &ast::Stmt) -> bool {
    match stmt.node {
        ast::StmtKind::Semi(ref expr) => match expr.node {
            ast::ExprKind::While(..)
            | ast::ExprKind::WhileLet(..)
            | ast::ExprKind::Loop(..)
            | ast::ExprKind::ForLoop(..) => false,
            ast::ExprKind::Break(..) | ast::ExprKind::Continue(..) | ast::ExprKind::Ret(..) => {
                context.config.trailing_semicolon()
            }
            _ => true,
        },
        ast::StmtKind::Expr(..) => false,
        _ => true,
    }
}

#[inline]
pub fn stmt_expr(stmt: &ast::Stmt) -> Option<&ast::Expr> {
    match stmt.node {
        ast::StmtKind::Expr(ref expr) => Some(expr),
        _ => None,
    }
}

#[inline]
pub fn count_newlines(input: &str) -> usize {
    input.chars().filter(|&c| c == '\n').count()
}

macro_rules! msg {
    ($($arg:tt)*) => (
        match writeln!(&mut ::std::io::stderr(), $($arg)* ) {
            Ok(_) => {},
            Err(x) => panic!("Unable to write to stderr: {}", x),
        }
    )
}

// For format_missing and last_pos, need to use the source callsite (if applicable).
// Required as generated code spans aren't guaranteed to follow on from the last span.
macro_rules! source {
    ($this: ident, $sp: expr) => {
        $sp.source_callsite()
    };
}

pub fn mk_sp(lo: BytePos, hi: BytePos) -> Span {
    Span::new(lo, hi, NO_EXPANSION)
}

// Return true if the given span does not intersect with file lines.
macro_rules! out_of_file_lines_range {
    ($self: ident, $span: expr) => {
        !$self.config.file_lines().is_all()
            && !$self
                .config
                .file_lines()
                .intersects(&$self.codemap.lookup_line_range($span))
    };
}

macro_rules! skip_out_of_file_lines_range {
    ($self: ident, $span: expr) => {
        if out_of_file_lines_range!($self, $span) {
            return None;
        }
    };
}

macro_rules! skip_out_of_file_lines_range_visitor {
    ($self: ident, $span: expr) => {
        if out_of_file_lines_range!($self, $span) {
            $self.push_rewrite($span, None);
            return;
        }
    };
}

// Wraps String in an Option. Returns Some when the string adheres to the
// Rewrite constraints defined for the Rewrite trait and else otherwise.
pub fn wrap_str(s: String, max_width: usize, shape: Shape) -> Option<String> {
    if is_valid_str(&s, max_width, shape) {
        Some(s)
    } else {
        None
    }
}

fn is_valid_str(snippet: &str, max_width: usize, shape: Shape) -> bool {
    if !snippet.is_empty() {
        // First line must fits with `shape.width`.
        if first_line_width(snippet) > shape.width {
            return false;
        }
        // If the snippet does not include newline, we are done.
        if first_line_width(snippet) == snippet.len() {
            return true;
        }
        // The other lines must fit within the maximum width.
        if snippet.lines().skip(1).any(|line| line.len() > max_width) {
            return false;
        }
        // A special check for the last line, since the caller may
        // place trailing characters on this line.
        if last_line_width(snippet) > shape.used_width() + shape.width {
            return false;
        }
    }
    true
}

#[inline]
pub fn colon_spaces(before: bool, after: bool) -> &'static str {
    match (before, after) {
        (true, true) => " : ",
        (true, false) => " :",
        (false, true) => ": ",
        (false, false) => ":",
    }
}

#[inline]
pub fn paren_overhead(context: &RewriteContext) -> usize {
    if context.config.spaces_within_parens_and_brackets() {
        4
    } else {
        2
    }
}

pub fn left_most_sub_expr(e: &ast::Expr) -> &ast::Expr {
    match e.node {
        ast::ExprKind::InPlace(ref e, _)
        | ast::ExprKind::Call(ref e, _)
        | ast::ExprKind::Binary(_, ref e, _)
        | ast::ExprKind::Cast(ref e, _)
        | ast::ExprKind::Type(ref e, _)
        | ast::ExprKind::Assign(ref e, _)
        | ast::ExprKind::AssignOp(_, ref e, _)
        | ast::ExprKind::Field(ref e, _)
        | ast::ExprKind::TupField(ref e, _)
        | ast::ExprKind::Index(ref e, _)
        | ast::ExprKind::Range(Some(ref e), _, _)
        | ast::ExprKind::Try(ref e) => left_most_sub_expr(e),
        _ => e,
    }
}

// isatty shamelessly adapted from cargo.
#[cfg(unix)]
pub fn isatty() -> bool {
    extern crate libc;

    unsafe { libc::isatty(libc::STDOUT_FILENO) != 0 }
}
#[cfg(windows)]
pub fn isatty() -> bool {
    extern crate winapi;

    unsafe {
        let handle = winapi::um::processenv::GetStdHandle(winapi::um::winbase::STD_OUTPUT_HANDLE);
        let mut out = 0;
        winapi::um::consoleapi::GetConsoleMode(handle, &mut out) != 0
    }
}

pub fn use_colored_tty(color: Color) -> bool {
    match color {
        Color::Always => true,
        Color::Never => false,
        Color::Auto => isatty(),
    }
}

pub fn starts_with_newline(s: &str) -> bool {
    s.starts_with('\n') || s.starts_with("\r\n")
}
<commit_msg>Omit unnecessary UTF-8 decoding<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::borrow::Cow;

use syntax::{abi, ptr};
use syntax::ast::{self, Attribute, CrateSugar, MetaItem, MetaItemKind, NestedMetaItem,
                  NestedMetaItemKind, Path, Visibility, VisibilityKind};
use syntax::codemap::{BytePos, Span, NO_EXPANSION};

use config::Color;
use rewrite::RewriteContext;
use shape::Shape;

// When we get scoped annotations, we should have rustfmt::skip.
const SKIP_ANNOTATION: &str = "rustfmt_skip";

// Computes the length of a string's last line, minus offset.
pub fn extra_offset(text: &str, shape: Shape) -> usize {
    match text.rfind('\n') {
        // 1 for newline character
        Some(idx) => text.len()
            .checked_sub(idx + 1 + shape.used_width())
            .unwrap_or(0),
        None => text.len(),
    }
}

// Uses Cow to avoid allocating in the common cases.
pub fn format_visibility(vis: &Visibility) -> Cow<'static, str> {
    match vis.node {
        VisibilityKind::Public => Cow::from("pub "),
        VisibilityKind::Inherited => Cow::from(""),
        VisibilityKind::Crate(CrateSugar::PubCrate) => Cow::from("pub(crate) "),
        VisibilityKind::Crate(CrateSugar::JustCrate) => Cow::from("crate "),
        VisibilityKind::Restricted { ref path, .. } => {
            let Path { ref segments, .. } = **path;
            let mut segments_iter = segments.iter().map(|seg| seg.identifier.name.to_string());
            if path.is_global() {
                segments_iter
                    .next()
                    .expect("Non-global path in pub(restricted)?");
            }
            let is_keyword = |s: &str| s == "self" || s == "super";
            let path = segments_iter.collect::<Vec<_>>().join("::");
            let in_str = if is_keyword(&path) { "" } else { "in " };

            Cow::from(format!("pub({}{}) ", in_str, path))
        }
    }
}

#[inline]
pub fn format_constness(constness: ast::Constness) -> &'static str {
    match constness {
        ast::Constness::Const => "const ",
        ast::Constness::NotConst => "",
    }
}

#[inline]
pub fn format_defaultness(defaultness: ast::Defaultness) -> &'static str {
    match defaultness {
        ast::Defaultness::Default => "default ",
        ast::Defaultness::Final => "",
    }
}

#[inline]
pub fn format_unsafety(unsafety: ast::Unsafety) -> &'static str {
    match unsafety {
        ast::Unsafety::Unsafe => "unsafe ",
        ast::Unsafety::Normal => "",
    }
}

#[inline]
pub fn format_mutability(mutability: ast::Mutability) -> &'static str {
    match mutability {
        ast::Mutability::Mutable => "mut ",
        ast::Mutability::Immutable => "",
    }
}

#[inline]
pub fn format_abi(abi: abi::Abi, explicit_abi: bool, is_mod: bool) -> Cow<'static, str> {
    if abi == abi::Abi::Rust && !is_mod {
        Cow::from("")
    } else if abi == abi::Abi::C && !explicit_abi {
        Cow::from("extern ")
    } else {
        Cow::from(format!("extern {} ", abi))
    }
}

#[inline]
// Transform `Vec<syntax::ptr::P<T>>` into `Vec<&T>`
pub fn ptr_vec_to_ref_vec<T>(vec: &[ptr::P<T>]) -> Vec<&T> {
    vec.iter().map(|x| &**x).collect::<Vec<_>>()
}

#[inline]
pub fn filter_attributes(attrs: &[ast::Attribute], style: ast::AttrStyle) -> Vec<ast::Attribute> {
    attrs
        .iter()
        .filter(|a| a.style == style)
        .cloned()
        .collect::<Vec<_>>()
}

#[inline]
pub fn inner_attributes(attrs: &[ast::Attribute]) -> Vec<ast::Attribute> {
    filter_attributes(attrs, ast::AttrStyle::Inner)
}

#[inline]
pub fn outer_attributes(attrs: &[ast::Attribute]) -> Vec<ast::Attribute> {
    filter_attributes(attrs, ast::AttrStyle::Outer)
}

#[inline]
pub fn last_line_contains_single_line_comment(s: &str) -> bool {
    s.lines().last().map_or(false, |l| l.contains("//"))
}

#[inline]
pub fn is_attributes_extendable(attrs_str: &str) -> bool {
    !attrs_str.contains('\n') && !last_line_contains_single_line_comment(attrs_str)
}

// The width of the first line in s.
#[inline]
pub fn first_line_width(s: &str) -> usize {
    match s.find('\n') {
        Some(n) => n,
        None => s.len(),
    }
}

// The width of the last line in s.
#[inline]
pub fn last_line_width(s: &str) -> usize {
    match s.rfind('\n') {
        Some(n) => s.len() - n - 1,
        None => s.len(),
    }
}

// The total used width of the last line.
#[inline]
pub fn last_line_used_width(s: &str, offset: usize) -> usize {
    if s.contains('\n') {
        last_line_width(s)
    } else {
        offset + s.len()
    }
}

#[inline]
pub fn trimmed_last_line_width(s: &str) -> usize {
    match s.rfind('\n') {
        Some(n) => s[(n + 1)..].trim().len(),
        None => s.trim().len(),
    }
}

#[inline]
pub fn last_line_extendable(s: &str) -> bool {
    if s.ends_with("\"#") {
        return true;
    }
    for c in s.chars().rev() {
        match c {
            '(' | ')' | ']' | '}' | '?' | '>' => continue,
            '\n' => break,
            _ if c.is_whitespace() => continue,
            _ => return false,
        }
    }
    true
}

#[inline]
fn is_skip(meta_item: &MetaItem) -> bool {
    match meta_item.node {
        MetaItemKind::Word => meta_item.name == SKIP_ANNOTATION,
        MetaItemKind::List(ref l) => {
            meta_item.name == "cfg_attr" && l.len() == 2 && is_skip_nested(&l[1])
        }
        _ => false,
    }
}

#[inline]
fn is_skip_nested(meta_item: &NestedMetaItem) -> bool {
    match meta_item.node {
        NestedMetaItemKind::MetaItem(ref mi) => is_skip(mi),
        NestedMetaItemKind::Literal(_) => false,
    }
}

#[inline]
pub fn contains_skip(attrs: &[Attribute]) -> bool {
    attrs
        .iter()
        .any(|a| a.meta().map_or(false, |a| is_skip(&a)))
}

#[inline]
pub fn semicolon_for_expr(context: &RewriteContext, expr: &ast::Expr) -> bool {
    match expr.node {
        ast::ExprKind::Ret(..) | ast::ExprKind::Continue(..) | ast::ExprKind::Break(..) => {
            context.config.trailing_semicolon()
        }
        _ => false,
    }
}

#[inline]
pub fn semicolon_for_stmt(context: &RewriteContext, stmt: &ast::Stmt) -> bool {
    match stmt.node {
        ast::StmtKind::Semi(ref expr) => match expr.node {
            ast::ExprKind::While(..)
            | ast::ExprKind::WhileLet(..)
            | ast::ExprKind::Loop(..)
            | ast::ExprKind::ForLoop(..) => false,
            ast::ExprKind::Break(..) | ast::ExprKind::Continue(..) | ast::ExprKind::Ret(..) => {
                context.config.trailing_semicolon()
            }
            _ => true,
        },
        ast::StmtKind::Expr(..) => false,
        _ => true,
    }
}

#[inline]
pub fn stmt_expr(stmt: &ast::Stmt) -> Option<&ast::Expr> {
    match stmt.node {
        ast::StmtKind::Expr(ref expr) => Some(expr),
        _ => None,
    }
}

#[inline]
pub fn count_newlines(input: &str) -> usize {
    // Using `as_bytes` to omit UTF-8 decoding
    input.as_bytes().iter().filter(|&&c| c == b'\n').count()
}

macro_rules! msg {
    ($($arg:tt)*) => (
        match writeln!(&mut ::std::io::stderr(), $($arg)* ) {
            Ok(_) => {},
            Err(x) => panic!("Unable to write to stderr: {}", x),
        }
    )
}

// For format_missing and last_pos, need to use the source callsite (if applicable).
// Required as generated code spans aren't guaranteed to follow on from the last span.
macro_rules! source {
    ($this: ident, $sp: expr) => {
        $sp.source_callsite()
    };
}

pub fn mk_sp(lo: BytePos, hi: BytePos) -> Span {
    Span::new(lo, hi, NO_EXPANSION)
}

// Return true if the given span does not intersect with file lines.
macro_rules! out_of_file_lines_range {
    ($self: ident, $span: expr) => {
        !$self.config.file_lines().is_all()
            && !$self
                .config
                .file_lines()
                .intersects(&$self.codemap.lookup_line_range($span))
    };
}

macro_rules! skip_out_of_file_lines_range {
    ($self: ident, $span: expr) => {
        if out_of_file_lines_range!($self, $span) {
            return None;
        }
    };
}

macro_rules! skip_out_of_file_lines_range_visitor {
    ($self: ident, $span: expr) => {
        if out_of_file_lines_range!($self, $span) {
            $self.push_rewrite($span, None);
            return;
        }
    };
}

// Wraps String in an Option. Returns Some when the string adheres to the
// Rewrite constraints defined for the Rewrite trait and else otherwise.
pub fn wrap_str(s: String, max_width: usize, shape: Shape) -> Option<String> {
    if is_valid_str(&s, max_width, shape) {
        Some(s)
    } else {
        None
    }
}

fn is_valid_str(snippet: &str, max_width: usize, shape: Shape) -> bool {
    if !snippet.is_empty() {
        // First line must fits with `shape.width`.
        if first_line_width(snippet) > shape.width {
            return false;
        }
        // If the snippet does not include newline, we are done.
        if first_line_width(snippet) == snippet.len() {
            return true;
        }
        // The other lines must fit within the maximum width.
        if snippet.lines().skip(1).any(|line| line.len() > max_width) {
            return false;
        }
        // A special check for the last line, since the caller may
        // place trailing characters on this line.
        if last_line_width(snippet) > shape.used_width() + shape.width {
            return false;
        }
    }
    true
}

#[inline]
pub fn colon_spaces(before: bool, after: bool) -> &'static str {
    match (before, after) {
        (true, true) => " : ",
        (true, false) => " :",
        (false, true) => ": ",
        (false, false) => ":",
    }
}

#[inline]
pub fn paren_overhead(context: &RewriteContext) -> usize {
    if context.config.spaces_within_parens_and_brackets() {
        4
    } else {
        2
    }
}

pub fn left_most_sub_expr(e: &ast::Expr) -> &ast::Expr {
    match e.node {
        ast::ExprKind::InPlace(ref e, _)
        | ast::ExprKind::Call(ref e, _)
        | ast::ExprKind::Binary(_, ref e, _)
        | ast::ExprKind::Cast(ref e, _)
        | ast::ExprKind::Type(ref e, _)
        | ast::ExprKind::Assign(ref e, _)
        | ast::ExprKind::AssignOp(_, ref e, _)
        | ast::ExprKind::Field(ref e, _)
        | ast::ExprKind::TupField(ref e, _)
        | ast::ExprKind::Index(ref e, _)
        | ast::ExprKind::Range(Some(ref e), _, _)
        | ast::ExprKind::Try(ref e) => left_most_sub_expr(e),
        _ => e,
    }
}

// isatty shamelessly adapted from cargo.
#[cfg(unix)]
pub fn isatty() -> bool {
    extern crate libc;

    unsafe { libc::isatty(libc::STDOUT_FILENO) != 0 }
}
#[cfg(windows)]
pub fn isatty() -> bool {
    extern crate winapi;

    unsafe {
        let handle = winapi::um::processenv::GetStdHandle(winapi::um::winbase::STD_OUTPUT_HANDLE);
        let mut out = 0;
        winapi::um::consoleapi::GetConsoleMode(handle, &mut out) != 0
    }
}

pub fn use_colored_tty(color: Color) -> bool {
    match color {
        Color::Always => true,
        Color::Never => false,
        Color::Auto => isatty(),
    }
}

pub fn starts_with_newline(s: &str) -> bool {
    s.starts_with('\n') || s.starts_with("\r\n")
}
<|endoftext|>