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

// 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/.

/// Basic ISO box structure.
pub struct BoxHeader {
    /// Four character box type
pub name: u32,
    /// Size of the box in bytes
pub size: u64,
    /// Offset to the start of the contained data (or header size).
pub offset: u64,
}

/// File type box 'ftyp'.
pub struct FileTypeBox {
    name: u32,
    size: u64,
    major_brand: u32,
    minor_version: u32,
    compatible_brands: Vec<u32>,
}

/// Movie header box 'mvhd'.
pub struct MovieHeaderBox {
    pub name: u32,
    pub size: u64,
    pub timescale: u32,
    pub duration: u64,
    // Ignore other fields.
}

pub struct TrackHeaderBox {
    pub name: u32,
    pub size: u64,
    pub track_id: u32,
    pub duration: u64,
    pub width: u32,
    pub height: u32,
}

extern crate byteorder;
use byteorder::{BigEndian, ReadBytesExt};
use std::io::{Error, ErrorKind, Read, Seek, SeekFrom, Take};
use std::io::Cursor;

/// Parse a box out of a data buffer.
pub fn read_box_header<T: ReadBytesExt>(src: &mut T) -> byteorder::Result<BoxHeader> {
    let tmp_size = try!(src.read_u32::<BigEndian>());
    let name = try!(src.read_u32::<BigEndian>());
    let size = match tmp_size {
        1 => try!(src.read_u64::<BigEndian>()),
        _ => tmp_size as u64,
    };
    assert!(size >= 8);
    if tmp_size == 1 {
        assert!(size >= 16);
    }
    let offset = match tmp_size {
        1 => 4 + 4 + 8,
        _ => 4 + 4,
    };
    assert!(offset <= size);
    Ok(BoxHeader{
      name: name,
      size: size,
      offset: offset,
    })
}

/// Parse the extra header fields for a full box.
fn read_fullbox_extra<T: ReadBytesExt>(src: &mut T) -> (u8, u32) {
    let version = src.read_u8().unwrap();
    let flags_a = src.read_u8().unwrap();
    let flags_b = src.read_u8().unwrap();
    let flags_c = src.read_u8().unwrap();
    (version, (flags_a as u32) << 16 |
              (flags_b as u32) <<  8 |
              (flags_c as u32))
}

/// Skip over the contents of a box.
pub fn skip_box_content<T: ReadBytesExt + Seek>
  (src: &mut T, header: &BoxHeader)
  -> std::io::Result<u64>
{
    src.seek(SeekFrom::Current((header.size - header.offset) as i64))
}

/// Helper to construct a Take over the contents of a box.
fn limit<'a, T: Read>(f: &'a mut T, h: &BoxHeader) -> Take<&'a mut T> {
    f.take(h.size - h.offset)
}

/// Helper to construct a Cursor over the contents of a box.
fn recurse<T: Read>(f: &mut T, h: &BoxHeader) -> byteorder::Result<()> {
    println!("{} -- recursing", h);
    // FIXME: I couldn't figure out how to do this without copying.
    // We use Seek on the Read we return in skip_box_content, but
    // that trait isn't implemented for a Take like our limit()
    // returns. Slurping the buffer and wrapping it in a Cursor
    // functions as a work around.
    let buf: Vec<u8> = limit(f, &h)
        .bytes()
        .map(|u| u.unwrap())
        .collect();
    let mut content = Cursor::new(buf);
    loop {
        match read_box(&mut content) {
            Ok(_) => {},
            Err(byteorder::Error::UnexpectedEOF) => {
                // byteorder returns EOF at the end of the buffer.
                // This isn't an error for us, just an signal to
                // stop recursion.
                break;
            },
            Err(byteorder::Error::Io(e)) => {
                println!("Error '{:?}' reading box", e.kind());
                return Err(byteorder::Error::Io(e));
            },
        }
    }
    println!("{} -- end", h);
    Ok(())
}

/// Read the contents of a box, including sub boxes.
/// Right now it just prints the box value rather than
/// returning anything.
pub fn read_box<T: Read + Seek>(f: &mut T) -> byteorder::Result<()> {
    read_box_header(f).and_then(|h| {
        match &(fourcc_to_string(h.name))[..] {
            "ftyp" => {
                let mut content = limit(f, &h);
                let ftyp = try!(read_ftyp(&mut content, &h));
                println!("{}", ftyp);
            },
            "moov" => try!(recurse(f, &h)),
            "mvhd" => {
                let mut content = limit(f, &h);
                let mvhd = try!(read_mvhd(&mut content, &h));
                println!("  {}", mvhd);
            },
            "trak" => try!(recurse(f, &h)),
            "tkhd" => {
                let mut content = limit(f, &h);
                let tkhd = try!(read_tkhd(&mut content, &h));
                println!("  {}", tkhd);
            },
            _ => {
                // Skip the contents of unknown chunks.
                println!("{} (skipped)", h);
                try!(skip_box_content(f, &h).and(Ok(())));
            },
        };
        Ok(()) // and_then needs a Result to return.
    })
}

/// Entry point for C language callers.
/// Take a buffer and call read_box() on it.
#[no_mangle]
pub unsafe extern fn read_box_from_buffer(buffer: *const u8, size: usize)
  -> bool {
    use std::slice;
    use std::thread;

    // Validate arguments from C.
    if buffer.is_null() || size < 8 {
        return false;
    }

    // Wrap the buffer we've been give in a slice.
    let b = slice::from_raw_parts(buffer, size);
    let mut c = Cursor::new(b);

    // Parse in a subthread.
    let task = thread::spawn(move || {
        read_box(&mut c).unwrap();
    });
    // Catch any panics.
    task.join().is_ok()
}

/// Parse an ftype box.
pub fn read_ftyp<T: ReadBytesExt>(src: &mut T, head: &BoxHeader)
  -> byteorder::Result<FileTypeBox> {
    let major = try!(src.read_u32::<BigEndian>());
    let minor = try!(src.read_u32::<BigEndian>());
    let brand_count = (head.size - 8 - 8) /4;
    let mut brands = Vec::new();
    for _ in 0..brand_count {
        brands.push( try!(src.read_u32::<BigEndian>()) );
    }
    Ok(FileTypeBox{
        name: head.name,
        size: head.size,
        major_brand: major,
        minor_version: minor,
        compatible_brands: brands,
    })
}

/// Parse an mvhd box.
pub fn read_mvhd<T: ReadBytesExt>(src: &mut T, head: &BoxHeader)
  -> byteorder::Result<MovieHeaderBox> {
    let (version, _) = read_fullbox_extra(src);
    match version {
        1 => {
            // 64 bit creation and modification times.
            let mut skip: Vec<u8> = vec![0; 16];
            let r = try!(src.read(&mut skip));
            assert!(r == skip.len());
        },
        0 => {
            // 32 bit creation and modification times.
            // 64 bit creation and modification times.
            let mut skip: Vec<u8> = vec![0; 8];
            let r = try!(src.read(&mut skip));
            assert!(r == skip.len());
        },
        _ => panic!("invalid mhdr version"),
    }
    let timescale = src.read_u32::<BigEndian>().unwrap();
    let duration = match version {
        1 => try!(src.read_u64::<BigEndian>()),
        0 => try!(src.read_u32::<BigEndian>()) as u64,
        _ => panic!("invalid mhdr version"),
    };
    // Skip remaining fields.
    let mut skip: Vec<u8> = vec![0; 80];
    let r = try!(src.read(&mut skip));
    assert!(r == skip.len());
    Ok(MovieHeaderBox {
        name: head.name,
        size: head.size,
        timescale: timescale,
        duration: duration,
    })
}

/// Parse a tkhd box.
pub fn read_tkhd<T: ReadBytesExt>(src: &mut T, head: &BoxHeader)
  -> byteorder::Result<TrackHeaderBox> {
    let (version, flags) = read_fullbox_extra(src);
    if flags & 0x1u32 == 0 || flags & 0x2u32 == 0 {
        return Err(byteorder::Error::Io(
            Error::new(ErrorKind::Other, "Track is disabled")
        ));
    }
    match version {
        1 => {
            // 64 bit creation and modification times.
            let mut skip: Vec<u8> = vec![0; 16];
            let r = try!(src.read(&mut skip));
            assert!(r == skip.len());
        },
        0 => {
            // 32 bit creation and modification times.
            // 64 bit creation and modification times.
            let mut skip: Vec<u8> = vec![0; 8];
            let r = try!(src.read(&mut skip));
            assert!(r == skip.len());
        },
        _ => panic!("invalid tkhd version"),
    }
    let track_id = try!(src.read_u32::<BigEndian>());
    let _reserved = try!(src.read_u32::<BigEndian>());
    assert!(_reserved == 0);
    let duration = match version {
        1 => {
            try!(src.read_u64::<BigEndian>())
        },
        0 => try!(src.read_u32::<BigEndian>()) as u64,
        _ => panic!("invalid tkhd version"),
    };
    let _reserved = try!(src.read_u32::<BigEndian>());
    let _reserved = try!(src.read_u32::<BigEndian>());
    // Skip uninterested fields.
    let mut skip: Vec<u8> = vec![0; 44];
    let r = try!(src.read(&mut skip));
    assert!(r == skip.len());
    let width = try!(src.read_u32::<BigEndian>());
    let height = try!(src.read_u32::<BigEndian>());
    Ok(TrackHeaderBox {
        name: head.name,
        size: head.size,
        track_id: track_id,
        duration: duration,
        width: width,
        height: height,
    })
}

/// Convert the iso box type or other 4-character value to a string.
fn fourcc_to_string(name: u32) -> String {
    let u32_to_vec = |u| {
        vec!((u >> 24 & 0xffu32) as u8,
             (u >> 16 & 0xffu32) as u8,
             (u >>  8 & 0xffu32) as u8,
             (u & 0xffu32) as u8)
    };
    let name_bytes = u32_to_vec(name);
    String::from_utf8_lossy(&name_bytes).into_owned()
}

use std::fmt;
impl fmt::Display for BoxHeader {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "'{}' {} bytes", fourcc_to_string(self.name), self.size)
    }
}

impl fmt::Display for FileTypeBox {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let name = fourcc_to_string(self.name);
        let brand = fourcc_to_string(self.major_brand);
        write!(f, "'{}' {} bytes '{}' v{}", name, self.size,
            brand, self.minor_version)
    }
}

impl fmt::Display for MovieHeaderBox {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let name = fourcc_to_string(self.name);
        write!(f, "'{}' {} bytes duration {}s", name, self.size,
            (self.duration as f64)/(self.timescale as f64))
    }
}

use std::u16;
impl fmt::Display for TrackHeaderBox {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let name = fourcc_to_string(self.name);
        // Dimensions are 16.16 fixed-point.
        let base = u16::MAX as f64 + 1.0;
        let width = (self.width as f64) / base;
        let height = (self.height as f64) / base;
        write!(f, "'{}' {} bytes duration {} id {} {}x{}",
            name, self.size, self.duration, self.track_id,
            width, height)
    }
}

#[test]
fn test_read_box_header() {
    use std::io::Cursor;
    use std::io::Write;
    let mut test: Vec<u8> = vec![0, 0, 0, 8];  // minimal box length
    write!(&mut test, "test").unwrap(); // box type
    let mut stream = Cursor::new(test);
    let parsed = read_box_header(&mut stream).unwrap();
    assert_eq!(parsed.name, 1952805748);
    assert_eq!(parsed.size, 8);
    println!("box {}", parsed);
}

#[test]
fn test_read_box_header_long() {
    use std::io::Cursor;
    let mut test: Vec<u8> = vec![0, 0, 0, 1]; // long box extension code
    test.extend("long".to_string().into_bytes()); // box type
    test.extend(vec![0, 0, 0, 0, 0, 0, 16, 0]); // 64 bit size
    // Skip generating box content.
    let mut stream = Cursor::new(test);
    let parsed = read_box_header(&mut stream).unwrap();
    assert_eq!(parsed.name, 1819242087);
    assert_eq!(parsed.size, 4096);
    println!("box {}", parsed);
}

#[test]
fn test_read_ftyp() {
    use std::io::Cursor;
    use std::io::Write;
    let mut test: Vec<u8> = vec![0, 0, 0, 24]; // size
    write!(&mut test, "ftyp").unwrap(); // type
    write!(&mut test, "mp42").unwrap(); // major brand
    test.extend(vec![0, 0, 0, 0]);      // minor version
    write!(&mut test, "isom").unwrap(); // compatible brands...
    write!(&mut test, "mp42").unwrap();
    assert_eq!(test.len(), 24);

    let mut stream = Cursor::new(test);
    let header = read_box_header(&mut stream).unwrap();
    let parsed = read_ftyp(&mut stream, &header).unwrap();
    assert_eq!(parsed.name, 1718909296);
    assert_eq!(parsed.size, 24);
    assert_eq!(parsed.major_brand, 1836069938);
    assert_eq!(parsed.minor_version, 0);
    assert_eq!(parsed.compatible_brands.len(), 2);
    println!("box {}", parsed);
}
<commit_msg>Isolate our use of io::Error.<commit_after>// Module for parsing ISO Base Media Format aka video/mp4 streams.

// 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/.

/// Basic ISO box structure.
pub struct BoxHeader {
    /// Four character box type
pub name: u32,
    /// Size of the box in bytes
pub size: u64,
    /// Offset to the start of the contained data (or header size).
pub offset: u64,
}

/// File type box 'ftyp'.
pub struct FileTypeBox {
    name: u32,
    size: u64,
    major_brand: u32,
    minor_version: u32,
    compatible_brands: Vec<u32>,
}

/// Movie header box 'mvhd'.
pub struct MovieHeaderBox {
    pub name: u32,
    pub size: u64,
    pub timescale: u32,
    pub duration: u64,
    // Ignore other fields.
}

pub struct TrackHeaderBox {
    pub name: u32,
    pub size: u64,
    pub track_id: u32,
    pub duration: u64,
    pub width: u32,
    pub height: u32,
}

extern crate byteorder;
use byteorder::{BigEndian, ReadBytesExt};
use std::io::{Read, Seek, SeekFrom, Take};
use std::io::Cursor;

/// Parse a box out of a data buffer.
pub fn read_box_header<T: ReadBytesExt>(src: &mut T) -> byteorder::Result<BoxHeader> {
    let tmp_size = try!(src.read_u32::<BigEndian>());
    let name = try!(src.read_u32::<BigEndian>());
    let size = match tmp_size {
        1 => try!(src.read_u64::<BigEndian>()),
        _ => tmp_size as u64,
    };
    assert!(size >= 8);
    if tmp_size == 1 {
        assert!(size >= 16);
    }
    let offset = match tmp_size {
        1 => 4 + 4 + 8,
        _ => 4 + 4,
    };
    assert!(offset <= size);
    Ok(BoxHeader{
      name: name,
      size: size,
      offset: offset,
    })
}

/// Parse the extra header fields for a full box.
fn read_fullbox_extra<T: ReadBytesExt>(src: &mut T) -> (u8, u32) {
    let version = src.read_u8().unwrap();
    let flags_a = src.read_u8().unwrap();
    let flags_b = src.read_u8().unwrap();
    let flags_c = src.read_u8().unwrap();
    (version, (flags_a as u32) << 16 |
              (flags_b as u32) <<  8 |
              (flags_c as u32))
}

/// Skip over the contents of a box.
pub fn skip_box_content<T: ReadBytesExt + Seek>
  (src: &mut T, header: &BoxHeader)
  -> std::io::Result<u64>
{
    src.seek(SeekFrom::Current((header.size - header.offset) as i64))
}

/// Helper to construct a Take over the contents of a box.
fn limit<'a, T: Read>(f: &'a mut T, h: &BoxHeader) -> Take<&'a mut T> {
    f.take(h.size - h.offset)
}

/// Helper to construct a Cursor over the contents of a box.
fn recurse<T: Read>(f: &mut T, h: &BoxHeader) -> byteorder::Result<()> {
    println!("{} -- recursing", h);
    // FIXME: I couldn't figure out how to do this without copying.
    // We use Seek on the Read we return in skip_box_content, but
    // that trait isn't implemented for a Take like our limit()
    // returns. Slurping the buffer and wrapping it in a Cursor
    // functions as a work around.
    let buf: Vec<u8> = limit(f, &h)
        .bytes()
        .map(|u| u.unwrap())
        .collect();
    let mut content = Cursor::new(buf);
    loop {
        match read_box(&mut content) {
            Ok(_) => {},
            Err(byteorder::Error::UnexpectedEOF) => {
                // byteorder returns EOF at the end of the buffer.
                // This isn't an error for us, just an signal to
                // stop recursion.
                break;
            },
            Err(byteorder::Error::Io(e)) => {
                println!("Error '{:?}' reading box", e.kind());
                return Err(byteorder::Error::Io(e));
            },
        }
    }
    println!("{} -- end", h);
    Ok(())
}

/// Read the contents of a box, including sub boxes.
/// Right now it just prints the box value rather than
/// returning anything.
pub fn read_box<T: Read + Seek>(f: &mut T) -> byteorder::Result<()> {
    read_box_header(f).and_then(|h| {
        match &(fourcc_to_string(h.name))[..] {
            "ftyp" => {
                let mut content = limit(f, &h);
                let ftyp = try!(read_ftyp(&mut content, &h));
                println!("{}", ftyp);
            },
            "moov" => try!(recurse(f, &h)),
            "mvhd" => {
                let mut content = limit(f, &h);
                let mvhd = try!(read_mvhd(&mut content, &h));
                println!("  {}", mvhd);
            },
            "trak" => try!(recurse(f, &h)),
            "tkhd" => {
                let mut content = limit(f, &h);
                let tkhd = try!(read_tkhd(&mut content, &h));
                println!("  {}", tkhd);
            },
            _ => {
                // Skip the contents of unknown chunks.
                println!("{} (skipped)", h);
                try!(skip_box_content(f, &h).and(Ok(())));
            },
        };
        Ok(()) // and_then needs a Result to return.
    })
}

/// Entry point for C language callers.
/// Take a buffer and call read_box() on it.
#[no_mangle]
pub unsafe extern fn read_box_from_buffer(buffer: *const u8, size: usize)
  -> bool {
    use std::slice;
    use std::thread;

    // Validate arguments from C.
    if buffer.is_null() || size < 8 {
        return false;
    }

    // Wrap the buffer we've been give in a slice.
    let b = slice::from_raw_parts(buffer, size);
    let mut c = Cursor::new(b);

    // Parse in a subthread.
    let task = thread::spawn(move || {
        read_box(&mut c).unwrap();
    });
    // Catch any panics.
    task.join().is_ok()
}

/// Parse an ftype box.
pub fn read_ftyp<T: ReadBytesExt>(src: &mut T, head: &BoxHeader)
  -> byteorder::Result<FileTypeBox> {
    let major = try!(src.read_u32::<BigEndian>());
    let minor = try!(src.read_u32::<BigEndian>());
    let brand_count = (head.size - 8 - 8) /4;
    let mut brands = Vec::new();
    for _ in 0..brand_count {
        brands.push( try!(src.read_u32::<BigEndian>()) );
    }
    Ok(FileTypeBox{
        name: head.name,
        size: head.size,
        major_brand: major,
        minor_version: minor,
        compatible_brands: brands,
    })
}

/// Parse an mvhd box.
pub fn read_mvhd<T: ReadBytesExt>(src: &mut T, head: &BoxHeader)
  -> byteorder::Result<MovieHeaderBox> {
    let (version, _) = read_fullbox_extra(src);
    match version {
        1 => {
            // 64 bit creation and modification times.
            let mut skip: Vec<u8> = vec![0; 16];
            let r = try!(src.read(&mut skip));
            assert!(r == skip.len());
        },
        0 => {
            // 32 bit creation and modification times.
            // 64 bit creation and modification times.
            let mut skip: Vec<u8> = vec![0; 8];
            let r = try!(src.read(&mut skip));
            assert!(r == skip.len());
        },
        _ => panic!("invalid mhdr version"),
    }
    let timescale = src.read_u32::<BigEndian>().unwrap();
    let duration = match version {
        1 => try!(src.read_u64::<BigEndian>()),
        0 => try!(src.read_u32::<BigEndian>()) as u64,
        _ => panic!("invalid mhdr version"),
    };
    // Skip remaining fields.
    let mut skip: Vec<u8> = vec![0; 80];
    let r = try!(src.read(&mut skip));
    assert!(r == skip.len());
    Ok(MovieHeaderBox {
        name: head.name,
        size: head.size,
        timescale: timescale,
        duration: duration,
    })
}

/// Parse a tkhd box.
pub fn read_tkhd<T: ReadBytesExt>(src: &mut T, head: &BoxHeader)
  -> byteorder::Result<TrackHeaderBox> {
    use std::io::Error;
    let (version, flags) = read_fullbox_extra(src);
    if flags & 0x1u32 == 0 || flags & 0x2u32 == 0 {
        return Err(byteorder::Error::Io(
            Error::new(std::io::ErrorKind::Other, "Track is disabled")
        ));
    }
    match version {
        1 => {
            // 64 bit creation and modification times.
            let mut skip: Vec<u8> = vec![0; 16];
            let r = try!(src.read(&mut skip));
            assert!(r == skip.len());
        },
        0 => {
            // 32 bit creation and modification times.
            // 64 bit creation and modification times.
            let mut skip: Vec<u8> = vec![0; 8];
            let r = try!(src.read(&mut skip));
            assert!(r == skip.len());
        },
        _ => panic!("invalid tkhd version"),
    }
    let track_id = try!(src.read_u32::<BigEndian>());
    let _reserved = try!(src.read_u32::<BigEndian>());
    assert!(_reserved == 0);
    let duration = match version {
        1 => {
            try!(src.read_u64::<BigEndian>())
        },
        0 => try!(src.read_u32::<BigEndian>()) as u64,
        _ => panic!("invalid tkhd version"),
    };
    let _reserved = try!(src.read_u32::<BigEndian>());
    let _reserved = try!(src.read_u32::<BigEndian>());
    // Skip uninterested fields.
    let mut skip: Vec<u8> = vec![0; 44];
    let r = try!(src.read(&mut skip));
    assert!(r == skip.len());
    let width = try!(src.read_u32::<BigEndian>());
    let height = try!(src.read_u32::<BigEndian>());
    Ok(TrackHeaderBox {
        name: head.name,
        size: head.size,
        track_id: track_id,
        duration: duration,
        width: width,
        height: height,
    })
}

/// Convert the iso box type or other 4-character value to a string.
fn fourcc_to_string(name: u32) -> String {
    let u32_to_vec = |u| {
        vec!((u >> 24 & 0xffu32) as u8,
             (u >> 16 & 0xffu32) as u8,
             (u >>  8 & 0xffu32) as u8,
             (u & 0xffu32) as u8)
    };
    let name_bytes = u32_to_vec(name);
    String::from_utf8_lossy(&name_bytes).into_owned()
}

use std::fmt;
impl fmt::Display for BoxHeader {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "'{}' {} bytes", fourcc_to_string(self.name), self.size)
    }
}

impl fmt::Display for FileTypeBox {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let name = fourcc_to_string(self.name);
        let brand = fourcc_to_string(self.major_brand);
        write!(f, "'{}' {} bytes '{}' v{}", name, self.size,
            brand, self.minor_version)
    }
}

impl fmt::Display for MovieHeaderBox {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let name = fourcc_to_string(self.name);
        write!(f, "'{}' {} bytes duration {}s", name, self.size,
            (self.duration as f64)/(self.timescale as f64))
    }
}

use std::u16;
impl fmt::Display for TrackHeaderBox {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let name = fourcc_to_string(self.name);
        // Dimensions are 16.16 fixed-point.
        let base = u16::MAX as f64 + 1.0;
        let width = (self.width as f64) / base;
        let height = (self.height as f64) / base;
        write!(f, "'{}' {} bytes duration {} id {} {}x{}",
            name, self.size, self.duration, self.track_id,
            width, height)
    }
}

#[test]
fn test_read_box_header() {
    use std::io::Cursor;
    use std::io::Write;
    let mut test: Vec<u8> = vec![0, 0, 0, 8];  // minimal box length
    write!(&mut test, "test").unwrap(); // box type
    let mut stream = Cursor::new(test);
    let parsed = read_box_header(&mut stream).unwrap();
    assert_eq!(parsed.name, 1952805748);
    assert_eq!(parsed.size, 8);
    println!("box {}", parsed);
}

#[test]
fn test_read_box_header_long() {
    use std::io::Cursor;
    let mut test: Vec<u8> = vec![0, 0, 0, 1]; // long box extension code
    test.extend("long".to_string().into_bytes()); // box type
    test.extend(vec![0, 0, 0, 0, 0, 0, 16, 0]); // 64 bit size
    // Skip generating box content.
    let mut stream = Cursor::new(test);
    let parsed = read_box_header(&mut stream).unwrap();
    assert_eq!(parsed.name, 1819242087);
    assert_eq!(parsed.size, 4096);
    println!("box {}", parsed);
}

#[test]
fn test_read_ftyp() {
    use std::io::Cursor;
    use std::io::Write;
    let mut test: Vec<u8> = vec![0, 0, 0, 24]; // size
    write!(&mut test, "ftyp").unwrap(); // type
    write!(&mut test, "mp42").unwrap(); // major brand
    test.extend(vec![0, 0, 0, 0]);      // minor version
    write!(&mut test, "isom").unwrap(); // compatible brands...
    write!(&mut test, "mp42").unwrap();
    assert_eq!(test.len(), 24);

    let mut stream = Cursor::new(test);
    let header = read_box_header(&mut stream).unwrap();
    let parsed = read_ftyp(&mut stream, &header).unwrap();
    assert_eq!(parsed.name, 1718909296);
    assert_eq!(parsed.size, 24);
    assert_eq!(parsed.major_brand, 1836069938);
    assert_eq!(parsed.minor_version, 0);
    assert_eq!(parsed.compatible_brands.len(), 2);
    println!("box {}", parsed);
}
<|endoftext|>


<commit_before>//! DNS resolvers built on [`c-ares`](https://c-ares.haxx.se), for asynchronous DNS requests.
//!
//! This crate provides three resolver types - the `Resolver`, the `FutureResolver`, and the
//! `BlockingResolver`:
//!
//! * The `Resolver` is the thinnest wrapper around the underlying `c-ares` library.  It returns
//! answers via callbacks.  The other resolvers are built on top of this.
//!
//! * The `FutureResolver` returns answers as `std::future::Future`s.
//!
//! * The `BlockingResolver` isn't asynchronous at all - as the name suggests, it blocks until the
//! lookup completes.
//!
//! On all resolvers:
//!
//! -  methods like `query_xxx` correspond to the `c-ares` function `ares_query`, which "initiates
//! a single-question DNS query".
//!
//! -  methods like `search_xxx` correspond to the `c-ares` function `ares_search`, which
//! "initiates a series of single-question DNS queries ... using the channel's search domains as
//! well as a host alias file given by the HOSTALIAS environment variable".
//!
//! See [`c-ares` documentation](https://c-ares.haxx.se/docs.html) for more details.
//!
//! # Example
//!
//! ```rust
//! extern crate c_ares_resolver;
//! extern crate futures_executor;
//! use futures_executor::block_on;
//!
//! fn main() {
//!     let resolver = c_ares_resolver::FutureResolver::new().unwrap();
//!     let query = resolver.query_a("google.com");
//!     let response = block_on(query);
//!     match response {
//!         Ok(result) => println!("{}", result),
//!         Err(e) => println!("Lookup failed with error '{}'", e)
//!     }
//! }
//! ```
//!
//! Further examples showing how to use the library can be found
//! [here](https://github.com/dimbleby/c-ares-resolver/tree/master/examples).
#![deny(missing_docs)]
extern crate c_ares;
extern crate crossbeam_channel;
extern crate futures_channel;
extern crate polling;

mod blockingresolver;
mod error;
mod eventloop;
mod futureresolver;
mod host;
mod nameinfo;
mod resolver;

#[cfg(test)]
mod tests;

pub use crate::blockingresolver::BlockingResolver;
pub use crate::error::Error;
pub use crate::futureresolver::{CAresFuture, FutureResolver};
pub use crate::host::HostResults;
pub use crate::nameinfo::NameInfoResult;
pub use crate::resolver::{Options, Resolver};
<commit_msg>c-ares.org<commit_after>//! DNS resolvers built on [`c-ares`](https://c-ares.org), for asynchronous DNS requests.
//!
//! This crate provides three resolver types - the `Resolver`, the `FutureResolver`, and the
//! `BlockingResolver`:
//!
//! * The `Resolver` is the thinnest wrapper around the underlying `c-ares` library.  It returns
//! answers via callbacks.  The other resolvers are built on top of this.
//!
//! * The `FutureResolver` returns answers as `std::future::Future`s.
//!
//! * The `BlockingResolver` isn't asynchronous at all - as the name suggests, it blocks until the
//! lookup completes.
//!
//! On all resolvers:
//!
//! -  methods like `query_xxx` correspond to the `c-ares` function `ares_query`, which "initiates
//! a single-question DNS query".
//!
//! -  methods like `search_xxx` correspond to the `c-ares` function `ares_search`, which
//! "initiates a series of single-question DNS queries ... using the channel's search domains as
//! well as a host alias file given by the HOSTALIAS environment variable".
//!
//! See [`c-ares` documentation](https://c-ares.org/docs.html) for more details.
//!
//! # Example
//!
//! ```rust
//! extern crate c_ares_resolver;
//! extern crate futures_executor;
//! use futures_executor::block_on;
//!
//! fn main() {
//!     let resolver = c_ares_resolver::FutureResolver::new().unwrap();
//!     let query = resolver.query_a("google.com");
//!     let response = block_on(query);
//!     match response {
//!         Ok(result) => println!("{}", result),
//!         Err(e) => println!("Lookup failed with error '{}'", e)
//!     }
//! }
//! ```
//!
//! Further examples showing how to use the library can be found
//! [here](https://github.com/dimbleby/c-ares-resolver/tree/master/examples).
#![deny(missing_docs)]
extern crate c_ares;
extern crate crossbeam_channel;
extern crate futures_channel;
extern crate polling;

mod blockingresolver;
mod error;
mod eventloop;
mod futureresolver;
mod host;
mod nameinfo;
mod resolver;

#[cfg(test)]
mod tests;

pub use crate::blockingresolver::BlockingResolver;
pub use crate::error::Error;
pub use crate::futureresolver::{CAresFuture, FutureResolver};
pub use crate::host::HostResults;
pub use crate::nameinfo::NameInfoResult;
pub use crate::resolver::{Options, Resolver};
<|endoftext|>


<commit_before>
extern crate websocket;

use std::thread;
use std::sync::{Arc, Mutex};
use std::fs::File;
use std::path::Path;
use std::io::Read;
use std::io::Write;
use std::io::{Error,ErrorKind};
use std::string::*;

use websocket::{Server, Message, Sender, Receiver};
use websocket::header::WebSocketProtocol;
use websocket::message::Type;

extern crate iron;

use iron::prelude::*;
use iron::status;
use iron::mime::Mime;

#[macro_use]
mod tryopt;
mod stringerror;
mod controls;
mod broadcaster;
mod string_defaults;
pub mod control_updates;

extern crate serde_json;
use serde_json::Value;

fn load_string(file_name: &str) -> Result<String, Box<std::error::Error> >
{
  let path = &Path::new(&file_name);
  let mut inf = try!(File::open(path));
  let mut result = String::new();
  try!(inf.read_to_string(&mut result));
  Ok(result)
}

fn write_string(text: &str, file_name: &str) -> Result<(), Box<std::error::Error> >
{
  let path = &Path::new(&file_name);
  let mut inf = try!(File::create(path));
  match inf.write(text.as_bytes()) {
    Ok(_) => Ok(()),
    Err(e) => Err(Box::new(e)),
    }
}

pub struct ControlInfo {
  cm: controls::ControlMap,
  cnm: controls::ControlNameMap,
  guijson: String,
}

pub struct ControlServer {
  ci: Arc<Mutex<ControlInfo>>,
  bc: broadcaster::Broadcaster,
  on_update_received: fn(&control_updates::UpdateMsg) -> (),
}

impl ControlServer {
  fn get_cid_by_name(&self, name: &str) -> Option<Vec<i32> > {
    let guard = match self.ci.lock() {
      Ok(guard) => guard,
      Err(poisoned) => poisoned.into_inner(),
    };

    match guard.cnm.get(name) {
      Some(cid) => Some(cid.clone()),
      _ => None,
    }
  }
  pub fn make_update_msg(&self, name: &str) -> Option<control_updates::UpdateMsg> {
    let guard = match self.ci.lock() {
      Ok(guard) => guard,
      Err(poisoned) => poisoned.into_inner(),
    };

    match guard.cnm.get(name) {
      Some(cid) =>
        match guard.cm.get(cid) {
          Some(ctrl) => ctrl.empty_update(),
          None => None,
        },
      _ => None,
    }
  }
  pub fn update(&self, updmsg: &control_updates::UpdateMsg) {
    let mut ci = match self.ci.lock() {
      Ok(guard) => guard,
      Err(poisoned) => poisoned.into_inner(),
    };

    match ci.cm.get_mut(controls::get_um_id(&updmsg)) {
    Some(ctl) => {
      (*ctl).update(&updmsg);
      let val = controls::encode_update_message(&updmsg);
      match serde_json::ser::to_string(&val) {
        Ok(s) => self.bc.broadcast(Message::text(s)),
        Err(_) => ()
        }
      }
    None => (),
    }
  }
  pub fn load_gui_string(&self, guistring: &str) -> Result<(), Box<std::error::Error> >
  {
    match serde_json::from_str(guistring) {
      Ok(guival) => {
        match controls::deserialize_root(&guival) {
          Ok(controltree) => {
            println!("new control layout recieved!");

            println!("title: {} count: {} ",
              controltree.title, controltree.root_control.control_type());
            println!("controls: {:?}", controltree.root_control);

            // from control tree, make a map of ids->controls.
            // let mapp = controls::make_control_map(&*controltree.root_control);
            // let cnm = controls::control_map_to_name_map(&mapp);
  /*
            sci.cm = mapp;
            sci.guijson = guistring.to_string();
            bc.broadcast(Message::text(guistring.to_string()));

  pub struct ControlInfo {
    cm: controls::ControlMap,
    cnm: controls::ControlNameMap,
    guijson: String,
  }

  */
            let mut guard = match self.ci.lock() {
                Ok(guard) => guard,
                Err(poisoned) => poisoned.into_inner(),
            };

            (*guard).cm = controls::make_control_map(&*controltree.root_control);
            (*guard).cnm = controls::control_map_to_name_map(&(*guard).cm);
            (*guard).guijson = guistring.to_string();

            // send the updated gui string to all clients.
            self.bc.broadcast(Message::text(guistring.to_string()));

            Ok(())
          },
          Err(e) => {
            println!("error reading guiconfig from json: {:?}", e);
            let s = format!("error reading guiconfig json: {:?}", e);
            Err(Box::new(Error::new(ErrorKind::Other, s))) },
        }
      },
      Err(e) => {
        // println!("error reading guiconfig json: {:?}", e);
        let s = format!("error reading guiconfig json: {:?}", e);
        Err(Box::new(Error::new(ErrorKind::Other, s))) },
    }
  }

}

//  iron-server-canceler: ???
//  websocket-thread-canceler: ???
//  fn lookup_control(name) -> index
//  fn modify_control(index, ftn) -> result   <-- will this work with types??  have to cast.
//  load_guistring

//  how about ftns like the ones for faust, for building the gui tree?
//  addbutton
//  addslider
//  addlabel
//  startsizer
//  endsizer

pub fn startserver(guistring: &str,
    on_update_received: fn(&control_updates::UpdateMsg) -> (),
    ip: &str,
    http_port: &str,
    websockets_port: &str,
    htmltemplatefile: Option<&str> ) -> Result<ControlServer, Box<std::error::Error> >
{
    let mut http_ip = String::from(ip);
    http_ip.push_str(":");
    http_ip.push_str(&http_port);

    let mut websockets_ip = String::from(ip);
    websockets_ip.push_str(":");
    websockets_ip.push_str(&websockets_port);

    let htmltemplate = {
      match htmltemplatefile {
        Some(fname) => {
          try!(load_string(fname))
        }
        None => string_defaults::MAIN_HTML.to_string()
      }
    };

    let htmlstring = htmltemplate.replace("<websockets-port>", &websockets_port);

    // println!("{}", htmlstring);

    // let guistring = try!(load_string(&guifilename[..]));
    // let guival: Value = try!(serde_json::from_str(&guistring[..]));
    let guival: Value = try!(serde_json::from_str(guistring));

    let blah = try!(controls::deserialize_root(&guival));

    println!("title: {} rootcontroltype: {} ",
      blah.title, blah.root_control.control_type());
    println!("controls: {:?}", blah.root_control);

    // from control tree, make a map of ids->controls.
    let mapp = controls::make_control_map(&*blah.root_control);
    let cnm = controls::control_map_to_name_map(&mapp);
    // let guijson = guistring.clone();

    let ci = ControlInfo { cm: mapp, cnm: cnm, guijson: String::new() + guistring };

    let cmshare = Arc::new(Mutex::new(ci));
    let wscmshare = cmshare.clone();
    // let oscsocket = try!(UdpSocket::bind(&oscrecvip[..]));
    // for sending, bind to this.  if we bind to localhost, we can't
    // send messages to other machines.
    // let oscsendsocket = try!(UdpSocket::bind("0.0.0.0:0"));
    let bc = broadcaster::Broadcaster::new();
    // let wsos = try!(oscsendsocket.try_clone());
    let wsbc = bc.clone();
    // let wsoscsendip = oscsendip.clone();

    let cs_ret = ControlServer { ci: cmshare,
                                 bc: bc,
                                 on_update_received: on_update_received,
                               };

    /*
    thread::spawn(move || {
      match oscmain(oscsocket, bc, cmshare) {
        Err(e) => println!("oscmain exited with error: {:?}", e),
        Ok(_) => (),
      }
      });
    */

    // Spawn a thread for the websockets handler.
    thread::spawn(move || {
      match websockets_main(websockets_ip, wscmshare, wsbc, on_update_received) {
        Ok(_) => (),
        Err(e) => println!("error in websockets_main: {:?}", e),
      }
    });

    thread::spawn(move || {
      // use this thread for the web server.
      Iron::new(move | _: &mut Request| {
          let content_type = "text/html".parse::<Mime>().unwrap();
          Ok(Response::with((content_type, status::Ok, &*htmlstring)))
      }).http(&http_ip[..]);
      // return when the web server dies, if it ever does.
      });

    Ok(cs_ret)

}

// need to lock the control structs and stuff, refresh them, then send out the
// updates.

// TODO: refactor to return a (rx/sx) pair for sending, recieving messages.
// library users start the websockets_main and get that pair of things.
// then, can send the various control structs and receive the messages.
fn websockets_main( ipaddr: String,
                    ci: Arc<Mutex<ControlInfo>>,
                    broadcaster: broadcaster::Broadcaster,
                    on_update_received: fn(&control_updates::UpdateMsg) -> (),
                    )
                  -> Result<(), Box<std::error::Error> >
{
	let server = try!(Server::bind(&ipaddr[..]));

	for connection in server {

    println!("new websockets connection!");
		// Spawn a new thread for each connection.

    let sci = ci.clone();
//    let osock = try!(oscsocket.try_clone());
 //   let osend = oscsendip.clone();
    let broadcaster = broadcaster.clone();

    let conn = try!(connection);
    thread::spawn(move || {
      match websockets_client(conn,
                            sci,
                            broadcaster,
                            on_update_received) {
        Ok(_) => (),
        Err(e) => {
          println!("error in websockets thread: {:?}", e);
          ()
        },
      }
    });
  }

  Ok(())
}

fn websockets_client(connection: websocket::server::Connection<websocket::stream::WebSocketStream, websocket::stream::WebSocketStream>,
                    ci: Arc<Mutex<ControlInfo>>,
                    mut broadcaster: broadcaster::Broadcaster,
                    on_update_received: fn(&control_updates::UpdateMsg) -> (),
                    ) -> Result<(), Box<std::error::Error> >
{
  // Get the request
  let request = try!(connection.read_request());
  // Keep the headers so we can check them
  let headers = request.headers.clone();

  try!(request.validate()); // Validate the request

  let mut response = request.accept(); // Form a response

  if let Some(&WebSocketProtocol(ref protocols)) = headers.get() {
    if protocols.contains(&("rust-websocket".to_string())) {
      // We have a protocol we want to use
      response.headers.set(WebSocketProtocol(vec!["rust-websocket".to_string()]));
    }
  }

  let mut client = try!(response.send()); // Send the response

  let ip = try!(client.get_mut_sender()
                  .get_mut()
                  .peer_addr());

  println!("Websocket connection from {}", ip);

  // send up the json of the current controls.
  {
    let sci = ci.lock().unwrap();

    let updarray = controls::cm_to_update_array(&sci.cm);

    // build json message containing both guijson and the updarray.
    // let updvals = updarray.into_iter().map(|x|{controls::encode_update_message(&x)}).collect();

    let mut updvals = Vec::new();

    for upd in updarray {
      let um = controls::encode_update_message(&upd);
      updvals.push(um);
    }

    let mut guival: Value = try!(serde_json::from_str(&sci.guijson[..]));

    match guival.as_object_mut() {
      Some(obj) => {
        obj.insert("state".to_string(), Value::Array(updvals));
        ()
      },
      None => (),
    }

    let guistring = try!(serde_json::ser::to_string(&guival));
    let message = Message::text(guistring);
    try!(client.send_message(&message));
  }

  let (sender, mut receiver) = client.split();

  let sendmeh = Arc::new(Mutex::new(sender));

  broadcaster.register(sendmeh.clone());

  for msg in receiver.incoming_messages() {
    let message: Message = try!(msg);
    // println!("message: {:?}", message);

    match message.opcode {
      Type::Close => {
        let message = Message::close();
        // let mut sender = try!(sendmeh.lock());
        let mut sender = sendmeh.lock().unwrap();
        try!(sender.send_message(&message));
        println!("Client {} disconnected", ip);
        return Ok(());
      }
      Type::Ping => {
        println!("Message::Ping(data)");
        let message = Message::pong(message.payload);
        let mut sender = sendmeh.lock().unwrap();
        try!(sender.send_message(&message));
      }
      Type::Text => {
        let u8 = message.payload.to_owned();
        let str = try!(std::str::from_utf8(&*u8));
        let jsonval: Value = try!(serde_json::from_str(str));
        let s_um = controls::decode_update_message(&jsonval);
        match s_um {
          Some(updmsg) => {
            let mut sci  = ci.lock().unwrap();
            let mbcntrl = sci.cm.get_mut(controls::get_um_id(&updmsg));
            match mbcntrl {
              Some(cntrl) => {
                (*cntrl).update(&updmsg);
                broadcaster.broadcast_others(&ip, Message::text(str));

                // TODO: callback ftn??
                /*
                match ctrl_update_to_osc(&updmsg, &**cntrl) {
                  Ok(v) => match oscsocket.send_to(&v, &oscsendip[..]) {
                    Ok(_) => (),
                    Err(e) =>
                      println!("error sending osc message: {:?}", e),
                    },
                  Err(e) =>
                    println!("error building osc message: {:?}", e),
                };
                */

                on_update_received(&updmsg);

                println!("websockets control update recieved: {:?}", updmsg);
                ()
              },
              None => println!("none"),
            }
          },
          _ => println!("decode_update_message failed on websockets msg: {:?}", message),
        }
      },
      _ => {
        println!("unknown websockets msg: {:?}", message);
      }
    }
  }

  Ok(())
}

<commit_msg>progress towards update processor trait<commit_after>
extern crate websocket;

use std::thread;
use std::sync::{Arc, Mutex};
use std::fs::File;
use std::path::Path;
use std::io::Read;
use std::io::Write;
use std::io::{Error,ErrorKind};
use std::string::*;

use websocket::{Server, Message, Sender, Receiver};
use websocket::header::WebSocketProtocol;
use websocket::message::Type;

extern crate iron;

use iron::prelude::*;
use iron::status;
use iron::mime::Mime;

#[macro_use]
mod tryopt;
mod stringerror;
mod controls;
mod broadcaster;
mod string_defaults;
pub mod control_updates;

extern crate serde_json;
use serde_json::Value;

fn load_string(file_name: &str) -> Result<String, Box<std::error::Error> >
{
  let path = &Path::new(&file_name);
  let mut inf = try!(File::open(path));
  let mut result = String::new();
  try!(inf.read_to_string(&mut result));
  Ok(result)
}

fn write_string(text: &str, file_name: &str) -> Result<(), Box<std::error::Error> >
{
  let path = &Path::new(&file_name);
  let mut inf = try!(File::create(path));
  match inf.write(text.as_bytes()) {
    Ok(_) => Ok(()),
    Err(e) => Err(Box::new(e)),
    }
}

pub struct ControlInfo {
  cm: controls::ControlMap,
  cnm: controls::ControlNameMap,
  guijson: String,
}

pub struct ControlServer<'a> {
  ci: Arc<Mutex<ControlInfo>>,
  bc: broadcaster::Broadcaster,
  on_update_received: &'a ControlUpdateProcessor,
}

pub trait ControlUpdateProcessor {
  fn on_update_received(&self, &control_updates::UpdateMsg) -> ();
}

impl<'a> ControlServer<'a> {
  fn get_cid_by_name(&self, name: &str) -> Option<Vec<i32> > {
    let guard = match self.ci.lock() {
      Ok(guard) => guard,
      Err(poisoned) => poisoned.into_inner(),
    };

    match guard.cnm.get(name) {
      Some(cid) => Some(cid.clone()),
      _ => None,
    }
  }

  pub fn get_osc_name(&self, id: &Vec<i32>) -> Option<String>
  {
    let ci = match self.ci.lock() {
      Ok(guard) => guard,
      Err(poisoned) => poisoned.into_inner(),
    };

    match ci.cm.get(id) {
      Some(ctrl) => Some(String::from(ctrl.oscname())),
      _ => None,
    }

  }

  pub fn make_update_msg(&self, name: &str) -> Option<control_updates::UpdateMsg> {
    let guard = match self.ci.lock() {
      Ok(guard) => guard,
      Err(poisoned) => poisoned.into_inner(),
    };

    match guard.cnm.get(name) {
      Some(cid) =>
        match guard.cm.get(cid) {
          Some(ctrl) => ctrl.empty_update(),
          None => None,
        },
      _ => None,
    }
  }
  pub fn update(&self, updmsg: &control_updates::UpdateMsg) {
    let mut ci = match self.ci.lock() {
      Ok(guard) => guard,
      Err(poisoned) => poisoned.into_inner(),
    };

    match ci.cm.get_mut(controls::get_um_id(&updmsg)) {
    Some(ctl) => {
      (*ctl).update(&updmsg);
      let val = controls::encode_update_message(&updmsg);
      match serde_json::ser::to_string(&val) {
        Ok(s) => self.bc.broadcast(Message::text(s)),
        Err(_) => ()
        }
      }
    None => (),
    }
  }
  pub fn load_gui_string(&self, guistring: &str) -> Result<(), Box<std::error::Error> >
  {
    match serde_json::from_str(guistring) {
      Ok(guival) => {
        match controls::deserialize_root(&guival) {
          Ok(controltree) => {
            println!("new control layout recieved!");

            println!("title: {} count: {} ",
              controltree.title, controltree.root_control.control_type());
            println!("controls: {:?}", controltree.root_control);

            // from control tree, make a map of ids->controls.
            // let mapp = controls::make_control_map(&*controltree.root_control);
            // let cnm = controls::control_map_to_name_map(&mapp);
  /*
            sci.cm = mapp;
            sci.guijson = guistring.to_string();
            bc.broadcast(Message::text(guistring.to_string()));

  pub struct ControlInfo {
    cm: controls::ControlMap,
    cnm: controls::ControlNameMap,
    guijson: String,
  }

  */
            let mut guard = match self.ci.lock() {
                Ok(guard) => guard,
                Err(poisoned) => poisoned.into_inner(),
            };

            (*guard).cm = controls::make_control_map(&*controltree.root_control);
            (*guard).cnm = controls::control_map_to_name_map(&(*guard).cm);
            (*guard).guijson = guistring.to_string();

            // send the updated gui string to all clients.
            self.bc.broadcast(Message::text(guistring.to_string()));

            Ok(())
          },
          Err(e) => {
            println!("error reading guiconfig from json: {:?}", e);
            let s = format!("error reading guiconfig json: {:?}", e);
            Err(Box::new(Error::new(ErrorKind::Other, s))) },
        }
      },
      Err(e) => {
        // println!("error reading guiconfig json: {:?}", e);
        let s = format!("error reading guiconfig json: {:?}", e);
        Err(Box::new(Error::new(ErrorKind::Other, s))) },
    }
  }

}

//  iron-server-canceler: ???
//  websocket-thread-canceler: ???
//  fn lookup_control(name) -> index
//  fn modify_control(index, ftn) -> result   <-- will this work with types??  have to cast.
//  load_guistring

//  how about ftns like the ones for faust, for building the gui tree?
//  addbutton
//  addslider
//  addlabel
//  startsizer
//  endsizer

pub fn startserver<'a>(guistring: &str,
    on_update_received: &'a ControlUpdateProcessor,
    ip: &str,
    http_port: &str,
    websockets_port: &str,
    htmltemplatefile: Option<&str> ) -> Result<ControlServer<'a>, Box<std::error::Error> >
{
    let mut http_ip = String::from(ip);
    http_ip.push_str(":");
    http_ip.push_str(&http_port);

    let mut websockets_ip = String::from(ip);
    websockets_ip.push_str(":");
    websockets_ip.push_str(&websockets_port);

    let htmltemplate = {
      match htmltemplatefile {
        Some(fname) => {
          try!(load_string(fname))
        }
        None => string_defaults::MAIN_HTML.to_string()
      }
    };

    let htmlstring = htmltemplate.replace("<websockets-port>", &websockets_port);

    // println!("{}", htmlstring);

    // let guistring = try!(load_string(&guifilename[..]));
    // let guival: Value = try!(serde_json::from_str(&guistring[..]));
    let guival: Value = try!(serde_json::from_str(guistring));

    let blah = try!(controls::deserialize_root(&guival));

    println!("title: {} rootcontroltype: {} ",
      blah.title, blah.root_control.control_type());
    println!("controls: {:?}", blah.root_control);

    // from control tree, make a map of ids->controls.
    let mapp = controls::make_control_map(&*blah.root_control);
    let cnm = controls::control_map_to_name_map(&mapp);
    // let guijson = guistring.clone();

    let ci = ControlInfo { cm: mapp, cnm: cnm, guijson: String::new() + guistring };

    let cmshare = Arc::new(Mutex::new(ci));
    let wscmshare = cmshare.clone();
    // let oscsocket = try!(UdpSocket::bind(&oscrecvip[..]));
    // for sending, bind to this.  if we bind to localhost, we can't
    // send messages to other machines.
    // let oscsendsocket = try!(UdpSocket::bind("0.0.0.0:0"));
    let bc = broadcaster::Broadcaster::new();
    // let wsos = try!(oscsendsocket.try_clone());
    let wsbc = bc.clone();
    // let wsoscsendip = oscsendip.clone();

    let cs_ret = ControlServer { ci: cmshare,
                                 bc: bc,
                                 on_update_received: on_update_received,
                               };

    /*
    thread::spawn(move || {
      match oscmain(oscsocket, bc, cmshare) {
        Err(e) => println!("oscmain exited with error: {:?}", e),
        Ok(_) => (),
      }
      });
    */

    // Spawn a thread for the websockets handler.
    thread::spawn(move || {
      match websockets_main(websockets_ip, wscmshare, wsbc, on_update_received) {
        Ok(_) => (),
        Err(e) => println!("error in websockets_main: {:?}", e),
      }
    });

    thread::spawn(move || {
      // use this thread for the web server.
      Iron::new(move | _: &mut Request| {
          let content_type = "text/html".parse::<Mime>().unwrap();
          Ok(Response::with((content_type, status::Ok, &*htmlstring)))
      }).http(&http_ip[..]);
      // return when the web server dies, if it ever does.
      });

    Ok(cs_ret)

}

// need to lock the control structs and stuff, refresh them, then send out the
// updates.

// TODO: refactor to return a (rx/sx) pair for sending, recieving messages.
// library users start the websockets_main and get that pair of things.
// then, can send the various control structs and receive the messages.
fn websockets_main<'a>( ipaddr: String,
                    ci: Arc<Mutex<ControlInfo>>,
                    broadcaster: broadcaster::Broadcaster,
                    on_update_received: &'a ControlUpdateProcessor,
                    )
                  -> Result<(), Box<std::error::Error> >
{
	let server = try!(Server::bind(&ipaddr[..]));

	for connection in server {

    println!("new websockets connection!");
		// Spawn a new thread for each connection.

    let sci = ci.clone();
//    let osock = try!(oscsocket.try_clone());
 //   let osend = oscsendip.clone();
    let broadcaster = broadcaster.clone();

    let conn = try!(connection);
    thread::spawn(move || {
      match websockets_client(conn,
                            sci,
                            broadcaster,
                            on_update_received) {
        Ok(_) => (),
        Err(e) => {
          println!("error in websockets thread: {:?}", e);
          ()
        },
      }
    });
  }

  Ok(())
}

fn websockets_client(connection: websocket::server::Connection<websocket::stream::WebSocketStream, websocket::stream::WebSocketStream>,
                    ci: Arc<Mutex<ControlInfo>>,
                    mut broadcaster: broadcaster::Broadcaster,
                    on_update_received: fn(&control_updates::UpdateMsg) -> (),
                    ) -> Result<(), Box<std::error::Error> >
{
  // Get the request
  let request = try!(connection.read_request());
  // Keep the headers so we can check them
  let headers = request.headers.clone();

  try!(request.validate()); // Validate the request

  let mut response = request.accept(); // Form a response

  if let Some(&WebSocketProtocol(ref protocols)) = headers.get() {
    if protocols.contains(&("rust-websocket".to_string())) {
      // We have a protocol we want to use
      response.headers.set(WebSocketProtocol(vec!["rust-websocket".to_string()]));
    }
  }

  let mut client = try!(response.send()); // Send the response

  let ip = try!(client.get_mut_sender()
                  .get_mut()
                  .peer_addr());

  println!("Websocket connection from {}", ip);

  // send up the json of the current controls.
  {
    let sci = ci.lock().unwrap();

    let updarray = controls::cm_to_update_array(&sci.cm);

    // build json message containing both guijson and the updarray.
    // let updvals = updarray.into_iter().map(|x|{controls::encode_update_message(&x)}).collect();

    let mut updvals = Vec::new();

    for upd in updarray {
      let um = controls::encode_update_message(&upd);
      updvals.push(um);
    }

    let mut guival: Value = try!(serde_json::from_str(&sci.guijson[..]));

    match guival.as_object_mut() {
      Some(obj) => {
        obj.insert("state".to_string(), Value::Array(updvals));
        ()
      },
      None => (),
    }

    let guistring = try!(serde_json::ser::to_string(&guival));
    let message = Message::text(guistring);
    try!(client.send_message(&message));
  }

  let (sender, mut receiver) = client.split();

  let sendmeh = Arc::new(Mutex::new(sender));

  broadcaster.register(sendmeh.clone());

  for msg in receiver.incoming_messages() {
    let message: Message = try!(msg);
    // println!("message: {:?}", message);

    match message.opcode {
      Type::Close => {
        let message = Message::close();
        // let mut sender = try!(sendmeh.lock());
        let mut sender = sendmeh.lock().unwrap();
        try!(sender.send_message(&message));
        println!("Client {} disconnected", ip);
        return Ok(());
      }
      Type::Ping => {
        println!("Message::Ping(data)");
        let message = Message::pong(message.payload);
        let mut sender = sendmeh.lock().unwrap();
        try!(sender.send_message(&message));
      }
      Type::Text => {
        let u8 = message.payload.to_owned();
        let str = try!(std::str::from_utf8(&*u8));
        let jsonval: Value = try!(serde_json::from_str(str));
        let s_um = controls::decode_update_message(&jsonval);
        match s_um {
          Some(updmsg) => {
            let mut sci  = ci.lock().unwrap();
            let mbcntrl = sci.cm.get_mut(controls::get_um_id(&updmsg));
            match mbcntrl {
              Some(cntrl) => {
                (*cntrl).update(&updmsg);
                broadcaster.broadcast_others(&ip, Message::text(str));

                // TODO: callback ftn??
                /*
                match ctrl_update_to_osc(&updmsg, &**cntrl) {
                  Ok(v) => match oscsocket.send_to(&v, &oscsendip[..]) {
                    Ok(_) => (),
                    Err(e) =>
                      println!("error sending osc message: {:?}", e),
                    },
                  Err(e) =>
                    println!("error building osc message: {:?}", e),
                };
                */

                on_update_received(&updmsg);

                println!("websockets control update recieved: {:?}", updmsg);
                ()
              },
              None => println!("none"),
            }
          },
          _ => println!("decode_update_message failed on websockets msg: {:?}", message),
        }
      },
      _ => {
        println!("unknown websockets msg: {:?}", message);
      }
    }
  }

  Ok(())
}

<|endoftext|>


<commit_before>extern crate fixedbitset;
extern crate rand;

use fixedbitset::FixedBitSet;
use rand::Rng;

pub trait NodeType {
    /// If the node allows incoming connections
    fn accept_incoming_links(&self) -> bool;

    /// If the node allows outgoing connections
    fn accept_outgoing_links(&self) -> bool;
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct NodeIndex(usize);

impl NodeIndex {
    pub fn index(&self) -> usize {
        self.0
    }
}

pub struct Link<W: Clone> {
    pub node_idx: NodeIndex,
    pub weight: W,
}

pub struct Node<NT: NodeType, N, W: Clone> {
    pub node_type: NT,
    pub node_data: N,
    pub input_links: Vec<Link<W>>,
    pub output_links: Vec<Link<W>>,
}

pub struct Network<NT: NodeType, N, W: Clone> {
    nodes: Vec<Node<NT, N, W>>,
}

impl<NT: NodeType, N, W: Clone> Network<NT, N, W> {
    pub fn new() -> Network<NT, N, W> {
        Network { nodes: Vec::new() }
    }

    pub fn nodes(&self) -> &[Node<NT, N, W>] {
        &self.nodes
    }

    pub fn add_node(&mut self,
                    node_type: NT,
                    node_data: N)
                    -> NodeIndex {
        let idx = NodeIndex(self.nodes.len());
        self.nodes.push(Node {
            node_type: node_type,
            node_data: node_data,
            input_links: Vec::new(),
            output_links: Vec::new(),
        });
        return idx;
    }

    /// Returns a random link between two unconnected nodes, which would not introduce
    /// a cycle. Return None is no such exists.
    pub fn find_random_unconnected_link_no_cycle<R: Rng>
        (&self,
         rng: &mut R)
         -> Option<(NodeIndex, NodeIndex)> {

        let n = self.nodes.len();

        let idx = &|i, j| i * n + j;

        let mut adj_matrix = FixedBitSet::with_capacity(n * n);

        // Build up a binary, undirected adjacency matrix of the graph.
        // Every unset bit in the adj_matrix will be a potential link.
        for (i, node) in self.nodes.iter().enumerate() {
            for link in node.output_links.iter() {
                let j = link.node_idx.index();
                adj_matrix.insert(idx(i, j));
                // include the link of reverse direction, because this would
                // create a cycle anyway.
                adj_matrix.insert(idx(j, i));
            }
        }

        let adj_matrix = adj_matrix;

        // We now test all potential links of every node in the graph, if it would
        // introduce a cycle. For that, we shuffle the node indices (`node_order`).
        // in random order.
        let mut node_order: Vec<_> = (0..n).into_iter().collect();
        let mut edge_order: Vec<_> = (0..n).into_iter().collect();
        rng.shuffle(&mut node_order);

        let node_order = node_order;

        for &i in &node_order {
            rng.shuffle(&mut edge_order);
            for &j in &edge_order {
                if i != j && !adj_matrix.contains(idx(i, j)) {
                    // The link (i, j) neither is reflexive, nor exists.
                    let ni = NodeIndex(i);
                    let nj = NodeIndex(j);
                    if self.valid_link(ni, nj).is_ok() && !self.link_would_cycle(ni, nj) {
                        // If the link is valid and does not create a cycle, we are done!
                        return Some((ni, nj));
                    }
                }
            }
        }

        return None;
    }

    /// Returns true if the introduction of this directed link would lead towards a cycle.
    pub fn link_would_cycle(&self,
                            source_node_idx: NodeIndex,
                            target_node_idx: NodeIndex)
                            -> bool {
        if source_node_idx == target_node_idx {
            return true;
        }

        let mut seen_nodes = FixedBitSet::with_capacity(self.nodes.len());
        let mut nodes_to_visit = Vec::new();

        // We start at the target node and iterate all paths from there. If we hit the source node,
        // we found a cycle. Otherwise not.
        let start = target_node_idx.index();

        // We are looking for a path to this node.
        let path_to = source_node_idx.index();

        nodes_to_visit.push(start);
        seen_nodes.insert(start);

        while let Some(visit_node) = nodes_to_visit.pop() {
            for out_link in &self.nodes[visit_node].output_links {
                let next_node = out_link.node_idx.index();
                if !seen_nodes.contains(next_node) {
                    if next_node == path_to {
                        // We found a path to `path_to`. We have found a cycle.
                        return true;
                    }

                    seen_nodes.insert(next_node);
                    nodes_to_visit.push(next_node)
                }
            }
        }

        // We haven't found a cycle.
        return false;
    }

    // Check if the link is valid. Doesn't check for cycles.
    pub fn valid_link(&self,
                      source_node_idx: NodeIndex,
                      target_node_idx: NodeIndex)
                      -> Result<(), &'static str> {
        if source_node_idx == target_node_idx {
            return Err("Loops are not allowed");
        }

        if !self.nodes[source_node_idx.index()].node_type.accept_outgoing_links() {
            return Err("Node does not allow outgoing links");
        }

        if !self.nodes[target_node_idx.index()].node_type.accept_incoming_links() {
           return Err("Node does not allow incoming links");
        }

        Ok(())
    }

    // Note: Doesn't check for cycles (except in the simple reflexive case).
    pub fn add_link(&mut self,
                    source_node_idx: NodeIndex,
                    target_node_idx: NodeIndex,
                    weight: W) {
        if let Err(err) = self.valid_link(source_node_idx, target_node_idx) {
            panic!(err);
        }

        self.nodes[source_node_idx.index()].output_links.push(Link {
            node_idx: target_node_idx,
            weight: weight.clone(),
        });

        self.nodes[target_node_idx.index()].input_links.push(Link {
            node_idx: source_node_idx,
            weight: weight,
        });
    }
}

#[cfg(test)]
mod tests {
    use rand;
    use super::{NodeType, Network};

    enum NodeT {
        Input,
        Hidden,
        Output
    }

    impl NodeType for NodeT {
        fn accept_incoming_links(&self) -> bool {
            match *self {
                NodeT::Input => false,
                _ => true
            }
        }
        fn accept_outgoing_links(&self) -> bool {
            match *self {
                NodeT::Output => false,
                _ => true
            }
        }
    }

    #[test]
    fn test_cycle() {
        let mut g = Network::new();
        let i1 = g.add_node(NodeT::Input, ());
        let h1 = g.add_node(NodeT::Hidden, ());
        let h2 = g.add_node(NodeT::Hidden, ());
        assert_eq!(true, g.valid_link(i1, i1).is_err());
        assert_eq!(true, g.valid_link(h1, h1).is_err());

        assert_eq!(true, g.valid_link(h1, i1).is_err());
        assert_eq!(Ok(()), g.valid_link(i1, h1));
        assert_eq!(Ok(()), g.valid_link(i1, h2));
        assert_eq!(Ok(()), g.valid_link(h1, h2));

        g.add_link(i1, h1, 0.0);
        assert_eq!(true, g.link_would_cycle(h1, i1));
        assert_eq!(false, g.link_would_cycle(i1, h1));
        assert_eq!(false, g.link_would_cycle(i1, h2));
        assert_eq!(true, g.link_would_cycle(i1, i1));
        assert_eq!(false, g.link_would_cycle(h1, h2));
        assert_eq!(false, g.link_would_cycle(h2, h1));
        assert_eq!(false, g.link_would_cycle(h2, i1));

        g.add_link(h1, h2, 0.0);
        assert_eq!(true, g.link_would_cycle(h2, i1));
        assert_eq!(true, g.link_would_cycle(h1, i1));
        assert_eq!(true, g.link_would_cycle(h2, h1));
        assert_eq!(false, g.link_would_cycle(i1, h2));
    }

    #[test]
    fn test_find_random_unconnected_link_no_cycle() {
        let mut g = Network::new();
        let i1 = g.add_node(NodeT::Input, ());
        let o1 = g.add_node(NodeT::Output, ());
        let o2 = g.add_node(NodeT::Output, ());

        let mut rng = rand::thread_rng();

        let link = g.find_random_unconnected_link_no_cycle(&mut rng);
        assert_eq!(true, link.is_some());
        let l = link.unwrap();
        assert!((i1, o1) == l || (i1, o2) == l);

        g.add_link(i1, o2, 0.0);
        let link = g.find_random_unconnected_link_no_cycle(&mut rng);
        assert_eq!(true, link.is_some());
        assert_eq!((i1, o1), link.unwrap());

        g.add_link(i1, o1, 0.0);
        let link = g.find_random_unconnected_link_no_cycle(&mut rng);
        assert_eq!(false, link.is_some());
    }
}
<commit_msg>Add NodeIndex#new()<commit_after>extern crate fixedbitset;
extern crate rand;

use fixedbitset::FixedBitSet;
use rand::Rng;

pub trait NodeType {
    /// If the node allows incoming connections
    fn accept_incoming_links(&self) -> bool;

    /// If the node allows outgoing connections
    fn accept_outgoing_links(&self) -> bool;
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct NodeIndex(usize);

impl NodeIndex {
    pub fn new(idx: usize) -> NodeIndex {
        NodeIndex(idx)
    }

    pub fn index(&self) -> usize {
        self.0
    }
}

pub struct Link<W: Clone> {
    pub node_idx: NodeIndex,
    pub weight: W,
}

pub struct Node<NT: NodeType, N, W: Clone> {
    pub node_type: NT,
    pub node_data: N,
    pub input_links: Vec<Link<W>>,
    pub output_links: Vec<Link<W>>,
}

pub struct Network<NT: NodeType, N, W: Clone> {
    nodes: Vec<Node<NT, N, W>>,
}

impl<NT: NodeType, N, W: Clone> Network<NT, N, W> {
    pub fn new() -> Network<NT, N, W> {
        Network { nodes: Vec::new() }
    }

    pub fn nodes(&self) -> &[Node<NT, N, W>] {
        &self.nodes
    }

    pub fn add_node(&mut self,
                    node_type: NT,
                    node_data: N)
                    -> NodeIndex {
        let idx = NodeIndex(self.nodes.len());
        self.nodes.push(Node {
            node_type: node_type,
            node_data: node_data,
            input_links: Vec::new(),
            output_links: Vec::new(),
        });
        return idx;
    }

    /// Returns a random link between two unconnected nodes, which would not introduce
    /// a cycle. Return None is no such exists.
    pub fn find_random_unconnected_link_no_cycle<R: Rng>
        (&self,
         rng: &mut R)
         -> Option<(NodeIndex, NodeIndex)> {

        let n = self.nodes.len();

        let idx = &|i, j| i * n + j;

        let mut adj_matrix = FixedBitSet::with_capacity(n * n);

        // Build up a binary, undirected adjacency matrix of the graph.
        // Every unset bit in the adj_matrix will be a potential link.
        for (i, node) in self.nodes.iter().enumerate() {
            for link in node.output_links.iter() {
                let j = link.node_idx.index();
                adj_matrix.insert(idx(i, j));
                // include the link of reverse direction, because this would
                // create a cycle anyway.
                adj_matrix.insert(idx(j, i));
            }
        }

        let adj_matrix = adj_matrix;

        // We now test all potential links of every node in the graph, if it would
        // introduce a cycle. For that, we shuffle the node indices (`node_order`).
        // in random order.
        let mut node_order: Vec<_> = (0..n).into_iter().collect();
        let mut edge_order: Vec<_> = (0..n).into_iter().collect();
        rng.shuffle(&mut node_order);

        let node_order = node_order;

        for &i in &node_order {
            rng.shuffle(&mut edge_order);
            for &j in &edge_order {
                if i != j && !adj_matrix.contains(idx(i, j)) {
                    // The link (i, j) neither is reflexive, nor exists.
                    let ni = NodeIndex(i);
                    let nj = NodeIndex(j);
                    if self.valid_link(ni, nj).is_ok() && !self.link_would_cycle(ni, nj) {
                        // If the link is valid and does not create a cycle, we are done!
                        return Some((ni, nj));
                    }
                }
            }
        }

        return None;
    }

    /// Returns true if the introduction of this directed link would lead towards a cycle.
    pub fn link_would_cycle(&self,
                            source_node_idx: NodeIndex,
                            target_node_idx: NodeIndex)
                            -> bool {
        if source_node_idx == target_node_idx {
            return true;
        }

        let mut seen_nodes = FixedBitSet::with_capacity(self.nodes.len());
        let mut nodes_to_visit = Vec::new();

        // We start at the target node and iterate all paths from there. If we hit the source node,
        // we found a cycle. Otherwise not.
        let start = target_node_idx.index();

        // We are looking for a path to this node.
        let path_to = source_node_idx.index();

        nodes_to_visit.push(start);
        seen_nodes.insert(start);

        while let Some(visit_node) = nodes_to_visit.pop() {
            for out_link in &self.nodes[visit_node].output_links {
                let next_node = out_link.node_idx.index();
                if !seen_nodes.contains(next_node) {
                    if next_node == path_to {
                        // We found a path to `path_to`. We have found a cycle.
                        return true;
                    }

                    seen_nodes.insert(next_node);
                    nodes_to_visit.push(next_node)
                }
            }
        }

        // We haven't found a cycle.
        return false;
    }

    // Check if the link is valid. Doesn't check for cycles.
    pub fn valid_link(&self,
                      source_node_idx: NodeIndex,
                      target_node_idx: NodeIndex)
                      -> Result<(), &'static str> {
        if source_node_idx == target_node_idx {
            return Err("Loops are not allowed");
        }

        if !self.nodes[source_node_idx.index()].node_type.accept_outgoing_links() {
            return Err("Node does not allow outgoing links");
        }

        if !self.nodes[target_node_idx.index()].node_type.accept_incoming_links() {
           return Err("Node does not allow incoming links");
        }

        Ok(())
    }

    // Note: Doesn't check for cycles (except in the simple reflexive case).
    pub fn add_link(&mut self,
                    source_node_idx: NodeIndex,
                    target_node_idx: NodeIndex,
                    weight: W) {
        if let Err(err) = self.valid_link(source_node_idx, target_node_idx) {
            panic!(err);
        }

        self.nodes[source_node_idx.index()].output_links.push(Link {
            node_idx: target_node_idx,
            weight: weight.clone(),
        });

        self.nodes[target_node_idx.index()].input_links.push(Link {
            node_idx: source_node_idx,
            weight: weight,
        });
    }
}

#[cfg(test)]
mod tests {
    use rand;
    use super::{NodeType, Network};

    enum NodeT {
        Input,
        Hidden,
        Output
    }

    impl NodeType for NodeT {
        fn accept_incoming_links(&self) -> bool {
            match *self {
                NodeT::Input => false,
                _ => true
            }
        }
        fn accept_outgoing_links(&self) -> bool {
            match *self {
                NodeT::Output => false,
                _ => true
            }
        }
    }

    #[test]
    fn test_cycle() {
        let mut g = Network::new();
        let i1 = g.add_node(NodeT::Input, ());
        let h1 = g.add_node(NodeT::Hidden, ());
        let h2 = g.add_node(NodeT::Hidden, ());
        assert_eq!(true, g.valid_link(i1, i1).is_err());
        assert_eq!(true, g.valid_link(h1, h1).is_err());

        assert_eq!(true, g.valid_link(h1, i1).is_err());
        assert_eq!(Ok(()), g.valid_link(i1, h1));
        assert_eq!(Ok(()), g.valid_link(i1, h2));
        assert_eq!(Ok(()), g.valid_link(h1, h2));

        g.add_link(i1, h1, 0.0);
        assert_eq!(true, g.link_would_cycle(h1, i1));
        assert_eq!(false, g.link_would_cycle(i1, h1));
        assert_eq!(false, g.link_would_cycle(i1, h2));
        assert_eq!(true, g.link_would_cycle(i1, i1));
        assert_eq!(false, g.link_would_cycle(h1, h2));
        assert_eq!(false, g.link_would_cycle(h2, h1));
        assert_eq!(false, g.link_would_cycle(h2, i1));

        g.add_link(h1, h2, 0.0);
        assert_eq!(true, g.link_would_cycle(h2, i1));
        assert_eq!(true, g.link_would_cycle(h1, i1));
        assert_eq!(true, g.link_would_cycle(h2, h1));
        assert_eq!(false, g.link_would_cycle(i1, h2));
    }

    #[test]
    fn test_find_random_unconnected_link_no_cycle() {
        let mut g = Network::new();
        let i1 = g.add_node(NodeT::Input, ());
        let o1 = g.add_node(NodeT::Output, ());
        let o2 = g.add_node(NodeT::Output, ());

        let mut rng = rand::thread_rng();

        let link = g.find_random_unconnected_link_no_cycle(&mut rng);
        assert_eq!(true, link.is_some());
        let l = link.unwrap();
        assert!((i1, o1) == l || (i1, o2) == l);

        g.add_link(i1, o2, 0.0);
        let link = g.find_random_unconnected_link_no_cycle(&mut rng);
        assert_eq!(true, link.is_some());
        assert_eq!((i1, o1), link.unwrap());

        g.add_link(i1, o1, 0.0);
        let link = g.find_random_unconnected_link_no_cycle(&mut rng);
        assert_eq!(false, link.is_some());
    }
}
<|endoftext|>


<commit_before>#[derive(Debug, PartialEq, Eq)]
pub struct GitStatus {
    oid: String,
    branch: String,
    upstream: Option<String>,
    ahead: bool,
    behind: bool,
    staged: bool,
    unstaged: bool,
    untracked: bool,
    ignored: bool,
}

impl GitStatus {
    pub fn new(status_txt: &str) -> Result<GitStatus, &'static str> {
        let mut s = GitStatus {
            oid: String::new(),
            branch: String::new(),
            upstream: None,
            ahead: false,
            behind: false,
            staged: false,
            unstaged: false,
            untracked: false,
            ignored: false,
        };
        for line in status_txt.lines() {
            let mut words = line.split(' ');
            match words.next() {
                Some("#") => {
                    match words.next() {
                        Some("branch.oid") => {
                            s.oid = String::from(words.next().ok_or("Bad input")?);
                        },
                        Some("branch.head") => {
                            s.branch = String::from(words.next().ok_or("Bad input")?);
                        }
                        Some("branch.upstream") => {
                            s.upstream = Some(String::from(words.next().ok_or("Bad input")?));
                        },
                        Some("branch.ab") => {
                            match words.next() {
                                Some("+0") => {},
                                Some(_) => s.ahead = true,
                                None => return Err("Bad input"),
                            }
                            match words.next() {
                                Some("-0") => {},
                                Some(_) => s.behind = true,
                                None => return Err("Bad input"),
                            }
                        },
                        _ => return Err("Bad input"),
                    }
                },
                Some("1") | Some("2") => {
                    if let Some(changes) = words.next() {
                        let mut changes = changes.chars();
                        match changes.next() {
                            Some('.') => {},
                            Some(_) => s.staged = true,
                            None => return Err("Bad input"),
                        }
                        match changes.next() {
                            Some('.') => {},
                            Some(_) => s.unstaged = true,
                            None => return Err("Bad input"),
                        }
                    }
                },
                Some("?") => s.untracked = true,
                Some("!") => s.ignored = true,
                Some("") | None => {},
                _ => return Err("Bad input"),
            }
        }
        Ok(s)
    }

    pub fn to_line(&self) -> String {
        let mut symbols = String::new();
        if self.ahead {
            symbols.push('A');
        }
        if self.behind {
            symbols.push('B');
        }
        if self.staged {
            symbols.push('+');
        }
        if self.unstaged {
            symbols.push('*');
        }
        if self.untracked {
            symbols.push('?');
        }
        if self.ignored {
            symbols.push('!');
        }
        let short_oid = if self.oid == "(initial)" {
            &self.oid
        } else {
            &self.oid[..7]
        };
        if symbols.is_empty() {
            format!("[{} {}]", self.branch, short_oid)
        } else {
            format!("[{} {} {}]", self.branch, short_oid, symbols)
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn parse_clean() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
";
        assert_eq!(
            GitStatus::new(test_status).unwrap(),
            GitStatus {
                oid: String::from("3845e7a3c3aadaaebb2d1b261bf07a9357d35a79"),
                branch: String::from("master"),
                upstream: None,
                ahead: false,
                behind: false,
                staged: false,
                unstaged: false,
                untracked: false,
                ignored: false,
            }
        );
    }

    #[test]
    fn parse_clean_upstream() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
# branch.upstream origin/master
# branch.ab +1 -1
";
        assert_eq!(
            GitStatus::new(test_status).unwrap(),
            GitStatus {
                oid: String::from("3845e7a3c3aadaaebb2d1b261bf07a9357d35a79"),
                branch: String::from("master"),
                upstream: Some(String::from("origin/master")),
                ahead: true,
                behind: true,
                staged: false,
                unstaged: false,
                untracked: false,
                ignored: false,
            }
        );
    }

    #[test]
    fn parse_staged_only() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
1 M. N... 100644 100644 100644 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 311c77295c5b6056f4599c2b8d0a019d4c76746a README.md
";
        let s = GitStatus::new(test_status).unwrap();
        assert!(s.staged);
        assert!(!s.unstaged);
    }

    #[test]
    fn parse_unstaged_only() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
1 .M N... 100644 100644 100644 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 README.md
";
        let s = GitStatus::new(test_status).unwrap();
        assert!(!s.staged);
        assert!(s.unstaged);
    }

    #[test]
    fn parse_both_staged_and_unstaged() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
1 MM N... 100644 100644 100644 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 311c77295c5b6056f4599c2b8d0a019d4c76746a README.md
";
        let s = GitStatus::new(test_status).unwrap();
        assert!(s.staged);
        assert!(s.unstaged);
    }

    #[test]
    fn parse_deleted() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
1 D. N... 100644 000000 000000 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 0000000000000000000000000000000000000000 README.md
";
        let s = GitStatus::new(test_status).unwrap();
        assert!(s.staged);
        assert!(!s.unstaged);
    }

    #[test]
    fn parse_renamed() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
2 R. N... 100644 100644 100644 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 R100 README	README.md
";
        let s = GitStatus::new(test_status).unwrap();
        assert!(s.staged);
        assert!(!s.unstaged);
    }

    #[test]
    fn parse_untracked() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
? foo.txt
";
        assert!(GitStatus::new(test_status).unwrap().untracked);
    }

    #[test]
    fn parse_ignored() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
! node_modules/
";
        assert!(GitStatus::new(test_status).unwrap().ignored);
    }

    #[test]
    fn parse_very_dirty() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
# branch.upstream origin/master
# branch.ab +1 -1
1 D. N... 100644 000000 000000 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 0000000000000000000000000000000000000000 README.md
1 .M N... 100644 100644 100644 5e8a8090976077ddf16252a560460a20dbbdd6a5 5e8a8090976077ddf16252a560460a20dbbdd6a5 gh-pages.sh
? foo.txt
! ignored.txt
";
        assert_eq!(
            GitStatus::new(test_status).unwrap(),
            GitStatus {
                oid: String::from("3845e7a3c3aadaaebb2d1b261bf07a9357d35a79"),
                branch: String::from("master"),
                upstream: Some(String::from("origin/master")),
                ahead: true,
                behind: true,
                staged: true,
                unstaged: true,
                untracked: true,
                ignored: true,
            }
        );
    }

    #[test]
    fn parse_bad_input() {
        assert_eq!(GitStatus::new("foo"), Err("Bad input"));
    }

    #[test]
    fn test_to_line_clean() {
        let s = GitStatus {
            oid: String::from("3845e7a3c3aadaaebb2d1b261bf07a9357d35a79"),
            branch: String::from("master"),
            upstream: None,
            ahead: false,
            behind: false,
            staged: false,
            unstaged: false,
            untracked: false,
            ignored: false,
        };
        assert_eq!(s.to_line(), String::from("[master 3845e7a]"));
    }

    #[test]
    fn test_to_line_dirty() {
        let s = GitStatus {
            oid: String::from("3845e7a3c3aadaaebb2d1b261bf07a9357d35a79"),
            branch: String::from("master"),
            upstream: Some(String::from("origin/master")),
            ahead: true,
            behind: true,
            staged: true,
            unstaged: true,
            untracked: true,
            ignored: true,
        };
        assert_eq!(s.to_line(), String::from("[master 3845e7a AB+*?!]"));
    }

    #[test]
    fn test_to_line_initial_commit() {
        let s = GitStatus {
            oid: String::from("(initial)"),
            branch: String::from("master"),
            upstream: None,
            ahead: false,
            behind: false,
            staged: false,
            unstaged: false,
            untracked: false,
            ignored: false,
        };
        assert_eq!(s.to_line(), String::from("[master (initial)]"));
    }
}
<commit_msg>More use of ok_or<commit_after>#[derive(Debug, PartialEq, Eq)]
pub struct GitStatus {
    oid: String,
    branch: String,
    upstream: Option<String>,
    ahead: bool,
    behind: bool,
    staged: bool,
    unstaged: bool,
    untracked: bool,
    ignored: bool,
}

impl GitStatus {
    pub fn new(status_txt: &str) -> Result<GitStatus, &'static str> {
        let mut s = GitStatus {
            oid: String::new(),
            branch: String::new(),
            upstream: None,
            ahead: false,
            behind: false,
            staged: false,
            unstaged: false,
            untracked: false,
            ignored: false,
        };
        let err = "Bad input";
        for line in status_txt.lines() {
            let mut words = line.split(' ');
            match words.next() {
                Some("#") => {
                    match words.next() {
                        Some("branch.oid") => {
                            s.oid = String::from(words.next().ok_or(err)?);
                        },
                        Some("branch.head") => {
                            s.branch = String::from(words.next().ok_or(err)?);
                        }
                        Some("branch.upstream") => {
                            s.upstream = Some(String::from(words.next().ok_or(err)?));
                        },
                        Some("branch.ab") => {
                            if words.next().ok_or(err)? != "+0" {
                                s.ahead = true;
                            }
                            if words.next().ok_or(err)? != "-0" {
                                s.behind = true;
                            }
                        },
                        _ => return Err(err),
                    }
                },
                Some("1") | Some("2") => {
                    let changes = words.next().ok_or(err)?;
                    let mut changes = changes.chars();
                    if changes.next().ok_or(err)? != '.' {
                        s.staged = true;
                    }
                    if changes.next().ok_or(err)? != '.' {
                        s.unstaged = true;
                    }
                },
                Some("?") => s.untracked = true,
                Some("!") => s.ignored = true,
                Some("") | None => {},
                _ => return Err(err),
            }
        }
        Ok(s)
    }

    pub fn to_line(&self) -> String {
        let mut symbols = String::new();
        if self.ahead {
            symbols.push('A');
        }
        if self.behind {
            symbols.push('B');
        }
        if self.staged {
            symbols.push('+');
        }
        if self.unstaged {
            symbols.push('*');
        }
        if self.untracked {
            symbols.push('?');
        }
        if self.ignored {
            symbols.push('!');
        }
        let short_oid = if self.oid == "(initial)" {
            &self.oid
        } else {
            &self.oid[..7]
        };
        if symbols.is_empty() {
            format!("[{} {}]", self.branch, short_oid)
        } else {
            format!("[{} {} {}]", self.branch, short_oid, symbols)
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn parse_clean() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
";
        assert_eq!(
            GitStatus::new(test_status).unwrap(),
            GitStatus {
                oid: String::from("3845e7a3c3aadaaebb2d1b261bf07a9357d35a79"),
                branch: String::from("master"),
                upstream: None,
                ahead: false,
                behind: false,
                staged: false,
                unstaged: false,
                untracked: false,
                ignored: false,
            }
        );
    }

    #[test]
    fn parse_clean_upstream() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
# branch.upstream origin/master
# branch.ab +1 -1
";
        assert_eq!(
            GitStatus::new(test_status).unwrap(),
            GitStatus {
                oid: String::from("3845e7a3c3aadaaebb2d1b261bf07a9357d35a79"),
                branch: String::from("master"),
                upstream: Some(String::from("origin/master")),
                ahead: true,
                behind: true,
                staged: false,
                unstaged: false,
                untracked: false,
                ignored: false,
            }
        );
    }

    #[test]
    fn parse_staged_only() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
1 M. N... 100644 100644 100644 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 311c77295c5b6056f4599c2b8d0a019d4c76746a README.md
";
        let s = GitStatus::new(test_status).unwrap();
        assert!(s.staged);
        assert!(!s.unstaged);
    }

    #[test]
    fn parse_unstaged_only() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
1 .M N... 100644 100644 100644 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 README.md
";
        let s = GitStatus::new(test_status).unwrap();
        assert!(!s.staged);
        assert!(s.unstaged);
    }

    #[test]
    fn parse_both_staged_and_unstaged() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
1 MM N... 100644 100644 100644 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 311c77295c5b6056f4599c2b8d0a019d4c76746a README.md
";
        let s = GitStatus::new(test_status).unwrap();
        assert!(s.staged);
        assert!(s.unstaged);
    }

    #[test]
    fn parse_deleted() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
1 D. N... 100644 000000 000000 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 0000000000000000000000000000000000000000 README.md
";
        let s = GitStatus::new(test_status).unwrap();
        assert!(s.staged);
        assert!(!s.unstaged);
    }

    #[test]
    fn parse_renamed() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
2 R. N... 100644 100644 100644 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 R100 README	README.md
";
        let s = GitStatus::new(test_status).unwrap();
        assert!(s.staged);
        assert!(!s.unstaged);
    }

    #[test]
    fn parse_untracked() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
? foo.txt
";
        assert!(GitStatus::new(test_status).unwrap().untracked);
    }

    #[test]
    fn parse_ignored() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
! node_modules/
";
        assert!(GitStatus::new(test_status).unwrap().ignored);
    }

    #[test]
    fn parse_very_dirty() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
# branch.upstream origin/master
# branch.ab +1 -1
1 D. N... 100644 000000 000000 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 0000000000000000000000000000000000000000 README.md
1 .M N... 100644 100644 100644 5e8a8090976077ddf16252a560460a20dbbdd6a5 5e8a8090976077ddf16252a560460a20dbbdd6a5 gh-pages.sh
? foo.txt
! ignored.txt
";
        assert_eq!(
            GitStatus::new(test_status).unwrap(),
            GitStatus {
                oid: String::from("3845e7a3c3aadaaebb2d1b261bf07a9357d35a79"),
                branch: String::from("master"),
                upstream: Some(String::from("origin/master")),
                ahead: true,
                behind: true,
                staged: true,
                unstaged: true,
                untracked: true,
                ignored: true,
            }
        );
    }

    #[test]
    fn parse_bad_input() {
        assert_eq!(GitStatus::new("foo"), Err("Bad input"));
    }

    #[test]
    fn test_to_line_clean() {
        let s = GitStatus {
            oid: String::from("3845e7a3c3aadaaebb2d1b261bf07a9357d35a79"),
            branch: String::from("master"),
            upstream: None,
            ahead: false,
            behind: false,
            staged: false,
            unstaged: false,
            untracked: false,
            ignored: false,
        };
        assert_eq!(s.to_line(), String::from("[master 3845e7a]"));
    }

    #[test]
    fn test_to_line_dirty() {
        let s = GitStatus {
            oid: String::from("3845e7a3c3aadaaebb2d1b261bf07a9357d35a79"),
            branch: String::from("master"),
            upstream: Some(String::from("origin/master")),
            ahead: true,
            behind: true,
            staged: true,
            unstaged: true,
            untracked: true,
            ignored: true,
        };
        assert_eq!(s.to_line(), String::from("[master 3845e7a AB+*?!]"));
    }

    #[test]
    fn test_to_line_initial_commit() {
        let s = GitStatus {
            oid: String::from("(initial)"),
            branch: String::from("master"),
            upstream: None,
            ahead: false,
            behind: false,
            staged: false,
            unstaged: false,
            untracked: false,
            ignored: false,
        };
        assert_eq!(s.to_line(), String::from("[master (initial)]"));
    }
}
<|endoftext|>