summaryrefslogtreecommitdiffstats
path: root/third_party/rust/xml-rs/src/util.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/xml-rs/src/util.rs')
-rw-r--r--third_party/rust/xml-rs/src/util.rs107
1 files changed, 107 insertions, 0 deletions
diff --git a/third_party/rust/xml-rs/src/util.rs b/third_party/rust/xml-rs/src/util.rs
new file mode 100644
index 0000000000..23fee04eed
--- /dev/null
+++ b/third_party/rust/xml-rs/src/util.rs
@@ -0,0 +1,107 @@
+use std::io::{self, Read};
+use std::str;
+use std::fmt;
+
+#[derive(Debug)]
+pub enum CharReadError {
+ UnexpectedEof,
+ Utf8(str::Utf8Error),
+ Io(io::Error)
+}
+
+impl From<str::Utf8Error> for CharReadError {
+ fn from(e: str::Utf8Error) -> CharReadError {
+ CharReadError::Utf8(e)
+ }
+}
+
+impl From<io::Error> for CharReadError {
+ fn from(e: io::Error) -> CharReadError {
+ CharReadError::Io(e)
+ }
+}
+
+impl fmt::Display for CharReadError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::CharReadError::*;
+ match *self {
+ UnexpectedEof => write!(f, "unexpected end of stream"),
+ Utf8(ref e) => write!(f, "UTF-8 decoding error: {}", e),
+ Io(ref e) => write!(f, "I/O error: {}", e)
+ }
+ }
+}
+
+pub fn next_char_from<R: Read>(source: &mut R) -> Result<Option<char>, CharReadError> {
+ const MAX_CODEPOINT_LEN: usize = 4;
+
+ let mut bytes = source.bytes();
+ let mut buf = [0u8; MAX_CODEPOINT_LEN];
+ let mut pos = 0;
+
+ loop {
+ let next = match bytes.next() {
+ Some(Ok(b)) => b,
+ Some(Err(e)) => return Err(e.into()),
+ None if pos == 0 => return Ok(None),
+ None => return Err(CharReadError::UnexpectedEof)
+ };
+ buf[pos] = next;
+ pos += 1;
+
+ match str::from_utf8(&buf[..pos]) {
+ Ok(s) => return Ok(s.chars().next()), // always Some(..)
+ Err(_) if pos < MAX_CODEPOINT_LEN => {},
+ Err(e) => return Err(e.into())
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ #[test]
+ fn test_next_char_from() {
+ use std::io;
+ use std::error::Error;
+
+ let mut bytes: &[u8] = "correct".as_bytes(); // correct ASCII
+ assert_eq!(super::next_char_from(&mut bytes).unwrap(), Some('c'));
+
+ let mut bytes: &[u8] = "правильно".as_bytes(); // correct BMP
+ assert_eq!(super::next_char_from(&mut bytes).unwrap(), Some('п'));
+
+ let mut bytes: &[u8] = "😊".as_bytes(); // correct non-BMP
+ assert_eq!(super::next_char_from(&mut bytes).unwrap(), Some('😊'));
+
+ let mut bytes: &[u8] = b""; // empty
+ assert_eq!(super::next_char_from(&mut bytes).unwrap(), None);
+
+ let mut bytes: &[u8] = b"\xf0\x9f\x98"; // incomplete code point
+ match super::next_char_from(&mut bytes).unwrap_err() {
+ super::CharReadError::UnexpectedEof => {},
+ e => panic!("Unexpected result: {:?}", e)
+ };
+
+ let mut bytes: &[u8] = b"\xff\x9f\x98\x32"; // invalid code point
+ match super::next_char_from(&mut bytes).unwrap_err() {
+ super::CharReadError::Utf8(_) => {},
+ e => panic!("Unexpected result: {:?}", e)
+ };
+
+
+ // error during read
+ struct ErrorReader;
+ impl io::Read for ErrorReader {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ Err(io::Error::new(io::ErrorKind::Other, "test error"))
+ }
+ }
+
+ let mut r = ErrorReader;
+ match super::next_char_from(&mut r).unwrap_err() {
+ super::CharReadError::Io(ref e) if e.kind() == io::ErrorKind::Other &&
+ e.description() == "test error" => {},
+ e => panic!("Unexpected result: {:?}", e)
+ }
+ }
+}