1use Span;
2use errors::Result;
3use std::io::Read;
4
5const NL: u8 = '\n' as u8;
6const CR: u8 = '\r' as u8;
7
8#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
10pub struct Position {
11 pub line: usize,
12 pub col: usize,
13}
14
15pub fn find_line<'a, R: AsMut<Read + 'a>>(
17 mut reader: R,
18 span: (usize, usize),
19) -> Result<(String, usize, (usize, usize))> {
20 let r = reader.as_mut();
21
22 let mut line = 0usize;
23 let mut current = 0usize;
24 let mut buffer: Vec<u8> = Vec::new();
25
26 let start = span.0;
27 let end = span.1;
28
29 let mut it = r.bytes().peekable();
30 let mut read = 0usize;
31
32 while let Some(b) = it.next() {
33 let b = b?;
34 read += 1;
35
36 match b {
37 NL => {}
38 _ => {
39 buffer.push(b);
40 continue;
41 }
42 }
43
44 let start_of_line = current;
45 current += read;
46
47 if current > start {
48 let buffer = String::from_utf8(buffer)?;
49 let end = ::std::cmp::min(end, current);
50 let range = (start - start_of_line, end - start_of_line);
51 return Ok((buffer, line, range));
52 }
53
54 read = 0usize;
55 line += 1;
56 buffer.clear();
57 }
58
59 Err("bad file position".into())
60}
61
62pub fn find_range<'a, R: AsMut<Read + 'a>, S: Into<Span>>(
64 mut reader: R,
65 span: S,
66 encoding: Encoding,
67) -> Result<(Position, Position)> {
68 let span = span.into();
69
70 let r = reader.as_mut();
71
72 let mut start = Position::default();
73 let mut end = Position::default();
74
75 let mut line = 0usize;
76 let mut col = 0usize;
77
78 let mut buffer = Vec::new();
80 let mut it = r.bytes().enumerate().peekable();
81
82 while let Some((c, b)) = it.next() {
83 let b = b?;
84
85 let nl = match b {
86 CR => {
88 if let Some(&(_, Ok(NL))) = it.peek() {
90 it.next();
91 }
92
93 true
94 }
95 NL => true,
96 _ => false,
97 };
98
99 if nl {
100 line += 1;
101 col = 0;
102 buffer.clear();
103 } else {
104 buffer.push(b);
105 }
106
107 if c == span.start {
108 start.line = line;
109 start.col = encoding.column(&buffer, col)?;
110 }
111
112 if c == span.end {
113 end.line = line;
114 end.col = encoding.column(&buffer, col)?;
115 break;
116 }
117
118 if !nl {
119 col += 1;
120 }
121 }
122
123 Ok((start, end))
124}
125
126#[derive(Debug, Clone, Copy)]
128pub enum Encoding {
129 Bytes,
131 Utf8,
133 Utf16,
135}
136
137impl Encoding {
138 pub fn column(&self, buffer: &[u8], col: usize) -> Result<usize> {
140 use self::Encoding::*;
141
142 match *self {
143 Bytes => Ok(col),
144 Utf8 => Ok(::std::str::from_utf8(&buffer[..col])?.chars().count()),
145 Utf16 => Ok(::std::str::from_utf8(&buffer[..col])?
146 .encode_utf16()
147 .count()),
148 }
149 }
150}