<commit_before>use {Value, Response};
use parser::parse_response;
use error::RequestError;
use utils::escape_xml;

use hyper::client::{Client, Body};
use hyper::header::{ContentType, UserAgent};

use std::io::{self, Write};

/// A request to call a procedure.
pub struct Request<'a> {
    name: &'a str,
    args: Vec<Value>,
}

impl<'a> Request<'a> {
    /// Creates a new request to call a function named `name`.
    ///
    /// By default, no arguments are passed. Use the `arg` method to append arguments.
    pub fn new(name: &'a str) -> Self {
        Request {
            name: name,
            args: Vec::new(),
        }
    }

    /// Appends an argument to be passed to the current list of arguments.
    pub fn arg<T: Into<Value>>(mut self, value: T) -> Self {
        self.args.push(value.into());

        Request {
            name: self.name,
            args: self.args,
        }
    }

    /// Calls the method using the given `Client`.
    ///
    /// Returns a `RequestResult` indicating whether the request was sent and processed successfully
    /// (according to the rules of XML-RPC).
    pub fn call(&self, client: &Client, url: &str) -> RequestResult {
        use hyper::mime::{Mime, TopLevel, SubLevel, Attr, Value};

        // First, build the body XML
        let mut body = Vec::new();
        try!(self.write_as_xml(&mut body));

        // Send XML-RPC request
        let mut response = try!(client.post(url)
            .header(UserAgent("Rust xmlrpc".to_string()))
            .header(ContentType(Mime(TopLevel::Text, SubLevel::Xml, vec![(Attr::Charset, Value::Utf8)])))
            .body(Body::BufBody(&body, body.len()))
            .send());

        // FIXME Check that the response headers are correct

        // Read the response and parse it
        // FIXME `BufRead`?
        Ok(try!(parse_response(&mut response)))
    }

    /// Formats this `Request` as XML.
    pub fn write_as_xml<W: Write>(&self, fmt: &mut W) -> io::Result<()> {
        try!(write!(fmt, r#"<?xml version="1.1" encoding="utf-8"?>"#));
        try!(write!(fmt, r#"<methodCall>"#));
        try!(write!(fmt, r#"    <methodName>{}</methodName>"#, escape_xml(&self.name)));
        try!(write!(fmt, r#"    <params>"#));
        for value in &self.args {
            try!(write!(fmt, r#"        <param>"#));
            try!(value.format(fmt));
            try!(write!(fmt, r#"        </param>"#));
        }
        try!(write!(fmt, r#"    </params>"#));
        try!(write!(fmt, r#"</methodCall>"#));
        Ok(())
    }
}

/// The result of executing a request.
///
/// When the request was executed without major errors (like an HTTP error or a malformed response),
/// this is `Ok`. The `Response` can still denote a `Fault` if the server returned a `<fault>`
/// response.
pub type RequestResult = Result<Response, RequestError>;

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

    #[test]
    fn escapes_method_names() {
        let mut output: Vec<u8> = Vec::new();
        let req = Request::new("x<&x");

        req.write_as_xml(&mut output).unwrap();
        assert!(
            str::from_utf8(&output)
            .unwrap()
            .contains("<methodName>x<&x</methodName>"));
    }
}
<commit_msg>Use XML version v1.0 in requests.<commit_after>use {Value, Response};
use parser::parse_response;
use error::RequestError;
use utils::escape_xml;

use hyper::client::{Client, Body};
use hyper::header::{ContentType, UserAgent};

use std::io::{self, Write};

/// A request to call a procedure.
pub struct Request<'a> {
    name: &'a str,
    args: Vec<Value>,
}

impl<'a> Request<'a> {
    /// Creates a new request to call a function named `name`.
    ///
    /// By default, no arguments are passed. Use the `arg` method to append arguments.
    pub fn new(name: &'a str) -> Self {
        Request {
            name: name,
            args: Vec::new(),
        }
    }

    /// Appends an argument to be passed to the current list of arguments.
    pub fn arg<T: Into<Value>>(mut self, value: T) -> Self {
        self.args.push(value.into());

        Request {
            name: self.name,
            args: self.args,
        }
    }

    /// Calls the method using the given `Client`.
    ///
    /// Returns a `RequestResult` indicating whether the request was sent and processed successfully
    /// (according to the rules of XML-RPC).
    pub fn call(&self, client: &Client, url: &str) -> RequestResult {
        use hyper::mime::{Mime, TopLevel, SubLevel, Attr, Value};

        // First, build the body XML
        let mut body = Vec::new();
        try!(self.write_as_xml(&mut body));

        // Send XML-RPC request
        let mut response = try!(client.post(url)
            .header(UserAgent("Rust xmlrpc".to_string()))
            .header(ContentType(Mime(TopLevel::Text, SubLevel::Xml, vec![(Attr::Charset, Value::Utf8)])))
            .body(Body::BufBody(&body, body.len()))
            .send());

        // FIXME Check that the response headers are correct

        // Read the response and parse it
        // FIXME `BufRead`?
        Ok(try!(parse_response(&mut response)))
    }

    /// Formats this `Request` as XML.
    pub fn write_as_xml<W: Write>(&self, fmt: &mut W) -> io::Result<()> {
        try!(write!(fmt, r#"<?xml version="1.0" encoding="utf-8"?>"#));
        try!(write!(fmt, r#"<methodCall>"#));
        try!(write!(fmt, r#"    <methodName>{}</methodName>"#, escape_xml(&self.name)));
        try!(write!(fmt, r#"    <params>"#));
        for value in &self.args {
            try!(write!(fmt, r#"        <param>"#));
            try!(value.format(fmt));
            try!(write!(fmt, r#"        </param>"#));
        }
        try!(write!(fmt, r#"    </params>"#));
        try!(write!(fmt, r#"</methodCall>"#));
        Ok(())
    }
}

/// The result of executing a request.
///
/// When the request was executed without major errors (like an HTTP error or a malformed response),
/// this is `Ok`. The `Response` can still denote a `Fault` if the server returned a `<fault>`
/// response.
pub type RequestResult = Result<Response, RequestError>;

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

    #[test]
    fn escapes_method_names() {
        let mut output: Vec<u8> = Vec::new();
        let req = Request::new("x<&x");

        req.write_as_xml(&mut output).unwrap();
        assert!(
            str::from_utf8(&output)
            .unwrap()
            .contains("<methodName>x<&x</methodName>"));
    }
}
<|endoftext|>


<commit_before>// see http://wiki.nesdev.com/w/index.php/INES for more information
use std::fs::File;
use std::io::Read;
use memory::Memory;

pub fn read_rom(file_path: &str) -> Rom {

    let mut rom = Rom::new();

    let mut rom_file = File::open(file_path).unwrap_or_else(|e| {
        panic!("Could not open the rom file {}: {}", file_path, e);
        });

    rom.read_header(&mut rom_file);
    rom.read_trainer_field(&mut rom_file);
    rom.read_prg_rom(&mut rom_file);
    rom.read_chr_rom(&mut rom_file);
    // mappers are currently unimplemented; mapper 0 is hardcoded
    // thus, panic if other mappers are used
    if rom.header.mapper != 0 {
        panic!("Currently only mapper 0 is implemented (rom uses mapper {})", rom.header.mapper);
    }
    rom
}

fn read_bytes_from_file_or_panic(length:u64, file: &mut File, err_msg: &str) -> Vec<u8>{
    let mut buf = vec![];
    let read_bytes =
        file
            .take(length)
            .read_to_end(&mut buf)
            .unwrap_or_else(
                |e| {
                    panic!("{}: {}", err_msg, e);
                });

    if read_bytes != buf.len() {
        panic!("{}: {} bytes read but {} was expected", err_msg, read_bytes, length);
    }
    buf
}

#[derive(Debug)]
pub struct Rom {
    pub header: RomHeader,
    trainer: Vec<u8>, // length is 0 if no trainer is present
    prg_rom_data: Vec<u8>,
    chr_rom_data: Vec<u8>,
}

// this is an inefficient implementation, requiring address calculations on each read\write
fn get_offset_temp_hardcore_impl(length: u8, address: u16) -> usize {

    // program rom is mapped to memory addresses 0x8000 - 0xBFFF and 0xC000 - 0xFFFF
    // if rom size is 16kb, 0x8000 - 0xBFFF and 0xC000 - 0xFFFF are mirrored
    // otherwise first 16kb of rom is mapped to 0x8000 -> and second 16kb is mapped to C0000 ->

    let base = address & 0x3FFF; // mask first two bits away to get offset
    if length > 1 && (address & 0xC000 == 0xC000) {
        (base + 0x4000) as usize // if rom size is not 16kb and address is 0xC000 ->, map to second 16kb
    } else {
        base as usize
    }
}

impl Memory for Rom {

    fn read(&mut self, address: u16) -> u8 {
        // TODO: Implement mappers & let them handle this
        // for now, mapper 0 is hardcoded (poorly)

        let offset = get_offset_temp_hardcore_impl(self.header.prg_rom_size, address);
        self.prg_rom_data[offset]
    }

    fn write(&mut self, address: u16, value: u8) {
        // TODO: Implement mappers & let them handle this (memory mapped io for mapper registers)
        // for now, panic on write attempts
        panic!("Writes are unimplemented for roms");
    }
}

impl Rom {
    pub fn new() -> Rom {
        Rom {
            header: RomHeader::new(),
            trainer: vec![],
            prg_rom_data: vec![],
            chr_rom_data: vec![]
        }
    }

    fn read_header(&mut self, rom_file: &mut File) {
        RomHeader::verify_magic_number_or_panic(rom_file);
        self.header.read_prg_rom_size(rom_file);
        self.header.read_chr_rom_size(rom_file);
        self.header.read_flags_6(rom_file);
        self.header.read_flags_7(rom_file);
        self.header.read_prg_ram_size(rom_file);
        self.header.read_flags_9(rom_file);
        RomHeader::read_padding(rom_file);
    }

    fn read_trainer_field(&mut self, rom_file: &mut File) {
        // check if the trainer bit is set - if not, there is no trainer and do nothing
        if self.header.has_trainer {

            self.trainer = read_bytes_from_file_or_panic(512, rom_file,
                "Could not read the trainer field from the rom");
        }
    }

    fn read_prg_rom(&mut self, rom_file: &mut File) {
        let prg_rom_unit_size = 16384;
        let size = prg_rom_unit_size * self.header.prg_rom_size as u64;
        self.prg_rom_data = read_bytes_from_file_or_panic(size, rom_file,
            "Could not read prg rom data from rom");
    }

    fn read_chr_rom(&mut self, rom_file: &mut File) {
        let chr_rom_unit_size = 8192;
        let size = chr_rom_unit_size * self.header.chr_rom_size as u64;
        self.chr_rom_data = read_bytes_from_file_or_panic(size, rom_file,
            "Could not read chr rom data from rom");
    }
}

#[derive(Debug, Clone)]
pub enum TvSystem {
    Uninitialized,
    PAL,
    NTSC
}

#[derive(Debug, Clone)]
pub enum Mirroring {
    Uninitialized,
    HorizontalMirroring,
    VerticalMirroring,
    FourScreenVRAM
}

#[derive(Debug)]
pub struct RomHeader {
    pub prg_rom_size:u8, // size in 16kb units
    pub chr_rom_size:u8, // size in 8kb units - if 0, chr ram is used
    pub prg_ram_size:u8, // size in 8kb units - if 0, 8kb of ram is assumed
    pub mapper: u8,
    pub mirroring: Mirroring,
    pub tv_system: TvSystem,
    has_trainer: bool,
    has_battery_backing: bool,
}

impl RomHeader {
    fn new() -> RomHeader {
        RomHeader {
            prg_rom_size: 0,
            chr_rom_size:0,
            prg_ram_size:0,
            mapper: 0,
            mirroring: Mirroring::Uninitialized,
            tv_system: TvSystem::Uninitialized,
            has_trainer: false,
            has_battery_backing: false,
        }
    }

    fn verify_magic_number_or_panic(rom_file: &mut File) {
        let buf = read_bytes_from_file_or_panic(4, rom_file,
            "Could not read the magic number from the header");

        if !(buf[0] == 0x4E && buf[1] == 0x45 && buf[2] == 0x53 && buf[3] == 0x1A) {
            panic!("Invalid magic number");
        }
    }

    fn read_prg_rom_size(&mut self, rom_file: &mut File) {
        let buf = read_bytes_from_file_or_panic(1, rom_file,
            "Could not read the prg rom size from the header");
        self.prg_rom_size = buf[0];
    }

    fn read_chr_rom_size(&mut self, rom_file: &mut File) {
        let buf = read_bytes_from_file_or_panic(1, rom_file,
            "Could not read the chr rom size from the header");
        self.chr_rom_size = buf[0];
    }

    /*
    Documentation on flags 6:

    76543210
    ||||||||
    ||||+||+- 0xx0: vertical arrangement/horizontal mirroring (CIRAM A10 = PPU A11)
    |||| ||   0xx1: horizontal arrangement/vertical mirroring (CIRAM A10 = PPU A10)
    |||| ||   1xxx: four-screen VRAM
    |||| |+-- 1: Cartridge contains battery-backed PRG RAM ($6000-7FFF) or other persistent memory
    |||| +--- 1: 512-byte trainer at $7000-$71FF (stored before PRG data)
    ++++----- Lower nybble of mapper number
*/
    fn read_flags_6(&mut self, rom_file: &mut File) {
        let buf = read_bytes_from_file_or_panic(1, rom_file,
            "Could not read the flags_6 field from header");

        // if bit 2 is set, trainer is present
        self.has_trainer = (buf[0] & (1 << 2)) != 0;

        // if bit 1 is set, battery backed memory (or other persistent memory) is present
        self.has_battery_backing = (buf[0] & (1 << 1)) != 0;

        // if bit 3 is set, bit 0 is ignored
        if (buf[0] & (1 << 3)) != 0 {
            self.mirroring = Mirroring::FourScreenVRAM;
        } else { // otherwise, read bit 0
            if (buf[0] & 1) == 0 {
                self.mirroring = Mirroring::HorizontalMirroring;
            } else {
                self.mirroring = Mirroring::VerticalMirroring;
            }
        }

        // set lower 4 bits of mapper number
        let lower_nybble =  buf[0] >> 4;
        self.mapper = self.mapper & 0xf0; // set lower 4 bits to 0, in case they were not
        self.mapper = self.mapper | lower_nybble;
    }

    /*
    Documentation on flags 7:

    76543210
    ||||||||
    |||||||+- VS Unisystem
    ||||||+-- PlayChoice-10 (8KB of Hint Screen data stored after CHR data)
    ||||++--- If equal to 2, flags 8-15 are in NES 2.0 format
    ++++----- Upper nybble of mapper number

    */
    fn read_flags_7(&mut self, rom_file: &mut File) {
        let buf = read_bytes_from_file_or_panic(1, rom_file,
            "Could not read the flags_7 field from header");

        // check if nes 2.0 format; if so, panic as this is currently not supported
        if (buf[0] & 0x0C) >> 2 == 0x02 {
            panic!("Rom is in nes 2.0 format which is currently unsupported");
        }
        // extract the upper nybble of the mapper number
        let upper_nybble = 0xf0 & buf[0];
        // set upper nybble to zero, in case it wasn't
        self.mapper = self.mapper & 0x0f;
        self.mapper = self.mapper | upper_nybble;

        // unisystem - playchoice are currently ignored
    }

    fn read_prg_ram_size(&mut self, rom_file: &mut File) {
        let buf = read_bytes_from_file_or_panic(1, rom_file,
            "Could not read the prg ram size from header");

        self.prg_ram_size = buf[0];
        // to quoth the documentation:
        // "Size of PRG RAM in 8 KB units (Value 0 infers 8 KB for compatibility; see PRG RAM circuit)"
        if self.prg_ram_size == 0 {
            self.prg_ram_size = 1;
        }
    }

    /*
        Documentation on flags 9:

        76543210
        ||||||||
        |||||||+- TV system (0: NTSC; 1: PAL)
        +++++++-- Reserved, set to zero

    */
    fn read_flags_9(&mut self, rom_file: &mut File) {
        let buf = read_bytes_from_file_or_panic(1, rom_file,
            "Could not read the flags_9 field from header");

        // Bits 1 - 7 should be zero. Thus, if the value is greater than 1, one or more of these
        // bits are set and something is wrong (possibly unsupported ROM version)
        if buf[0] > 1 {
            panic!("flags_9 field has invalid value {}: Other bits than the first one are set",
            buf[0])
        }

        if buf[0] == 0 {
            self.tv_system = TvSystem::NTSC;
        } else {
            self.tv_system = TvSystem::PAL;
        }
    }

    fn read_padding(rom_file: &mut File) {
        let buf = read_bytes_from_file_or_panic(6, rom_file,
            "Could not read the padding from the header");

        if !(buf[0] == 0 && buf[1] == 0 && buf[2] == 0 && buf[3] == 0 && buf[4] == 0 && buf[5] == 0) {
            panic!("Invalid padding: Padding is expected to be zero initialized");
        }
    }
}
<commit_msg>Fixed a typo and implemented chr rom reads<commit_after>// see http://wiki.nesdev.com/w/index.php/INES for more information
use std::fs::File;
use std::io::Read;
use memory::Memory;

pub fn read_rom(file_path: &str) -> Rom {

    let mut rom = Rom::new();

    let mut rom_file = File::open(file_path).unwrap_or_else(|e| {
        panic!("Could not open the rom file {}: {}", file_path, e);
        });

    rom.read_header(&mut rom_file);
    rom.read_trainer_field(&mut rom_file);
    rom.read_prg_rom(&mut rom_file);
    rom.read_chr_rom(&mut rom_file);
    // mappers are currently unimplemented; mapper 0 is hardcoded
    // thus, panic if other mappers are used
    if rom.header.mapper != 0 {
        panic!("Currently only mapper 0 is implemented (rom uses mapper {})", rom.header.mapper);
    }
    rom
}

fn read_bytes_from_file_or_panic(length:u64, file: &mut File, err_msg: &str) -> Vec<u8>{
    let mut buf = vec![];
    let read_bytes =
        file
            .take(length)
            .read_to_end(&mut buf)
            .unwrap_or_else(
                |e| {
                    panic!("{}: {}", err_msg, e);
                });

    if read_bytes != buf.len() {
        panic!("{}: {} bytes read but {} was expected", err_msg, read_bytes, length);
    }
    buf
}

#[derive(Debug)]
pub struct Rom {
    pub header: RomHeader,
    trainer: Vec<u8>, // length is 0 if no trainer is present
    prg_rom_data: Vec<u8>,
    chr_rom_data: Vec<u8>,
}

// this is an inefficient implementation, requiring address calculations on each read\write
fn get_offset_temp_hardcoded_impl(length: u8, address: u16) -> usize {

    // program rom is mapped to memory addresses 0x8000 - 0xBFFF and 0xC000 - 0xFFFF
    // if rom size is 16kb, 0x8000 - 0xBFFF and 0xC000 - 0xFFFF are mirrored
    // otherwise first 16kb of rom is mapped to 0x8000 -> and second 16kb is mapped to C0000 ->

    let base = address & 0x3FFF; // mask first two bits away to get offset
    if length > 1 && (address & 0xC000 == 0xC000) {
        (base + 0x4000) as usize // if rom size is not 16kb and address is 0xC000 ->, map to second 16kb
    } else {
        base as usize
    }
}

impl Memory for Rom {

    fn read(&mut self, address: u16) -> u8 {
        // TODO: Implement mappers & let them handle this
        // for now, mapper 0 is hardcoded (poorly)

        // basically the hardcoded assumption right now is that any read form 0x2000 or below is to chr rom,
        // otherwise it's to prg rom
        if address < 0x2000 {
            self.chr_rom_data[address as usize]
        } else {
            let offset = get_offset_temp_hardcoded_impl(self.header.prg_rom_size, address);
            self.prg_rom_data[offset]
        }
    }

    fn write(&mut self, address: u16, value: u8) {
        // TODO: Implement mappers & let them handle this (memory mapped io for mapper registers)
        // for now, panic on write attempts
        panic!("Writes are unimplemented for roms (address 0x{:04X}, value: 0x{:04X})", address, value);
    }
}

impl Rom {
    pub fn new() -> Rom {
        Rom {
            header: RomHeader::new(),
            trainer: vec![],
            prg_rom_data: vec![],
            chr_rom_data: vec![]
        }
    }

    fn read_header(&mut self, rom_file: &mut File) {
        RomHeader::verify_magic_number_or_panic(rom_file);
        self.header.read_prg_rom_size(rom_file);
        self.header.read_chr_rom_size(rom_file);
        self.header.read_flags_6(rom_file);
        self.header.read_flags_7(rom_file);
        self.header.read_prg_ram_size(rom_file);
        self.header.read_flags_9(rom_file);
        RomHeader::read_padding(rom_file);
    }

    fn read_trainer_field(&mut self, rom_file: &mut File) {
        // check if the trainer bit is set - if not, there is no trainer and do nothing
        if self.header.has_trainer {

            self.trainer = read_bytes_from_file_or_panic(512, rom_file,
                "Could not read the trainer field from the rom");
        }
    }

    fn read_prg_rom(&mut self, rom_file: &mut File) {
        let prg_rom_unit_size = 16384;
        let size = prg_rom_unit_size * self.header.prg_rom_size as u64;
        self.prg_rom_data = read_bytes_from_file_or_panic(size, rom_file,
            "Could not read prg rom data from rom");
    }

    fn read_chr_rom(&mut self, rom_file: &mut File) {
        let chr_rom_unit_size = 8192;
        let size = chr_rom_unit_size * self.header.chr_rom_size as u64;
        self.chr_rom_data = read_bytes_from_file_or_panic(size, rom_file,
            "Could not read chr rom data from rom");
    }
}

#[derive(Debug, Clone)]
pub enum TvSystem {
    Uninitialized,
    PAL,
    NTSC
}

#[derive(Debug, Clone)]
pub enum Mirroring {
    Uninitialized,
    HorizontalMirroring,
    VerticalMirroring,
    FourScreenVRAM
}

#[derive(Debug)]
pub struct RomHeader {
    pub prg_rom_size:u8, // size in 16kb units
    pub chr_rom_size:u8, // size in 8kb units - if 0, chr ram is used
    pub prg_ram_size:u8, // size in 8kb units - if 0, 8kb of ram is assumed
    pub mapper: u8,
    pub mirroring: Mirroring,
    pub tv_system: TvSystem,
    has_trainer: bool,
    has_battery_backing: bool,
}

impl RomHeader {
    fn new() -> RomHeader {
        RomHeader {
            prg_rom_size: 0,
            chr_rom_size:0,
            prg_ram_size:0,
            mapper: 0,
            mirroring: Mirroring::Uninitialized,
            tv_system: TvSystem::Uninitialized,
            has_trainer: false,
            has_battery_backing: false,
        }
    }

    fn verify_magic_number_or_panic(rom_file: &mut File) {
        let buf = read_bytes_from_file_or_panic(4, rom_file,
            "Could not read the magic number from the header");

        if !(buf[0] == 0x4E && buf[1] == 0x45 && buf[2] == 0x53 && buf[3] == 0x1A) {
            panic!("Invalid magic number");
        }
    }

    fn read_prg_rom_size(&mut self, rom_file: &mut File) {
        let buf = read_bytes_from_file_or_panic(1, rom_file,
            "Could not read the prg rom size from the header");
        self.prg_rom_size = buf[0];
    }

    fn read_chr_rom_size(&mut self, rom_file: &mut File) {
        let buf = read_bytes_from_file_or_panic(1, rom_file,
            "Could not read the chr rom size from the header");
        self.chr_rom_size = buf[0];
    }

    /*
    Documentation on flags 6:

    76543210
    ||||||||
    ||||+||+- 0xx0: vertical arrangement/horizontal mirroring (CIRAM A10 = PPU A11)
    |||| ||   0xx1: horizontal arrangement/vertical mirroring (CIRAM A10 = PPU A10)
    |||| ||   1xxx: four-screen VRAM
    |||| |+-- 1: Cartridge contains battery-backed PRG RAM ($6000-7FFF) or other persistent memory
    |||| +--- 1: 512-byte trainer at $7000-$71FF (stored before PRG data)
    ++++----- Lower nybble of mapper number
*/
    fn read_flags_6(&mut self, rom_file: &mut File) {
        let buf = read_bytes_from_file_or_panic(1, rom_file,
            "Could not read the flags_6 field from header");

        // if bit 2 is set, trainer is present
        self.has_trainer = (buf[0] & (1 << 2)) != 0;

        // if bit 1 is set, battery backed memory (or other persistent memory) is present
        self.has_battery_backing = (buf[0] & (1 << 1)) != 0;

        // if bit 3 is set, bit 0 is ignored
        if (buf[0] & (1 << 3)) != 0 {
            self.mirroring = Mirroring::FourScreenVRAM;
        } else { // otherwise, read bit 0
            if (buf[0] & 1) == 0 {
                self.mirroring = Mirroring::HorizontalMirroring;
            } else {
                self.mirroring = Mirroring::VerticalMirroring;
            }
        }

        // set lower 4 bits of mapper number
        let lower_nybble =  buf[0] >> 4;
        self.mapper = self.mapper & 0xf0; // set lower 4 bits to 0, in case they were not
        self.mapper = self.mapper | lower_nybble;
    }

    /*
    Documentation on flags 7:

    76543210
    ||||||||
    |||||||+- VS Unisystem
    ||||||+-- PlayChoice-10 (8KB of Hint Screen data stored after CHR data)
    ||||++--- If equal to 2, flags 8-15 are in NES 2.0 format
    ++++----- Upper nybble of mapper number

    */
    fn read_flags_7(&mut self, rom_file: &mut File) {
        let buf = read_bytes_from_file_or_panic(1, rom_file,
            "Could not read the flags_7 field from header");

        // check if nes 2.0 format; if so, panic as this is currently not supported
        if (buf[0] & 0x0C) >> 2 == 0x02 {
            panic!("Rom is in nes 2.0 format which is currently unsupported");
        }
        // extract the upper nybble of the mapper number
        let upper_nybble = 0xf0 & buf[0];
        // set upper nybble to zero, in case it wasn't
        self.mapper = self.mapper & 0x0f;
        self.mapper = self.mapper | upper_nybble;

        // unisystem - playchoice are currently ignored
    }

    fn read_prg_ram_size(&mut self, rom_file: &mut File) {
        let buf = read_bytes_from_file_or_panic(1, rom_file,
            "Could not read the prg ram size from header");

        self.prg_ram_size = buf[0];
        // to quoth the documentation:
        // "Size of PRG RAM in 8 KB units (Value 0 infers 8 KB for compatibility; see PRG RAM circuit)"
        if self.prg_ram_size == 0 {
            self.prg_ram_size = 1;
        }
    }

    /*
        Documentation on flags 9:

        76543210
        ||||||||
        |||||||+- TV system (0: NTSC; 1: PAL)
        +++++++-- Reserved, set to zero

    */
    fn read_flags_9(&mut self, rom_file: &mut File) {
        let buf = read_bytes_from_file_or_panic(1, rom_file,
            "Could not read the flags_9 field from header");

        // Bits 1 - 7 should be zero. Thus, if the value is greater than 1, one or more of these
        // bits are set and something is wrong (possibly unsupported ROM version)
        if buf[0] > 1 {
            panic!("flags_9 field has invalid value {}: Other bits than the first one are set",
            buf[0])
        }

        if buf[0] == 0 {
            self.tv_system = TvSystem::NTSC;
        } else {
            self.tv_system = TvSystem::PAL;
        }
    }

    fn read_padding(rom_file: &mut File) {
        let buf = read_bytes_from_file_or_panic(6, rom_file,
            "Could not read the padding from the header");

        if !(buf[0] == 0 && buf[1] == 0 && buf[2] == 0 && buf[3] == 0 && buf[4] == 0 && buf[5] == 0) {
            panic!("Invalid padding: Padding is expected to be zero initialized");
        }
    }
}
<|endoftext|>


<commit_before>//! Time (base unit second, s1).

quantity! {
    /// Time (base unit second, s1).
    quantity: Time; "time";
    /// Time dimension, s1.
    dimension: ISQ<
        Z0,     // length
        Z0,     // mass
        P1,     // time
        Z0,     // electric current
        Z0,     // thermodynamic temperature
        Z0,     // amount of substance
        Z0>;    // luminous intensity
    units {
        @yottasecond: prefix!(yotta); "Ys", "yottasecond", "yottaseconds";
        @zettasecond: prefix!(zetta); "Zs", "zettasecond", "zettaseconds";
        @exasecond: prefix!(exa); "Es", "exasecond", "exaseconds";
        @petasecond: prefix!(peta); "Ps", "petasecond", "petaseconds";
        @terasecond: prefix!(tera); "Ts", "terasecond", "teraseconds";
        @gigasecond: prefix!(giga); "Gs", "gigasecond", "gigaseconds";
        @megasecond: prefix!(mega); "Ms", "megasecond", "megaseconds";
        @kilosecond: prefix!(kilo); "ks", "kilosecond", "kiloseconds";
        @hectosecond: prefix!(hecto); "hs", "hectosecond", "hectoseconds";
        @decasecond: prefix!(deca); "das", "decasecond", "decaseconds";
        /// The second is the duration of 9 192 631 770 periods of the radition corresponding to the
        /// transition between the two hyerfine levels of ground state of caesium 133 atom.
        @second: prefix!(none); "s", "second", "seconds";
        @decisecond: prefix!(deci); "ds", "decisecond", "deciseconds";
        @centisecond: prefix!(centi); "cs", "centisecond", "centiseconds";
        @millisecond: prefix!(milli); "ms", "millisecond", "milliseconds";
        @microsecond: prefix!(micro); "µs", "microsecond", "microseconds";
        @nanosecond: prefix!(nano); "ns", "nanosecond", "nanoseconds";
        @picosecond: prefix!(pico); "ps", "picosecond", "picoseconds";
        @femtosecond: prefix!(femto); "fs", "femtosecond", "femtoseconds";
        @attosecond: prefix!(atto); "as", "attosecond", "attoseconds";
        @zeptosecond: prefix!(zepto); "zs", "zeptosecond", "zeptoseconds";
        @yoctosecond: prefix!(yocto); "ys", "yoctosecond", "yoctoseconds";
    }
}
<commit_msg>Add additional time units.<commit_after>//! Time (base unit second, s1).

quantity! {
    /// Time (base unit second, s1).
    quantity: Time; "time";
    /// Time dimension, s1.
    dimension: ISQ<
        Z0,     // length
        Z0,     // mass
        P1,     // time
        Z0,     // electric current
        Z0,     // thermodynamic temperature
        Z0,     // amount of substance
        Z0>;    // luminous intensity
    units {
        @yottasecond: prefix!(yotta); "Ys", "yottasecond", "yottaseconds";
        @zettasecond: prefix!(zetta); "Zs", "zettasecond", "zettaseconds";
        @exasecond: prefix!(exa); "Es", "exasecond", "exaseconds";
        @petasecond: prefix!(peta); "Ps", "petasecond", "petaseconds";
        @terasecond: prefix!(tera); "Ts", "terasecond", "teraseconds";
        @gigasecond: prefix!(giga); "Gs", "gigasecond", "gigaseconds";
        @megasecond: prefix!(mega); "Ms", "megasecond", "megaseconds";
        @kilosecond: prefix!(kilo); "ks", "kilosecond", "kiloseconds";
        @hectosecond: prefix!(hecto); "hs", "hectosecond", "hectoseconds";
        @decasecond: prefix!(deca); "das", "decasecond", "decaseconds";
        /// The second is the duration of 9 192 631 770 periods of the radition corresponding to the
        /// transition between the two hyerfine levels of ground state of caesium 133 atom.
        @second: prefix!(none); "s", "second", "seconds";
        @decisecond: prefix!(deci); "ds", "decisecond", "deciseconds";
        @centisecond: prefix!(centi); "cs", "centisecond", "centiseconds";
        @millisecond: prefix!(milli); "ms", "millisecond", "milliseconds";
        @microsecond: prefix!(micro); "µs", "microsecond", "microseconds";
        @nanosecond: prefix!(nano); "ns", "nanosecond", "nanoseconds";
        @picosecond: prefix!(pico); "ps", "picosecond", "picoseconds";
        @femtosecond: prefix!(femto); "fs", "femtosecond", "femtoseconds";
        @attosecond: prefix!(atto); "as", "attosecond", "attoseconds";
        @zeptosecond: prefix!(zepto); "zs", "zeptosecond", "zeptoseconds";
        @yoctosecond: prefix!(yocto); "ys", "yoctosecond", "yoctoseconds";

        @day: 8.64_E4; "d", "day", "days";
        @hour: 3.6_E3; "h", "hour", "hours";
        @minute: 6.0_E1; "min", "minute", "minutes";
        @shake: 1.0_E-8; "10.0 ns", "shake", "shakes";
        @year: 3.1536_E7; "a", "year", "years";
    }
}
<|endoftext|>


<commit_before>use std::{env, thread, time};
use std::sync::mpsc;

use crossbeam;

use bot;
use command_handler::CommandHandler;
use update::Update;

const BASE_URL: &'static str = "https://api.telegram.org/bot";

pub struct Updater {
    token: String,
    last_update_id: i32,
    pub running: bool,
    pub is_idle: bool,
}

impl Updater {
    /// Creates a new Updater struct.
    pub fn start(token: Option<String>,
                 poll_interval: Option<u64>,
                 timeout: Option<i32>,
                 network_delay: Option<i32>,
                 command_handler: CommandHandler) {
        let token = token.or_else(|| env::var("TELEGRAM_BOT_TOKEN").ok())
            .expect("You should pass in a token to new or set TELEGRAM_BOT_TOKEN");

        let updater = Updater {
            token: token,
            last_update_id: 0,
            running: false,
            is_idle: false,
        };

        updater.start_polling(poll_interval, timeout, network_delay, command_handler);
    }

    pub fn start_polling(mut self,
                         poll_interval: Option<u64>,
                         timeout: Option<i32>,
                         network_delay: Option<i32>,
                         mut command_handler: CommandHandler) {
        if !self.running {
            self.running = true;

            let (tx, rx) = mpsc::channel();
            let bot = bot::Bot::new([BASE_URL, &self.token].concat()).unwrap();

            // Spawn scoped threads
            crossbeam::scope(|scope| {
                scope.spawn(|| {
                    self.start_polling_thread(poll_interval, timeout, network_delay, &bot, tx)
                });
                scope.spawn(|| {
                    command_handler.start_command_handling(rx, &bot);
                });
            });
        }
    }

    fn start_polling_thread(&mut self,
                            poll_interval: Option<u64>,
                            timeout: Option<i32>,
                            network_delay: Option<i32>,
                            bot: &bot::Bot,
                            tx: mpsc::Sender<Update>) {
        let poll_interval = time::Duration::from_secs(poll_interval.unwrap_or(0));

        while self.running {
            let updates = bot.get_updates(self.last_update_id, None, timeout, network_delay);

            match updates {
                Ok(Some(ref v)) => {
                    if let Some(u) = v.last() {
                        for update in v {
                            tx.send(update.clone()).unwrap();
                        }
                        let update_id_store = u.update_id + 1;
                        self.last_update_id = update_id_store as i32;
                    } else {
                        // Do nothing, the vector is empty
                        continue;
                    }
                }
                Ok(None) => {
                    // Do nothing, we have nothing
                    continue;
                }
                Err(err) => {
                    // Handle error
                    continue;
                }
            };

            thread::sleep(poll_interval);
        }
    }
}
<commit_msg>Making updater::Updater::start_polling private<commit_after>use std::{env, thread, time};
use std::sync::mpsc;
use std::cell::Cell;

use crossbeam;

use bot;
use command_handler::CommandHandler;
use update::Update;

const BASE_URL: &'static str = "https://api.telegram.org/bot";
thread_local!(pub static RUNNING: Cell<bool> = Cell::new(false));

pub struct Updater {
    token: String,
    last_update_id: i32,
    pub is_idle: bool,
}

impl Updater {
    /// Creates a new Updater struct.
    pub fn start(token: Option<String>,
                 poll_interval: Option<u64>,
                 timeout: Option<i32>,
                 network_delay: Option<i32>,
                 command_handler: CommandHandler) {
        let token = token.or_else(|| env::var("TELEGRAM_BOT_TOKEN").ok())
            .expect("You should pass in a token to new or set the TELEGRAM_BOT_TOKEN env var");

        let updater = Updater {
            token: token,
            last_update_id: 0,
            is_idle: false,
        };

        updater.start_polling(poll_interval, timeout, network_delay, command_handler);
    }

    pub fn stop() {
        RUNNING.with(|s| s.set(false));
    }

    fn start_polling(mut self,
                     poll_interval: Option<u64>,
                     timeout: Option<i32>,
                     network_delay: Option<i32>,
                     mut command_handler: CommandHandler) {
        if !RUNNING.with(|s| s.get()) {
            RUNNING.with(|s| s.set(true));

            let (tx, rx) = mpsc::channel();
            let bot = bot::Bot::new([BASE_URL, &self.token].concat()).unwrap();

            // Spawn scoped threads
            crossbeam::scope(|scope| {
                scope.spawn(|| {
                    self.start_polling_thread(poll_interval, timeout, network_delay, &bot, tx)
                });
                scope.spawn(|| {
                    command_handler.start_command_handling(rx, &bot);
                });
            });
        }
    }

    fn start_polling_thread(&mut self,
                            poll_interval: Option<u64>,
                            timeout: Option<i32>,
                            network_delay: Option<i32>,
                            bot: &bot::Bot,
                            tx: mpsc::Sender<Update>) {
        let poll_interval = time::Duration::from_secs(poll_interval.unwrap_or(0));

        while RUNNING.with(|s| s.get()) {
            let updates = bot.get_updates(self.last_update_id, None, timeout, network_delay);

            match updates {
                Ok(Some(ref v)) => {
                    if let Some(u) = v.last() {
                        for update in v {
                            tx.send(update.clone()).unwrap();
                        }
                        let update_id_store = u.update_id + 1;
                        self.last_update_id = update_id_store as i32;
                    } else {
                        // Do nothing, the vector is empty
                        continue;
                    }
                }
                Ok(None) => {
                    // Do nothing, we have nothing
                    continue;
                }
                Err(err) => {
                    // Handle error
                    continue;
                }
            };

            thread::sleep(poll_interval);
        }
    }
}
<|endoftext|>


<commit_before>//! Web interface of cratesfyi

mod rustdoc;
mod releases;
mod page;
mod crate_details;
mod source;
mod pool;
mod file;
mod builds;
mod error;

use std::env;
use std::error::Error;
use std::time::Duration;
use std::path::PathBuf;
use iron::prelude::*;
use iron::{Handler, status};
use router::{Router, NoRoute};
use staticfile::Static;
use handlebars_iron::{HandlebarsEngine, DirectorySource};
use time;
use postgres::Connection;
use semver::{Version, VersionReq};
use rustc_serialize::json::{Json, ToJson};
use std::collections::BTreeMap;

/// Duration of static files for staticfile and DatabaseFileHandler (in seconds)
const STATIC_FILE_CACHE_DURATION: u64 = 60 * 60 * 24 * 30 * 12;   // 12 months
const STYLE_CSS: &'static str = include_str!(concat!(env!("OUT_DIR"), "/style.css"));

struct CratesfyiHandler {
    router_handler: Box<Handler>,
    database_file_handler: Box<Handler>,
    static_handler: Box<Handler>,
}

impl CratesfyiHandler {
    fn chain<H: Handler>(base: H) -> Chain {
        // TODO: Use DocBuilderOptions for paths
        let mut hbse = HandlebarsEngine::new();
        hbse.add(Box::new(DirectorySource::new("./templates", ".hbs")));

        // load templates
        if let Err(e) = hbse.reload() {
            panic!("Failed to load handlebar templates: {}", e.description());
        }

        let mut chain = Chain::new(base);
        chain.link_before(pool::Pool::new());
        chain.link_after(hbse);
        chain
    }

    pub fn new() -> CratesfyiHandler {
        let mut router = Router::new();
        router.get("/", releases::home_page);
        router.get("/style.css", style_css_handler);
        router.get("/about", |_: &mut Request| {
            page::Page::new(false).title("About Docs.rs").to_resp("about")
        });
        router.get("/releases", releases::releases_handler);
        router.get("/releases/recent/:page", releases::releases_handler);
        router.get("/releases/stars", releases::stars_handler);
        router.get("/releases/stars/:page", releases::stars_handler);
        router.get("/releases/:author", releases::author_handler);
        router.get("/releases/:author/:page", releases::author_handler);
        router.get("/releases/activity", releases::activity_handler);
        router.get("/releases/search", releases::search_handler);
        router.get("/crate/:name", crate_details::crate_details_handler);
        router.get("/crate/:name/", crate_details::crate_details_handler);
        router.get("/crate/:name/:version", crate_details::crate_details_handler);
        router.get("/crate/:name/:version/", crate_details::crate_details_handler);
        router.get("/crate/:name/:version/builds", builds::build_list_handler);
        router.get("/crate/:name/:version/builds/:id", builds::build_list_handler);
        router.get("/crate/:name/:version/source/", source::source_browser_handler);
        router.get("/crate/:name/:version/source/*", source::source_browser_handler);
        router.get("/:crate", rustdoc::rustdoc_redirector_handler);
        router.get("/:crate/", rustdoc::rustdoc_redirector_handler);
        router.get("/:crate/badge.svg", rustdoc::badge_handler);
        router.get("/:crate/:version", rustdoc::rustdoc_redirector_handler);
        router.get("/:crate/:version/", rustdoc::rustdoc_redirector_handler);
        router.get("/:crate/:version/search-index.js", rustdoc::rustdoc_html_server_handler);
        router.get("/:crate/:version/:target", rustdoc::rustdoc_redirector_handler);
        router.get("/:crate/:version/:target/", rustdoc::rustdoc_html_server_handler);
        router.get("/:crate/:version/:target/*.html", rustdoc::rustdoc_html_server_handler);

        let router_chain = Self::chain(router);
        let prefix = PathBuf::from(env::var("CRATESFYI_PREFIX").unwrap()).join("public_html");
        let static_handler = Static::new(prefix)
            .cache(Duration::from_secs(STATIC_FILE_CACHE_DURATION));

        CratesfyiHandler {
            router_handler: Box::new(router_chain),
            database_file_handler: Box::new(file::DatabaseFileHandler),
            static_handler: Box::new(static_handler),
        }
    }
}

impl Handler for CratesfyiHandler {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        // try router first then db/static file handler
        // return 404 if none of them return Ok
        self.router_handler
            .handle(req)
            .or_else(|e| {
                // if router fails try to serve files from database first
                self.database_file_handler.handle(req).or(Err(e))
            })
            .or_else(|e| {
                // and then try static handler. if all of them fails, return 404
                self.static_handler.handle(req).or(Err(e))
            })
            .or_else(|e| {
                debug!("{}", e.description());
                let err = if let Some(err) = e.error.downcast::<error::Nope>() {
                    *err
                } else if e.error.downcast::<NoRoute>().is_some() {
                    error::Nope::ResourceNotFound
                } else {
                    panic!("all cratesfyi errors should be of type Nope");
                };
                Self::chain(err).handle(req)
            })
    }
}

fn match_version(conn: &Connection, name: &str, version: Option<&str>) -> Option<String> {

    // version is an Option<&str> from router::Router::get
    // need to decode first
    use url::percent_encoding::percent_decode;
    let req_version = version.and_then(|v| {
        match percent_decode(v.as_bytes()).decode_utf8() {
            Ok(p) => Some(p.into_owned()),
            Err(_) => None,
        }
    }).map(|v| if v == "newest" { "*".to_owned() } else { v })
    .unwrap_or("*".to_string());

    let versions = {
        let mut versions = Vec::new();
        let rows = conn.query("SELECT versions FROM crates WHERE name = $1", &[&name]).unwrap();
        if rows.len() == 0 {
            return None;
        }
        let versions_json: Json = rows.get(0).get(0);
        for version in versions_json.as_array().unwrap() {
            let version: String = version.as_string().unwrap().to_owned();
            versions.push(version);
        }

        versions
    };

    // first check for exact match
    // we can't expect users to use semver in query
    for version in &versions {
        if version == &req_version {
            return Some(version.clone())
        }
    }

    // Now try to match with semver
    let req_sem_ver = match VersionReq::parse(&req_version) {
        Ok(v) => v,
        Err(_) => return None,
    };;

    // we need to sort versions first
    let versions_sem = {
        let mut versions_sem: Vec<Version> = Vec::new();

        for version in &versions {
            // in theory a crate must always have a semver compatible version
            // but check result just in case
            let version = match Version::parse(&version) {
                Ok(v) => v,
                Err(_) => return None,
            };
            versions_sem.push(version);
        }

        versions_sem.sort();
        versions_sem.reverse();
        versions_sem
    };

    for version in &versions_sem {
        if req_sem_ver.matches(&version) {
            return Some(format!("{}", version))
        }
    }

    None
}

/// Wrapper around the pulldown-cmark parser and renderer to render markdown
fn render_markdown(text: &str) -> String {
    use hoedown::{Markdown, Html, Render, Extension};
    use hoedown::renderer::html;

    let extensions = {
        use hoedown::{FENCED_CODE, FOOTNOTES, SUPERSCRIPT, TABLES};

        let mut extensions = Extension::empty();
        extensions.insert(FENCED_CODE);
        extensions.insert(FOOTNOTES);
        extensions.insert(SUPERSCRIPT);
        extensions.insert(TABLES);

        extensions
    };

    let doc = Markdown::new(text).extensions(extensions);
    let mut html = Html::new(html::Flags::empty(), 0);
    html.render(&doc).to_str().unwrap().to_owned()
}

/// Starts cratesfyi web server
pub fn start_web_server(sock_addr: Option<&str>) {
    let cratesfyi = CratesfyiHandler::new();
    Iron::new(cratesfyi).http(sock_addr.unwrap_or("localhost:3000")).unwrap();
}

/// Converts Timespec to nice readable relative time string
fn duration_to_str(ts: time::Timespec) -> String {

    let tm = time::at(ts);
    let delta = time::now() - tm;

    if delta.num_days() > 5 {
        format!("{}", tm.strftime("%b %d, %Y").unwrap())
    } else if delta.num_days() > 1 {
        format!("{} days ago", delta.num_days())
    } else if delta.num_days() == 1 {
        "one day ago".to_string()
    } else if delta.num_hours() > 1 {
        format!("{} hours ago", delta.num_hours())
    } else if delta.num_hours() == 1 {
        "an hour ago".to_string()
    } else if delta.num_minutes() > 1 {
        format!("{} minutes ago", delta.num_minutes())
    } else if delta.num_minutes() == 1 {
        "one minute ago".to_string()
    } else if delta.num_seconds() > 0 {
        format!("{} seconds ago", delta.num_seconds())
    } else {
        "just now".to_string()
    }

}

fn style_css_handler(_: &mut Request) -> IronResult<Response> {
    use iron::headers::{CacheControl, CacheDirective, ContentType};
    let mut response = Response::with((status::Ok, STYLE_CSS));
    let cache = vec![CacheDirective::Public,
    CacheDirective::MaxAge(STATIC_FILE_CACHE_DURATION as u32)];
    response.headers.set(ContentType("text/css".parse().unwrap()));
    response.headers.set(CacheControl(cache));
    Ok(response)
}

/// MetaData used in header
#[derive(Debug)]
pub struct MetaData {
    pub name: String,
    pub version: String,
    pub description: Option<String>,
    pub target_name: Option<String>,
    pub rustdoc_status: bool,
}

impl MetaData {
    pub fn from_crate(conn: &Connection, name: &str, version: &str) -> Option<MetaData> {
        for row in &conn.query("SELECT crates.name,
                                       releases.version,
                                       releases.description,
                                       releases.target_name,
                                       releases.rustdoc_status
                                FROM releases
                                INNER JOIN crates ON crates.id = releases.crate_id
                                WHERE crates.name = $1 AND releases.version = $2",
                               &[&name, &version]).unwrap() {

            return Some(MetaData {
                name: row.get(0),
                version: row.get(1),
                description: row.get(2),
                target_name: row.get(3),
                rustdoc_status: row.get(4),
            });
        }

        None
    }
}

impl ToJson for MetaData {
    fn to_json(&self) -> Json {
        let mut m: BTreeMap<String, Json> = BTreeMap::new();
        m.insert("name".to_owned(), self.name.to_json());
        m.insert("version".to_owned(), self.version.to_json());
        m.insert("description".to_owned(), self.description.to_json());
        m.insert("target_name".to_owned(), self.target_name.to_json());
        m.insert("rustdoc_status".to_owned(), self.rustdoc_status.to_json());
        m.to_json()
    }
}

#[cfg(test)]
mod test {
    extern crate env_logger;
    use super::*;

    #[test]
    #[ignore]
    fn test_start_web_server() {
        // FIXME: This test is doing nothing
        let _ = env_logger::init();
        start_web_server(None);
    }
}
<commit_msg>Use AUTOLINK extension with hoedown<commit_after>//! Web interface of cratesfyi

mod rustdoc;
mod releases;
mod page;
mod crate_details;
mod source;
mod pool;
mod file;
mod builds;
mod error;

use std::env;
use std::error::Error;
use std::time::Duration;
use std::path::PathBuf;
use iron::prelude::*;
use iron::{Handler, status};
use router::{Router, NoRoute};
use staticfile::Static;
use handlebars_iron::{HandlebarsEngine, DirectorySource};
use time;
use postgres::Connection;
use semver::{Version, VersionReq};
use rustc_serialize::json::{Json, ToJson};
use std::collections::BTreeMap;

/// Duration of static files for staticfile and DatabaseFileHandler (in seconds)
const STATIC_FILE_CACHE_DURATION: u64 = 60 * 60 * 24 * 30 * 12;   // 12 months
const STYLE_CSS: &'static str = include_str!(concat!(env!("OUT_DIR"), "/style.css"));

struct CratesfyiHandler {
    router_handler: Box<Handler>,
    database_file_handler: Box<Handler>,
    static_handler: Box<Handler>,
}

impl CratesfyiHandler {
    fn chain<H: Handler>(base: H) -> Chain {
        // TODO: Use DocBuilderOptions for paths
        let mut hbse = HandlebarsEngine::new();
        hbse.add(Box::new(DirectorySource::new("./templates", ".hbs")));

        // load templates
        if let Err(e) = hbse.reload() {
            panic!("Failed to load handlebar templates: {}", e.description());
        }

        let mut chain = Chain::new(base);
        chain.link_before(pool::Pool::new());
        chain.link_after(hbse);
        chain
    }

    pub fn new() -> CratesfyiHandler {
        let mut router = Router::new();
        router.get("/", releases::home_page);
        router.get("/style.css", style_css_handler);
        router.get("/about", |_: &mut Request| {
            page::Page::new(false).title("About Docs.rs").to_resp("about")
        });
        router.get("/releases", releases::releases_handler);
        router.get("/releases/recent/:page", releases::releases_handler);
        router.get("/releases/stars", releases::stars_handler);
        router.get("/releases/stars/:page", releases::stars_handler);
        router.get("/releases/:author", releases::author_handler);
        router.get("/releases/:author/:page", releases::author_handler);
        router.get("/releases/activity", releases::activity_handler);
        router.get("/releases/search", releases::search_handler);
        router.get("/crate/:name", crate_details::crate_details_handler);
        router.get("/crate/:name/", crate_details::crate_details_handler);
        router.get("/crate/:name/:version", crate_details::crate_details_handler);
        router.get("/crate/:name/:version/", crate_details::crate_details_handler);
        router.get("/crate/:name/:version/builds", builds::build_list_handler);
        router.get("/crate/:name/:version/builds/:id", builds::build_list_handler);
        router.get("/crate/:name/:version/source/", source::source_browser_handler);
        router.get("/crate/:name/:version/source/*", source::source_browser_handler);
        router.get("/:crate", rustdoc::rustdoc_redirector_handler);
        router.get("/:crate/", rustdoc::rustdoc_redirector_handler);
        router.get("/:crate/badge.svg", rustdoc::badge_handler);
        router.get("/:crate/:version", rustdoc::rustdoc_redirector_handler);
        router.get("/:crate/:version/", rustdoc::rustdoc_redirector_handler);
        router.get("/:crate/:version/search-index.js", rustdoc::rustdoc_html_server_handler);
        router.get("/:crate/:version/:target", rustdoc::rustdoc_redirector_handler);
        router.get("/:crate/:version/:target/", rustdoc::rustdoc_html_server_handler);
        router.get("/:crate/:version/:target/*.html", rustdoc::rustdoc_html_server_handler);

        let router_chain = Self::chain(router);
        let prefix = PathBuf::from(env::var("CRATESFYI_PREFIX").unwrap()).join("public_html");
        let static_handler = Static::new(prefix)
            .cache(Duration::from_secs(STATIC_FILE_CACHE_DURATION));

        CratesfyiHandler {
            router_handler: Box::new(router_chain),
            database_file_handler: Box::new(file::DatabaseFileHandler),
            static_handler: Box::new(static_handler),
        }
    }
}

impl Handler for CratesfyiHandler {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        // try router first then db/static file handler
        // return 404 if none of them return Ok
        self.router_handler
            .handle(req)
            .or_else(|e| {
                // if router fails try to serve files from database first
                self.database_file_handler.handle(req).or(Err(e))
            })
            .or_else(|e| {
                // and then try static handler. if all of them fails, return 404
                self.static_handler.handle(req).or(Err(e))
            })
            .or_else(|e| {
                debug!("{}", e.description());
                let err = if let Some(err) = e.error.downcast::<error::Nope>() {
                    *err
                } else if e.error.downcast::<NoRoute>().is_some() {
                    error::Nope::ResourceNotFound
                } else {
                    panic!("all cratesfyi errors should be of type Nope");
                };
                Self::chain(err).handle(req)
            })
    }
}

fn match_version(conn: &Connection, name: &str, version: Option<&str>) -> Option<String> {

    // version is an Option<&str> from router::Router::get
    // need to decode first
    use url::percent_encoding::percent_decode;
    let req_version = version.and_then(|v| {
        match percent_decode(v.as_bytes()).decode_utf8() {
            Ok(p) => Some(p.into_owned()),
            Err(_) => None,
        }
    }).map(|v| if v == "newest" { "*".to_owned() } else { v })
    .unwrap_or("*".to_string());

    let versions = {
        let mut versions = Vec::new();
        let rows = conn.query("SELECT versions FROM crates WHERE name = $1", &[&name]).unwrap();
        if rows.len() == 0 {
            return None;
        }
        let versions_json: Json = rows.get(0).get(0);
        for version in versions_json.as_array().unwrap() {
            let version: String = version.as_string().unwrap().to_owned();
            versions.push(version);
        }

        versions
    };

    // first check for exact match
    // we can't expect users to use semver in query
    for version in &versions {
        if version == &req_version {
            return Some(version.clone())
        }
    }

    // Now try to match with semver
    let req_sem_ver = match VersionReq::parse(&req_version) {
        Ok(v) => v,
        Err(_) => return None,
    };;

    // we need to sort versions first
    let versions_sem = {
        let mut versions_sem: Vec<Version> = Vec::new();

        for version in &versions {
            // in theory a crate must always have a semver compatible version
            // but check result just in case
            let version = match Version::parse(&version) {
                Ok(v) => v,
                Err(_) => return None,
            };
            versions_sem.push(version);
        }

        versions_sem.sort();
        versions_sem.reverse();
        versions_sem
    };

    for version in &versions_sem {
        if req_sem_ver.matches(&version) {
            return Some(format!("{}", version))
        }
    }

    None
}

/// Wrapper around the pulldown-cmark parser and renderer to render markdown
fn render_markdown(text: &str) -> String {
    use hoedown::{Markdown, Html, Render, Extension};
    use hoedown::renderer::html;

    let extensions = {
        use hoedown::{FENCED_CODE, FOOTNOTES, SUPERSCRIPT, TABLES, AUTOLINK};

        let mut extensions = Extension::empty();
        extensions.insert(FENCED_CODE);
        extensions.insert(FOOTNOTES);
        extensions.insert(SUPERSCRIPT);
        extensions.insert(TABLES);
        extensions.insert(AUTOLINK);

        extensions
    };

    let doc = Markdown::new(text).extensions(extensions);
    let mut html = Html::new(html::Flags::empty(), 0);
    html.render(&doc).to_str().unwrap().to_owned()
}

/// Starts cratesfyi web server
pub fn start_web_server(sock_addr: Option<&str>) {
    let cratesfyi = CratesfyiHandler::new();
    Iron::new(cratesfyi).http(sock_addr.unwrap_or("localhost:3000")).unwrap();
}

/// Converts Timespec to nice readable relative time string
fn duration_to_str(ts: time::Timespec) -> String {

    let tm = time::at(ts);
    let delta = time::now() - tm;

    if delta.num_days() > 5 {
        format!("{}", tm.strftime("%b %d, %Y").unwrap())
    } else if delta.num_days() > 1 {
        format!("{} days ago", delta.num_days())
    } else if delta.num_days() == 1 {
        "one day ago".to_string()
    } else if delta.num_hours() > 1 {
        format!("{} hours ago", delta.num_hours())
    } else if delta.num_hours() == 1 {
        "an hour ago".to_string()
    } else if delta.num_minutes() > 1 {
        format!("{} minutes ago", delta.num_minutes())
    } else if delta.num_minutes() == 1 {
        "one minute ago".to_string()
    } else if delta.num_seconds() > 0 {
        format!("{} seconds ago", delta.num_seconds())
    } else {
        "just now".to_string()
    }

}

fn style_css_handler(_: &mut Request) -> IronResult<Response> {
    use iron::headers::{CacheControl, CacheDirective, ContentType};
    let mut response = Response::with((status::Ok, STYLE_CSS));
    let cache = vec![CacheDirective::Public,
    CacheDirective::MaxAge(STATIC_FILE_CACHE_DURATION as u32)];
    response.headers.set(ContentType("text/css".parse().unwrap()));
    response.headers.set(CacheControl(cache));
    Ok(response)
}

/// MetaData used in header
#[derive(Debug)]
pub struct MetaData {
    pub name: String,
    pub version: String,
    pub description: Option<String>,
    pub target_name: Option<String>,
    pub rustdoc_status: bool,
}

impl MetaData {
    pub fn from_crate(conn: &Connection, name: &str, version: &str) -> Option<MetaData> {
        for row in &conn.query("SELECT crates.name,
                                       releases.version,
                                       releases.description,
                                       releases.target_name,
                                       releases.rustdoc_status
                                FROM releases
                                INNER JOIN crates ON crates.id = releases.crate_id
                                WHERE crates.name = $1 AND releases.version = $2",
                               &[&name, &version]).unwrap() {

            return Some(MetaData {
                name: row.get(0),
                version: row.get(1),
                description: row.get(2),
                target_name: row.get(3),
                rustdoc_status: row.get(4),
            });
        }

        None
    }
}

impl ToJson for MetaData {
    fn to_json(&self) -> Json {
        let mut m: BTreeMap<String, Json> = BTreeMap::new();
        m.insert("name".to_owned(), self.name.to_json());
        m.insert("version".to_owned(), self.version.to_json());
        m.insert("description".to_owned(), self.description.to_json());
        m.insert("target_name".to_owned(), self.target_name.to_json());
        m.insert("rustdoc_status".to_owned(), self.rustdoc_status.to_json());
        m.to_json()
    }
}

#[cfg(test)]
mod test {
    extern crate env_logger;
    use super::*;

    #[test]
    #[ignore]
    fn test_start_web_server() {
        // FIXME: This test is doing nothing
        let _ = env_logger::init();
        start_web_server(None);
    }
}
<|endoftext|>


<commit_before>use std::ffi::OsStr;
use std::io::{self, Read, Write};
use std::mem;
use std::os::windows::ffi::OsStrExt;
use std::os::windows::io::{FromRawHandle, RawHandle};
use std::path::Path;
use std::pin::Pin;
use std::ptr;
use std::task::{Context, Poll};

use serialport::{COMPort, SerialPort, SerialPortBuilder};
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
use tokio::net::windows::named_pipe::NamedPipeClient;
use winapi::um::commapi::SetCommTimeouts;
use winapi::um::fileapi::{CreateFileW, OPEN_EXISTING};
use winapi::um::handleapi::INVALID_HANDLE_VALUE;
use winapi::um::winbase::{COMMTIMEOUTS, FILE_FLAG_OVERLAPPED};
use winapi::um::winnt::{FILE_ATTRIBUTE_NORMAL, GENERIC_READ, GENERIC_WRITE, HANDLE};

#[derive(Debug)]
pub(crate) struct WindowsSerialStream {
    inner: COMPort,

    // [CF] Named pipes and COM ports are actually two entirely different things that hardly have anything in common.
    // The only thing they share is the opaque `HANDLE` type that can be fed into `CreateFileW`, `ReadFile`, `WriteFile`, etc.
    //
    // Both `mio` and `tokio` don't yet have any code to work on arbitrary HANDLEs.
    // But they have code for dealing with named pipes, and we (ab)use that here to work on COM ports.
    pipe: NamedPipeClient,
}

impl WindowsSerialStream {
    /// Opens a COM port at the specified path
    // [CF] Copied from https://github.com/berkowski/mio-serial/blob/38a3778324da5e312cfb31402bd89e52d0548a4c/src/lib.rs#L113-L166
    // See remarks in the code for important changes!
    pub fn open(builder: &SerialPortBuilder) -> crate::Result<Self> {
        let (path, baud, parity, data_bits, stop_bits, flow_control) = {
            let com_port = serialport::COMPort::open(builder)?;
            let name = com_port.name().ok_or(crate::Error::new(
                crate::ErrorKind::NoDevice,
                "Empty device name",
            ))?;
            let baud = com_port.baud_rate()?;
            let parity = com_port.parity()?;
            let data_bits = com_port.data_bits()?;
            let stop_bits = com_port.stop_bits()?;
            let flow_control = com_port.flow_control()?;

            let mut path = Vec::<u16>::new();
            path.extend(OsStr::new("\\\\.\\").encode_wide());
            path.extend(Path::new(&name).as_os_str().encode_wide());
            path.push(0);

            (path, baud, parity, data_bits, stop_bits, flow_control)
        };

        let handle = unsafe {
            CreateFileW(
                path.as_ptr(),
                GENERIC_READ | GENERIC_WRITE,
                0,
                ptr::null_mut(),
                OPEN_EXISTING,
                FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
                0 as HANDLE,
            )
        };

        if handle == INVALID_HANDLE_VALUE {
            return Err(crate::Error::from(io::Error::last_os_error()));
        }
        let handle = unsafe { mem::transmute(handle) };

        // Construct NamedPipe and COMPort from Handle
        //
        // [CF] ATTENTION: First set the COM Port parameters, THEN create the NamedPipeClient.
        // If I do it the other way round (as mio-serial does), the runtime hangs
        // indefinitely when querying for read readiness!
        //
        let mut com_port = unsafe { serialport::COMPort::from_raw_handle(handle) };
        com_port.set_baud_rate(baud)?;
        com_port.set_parity(parity)?;
        com_port.set_data_bits(data_bits)?;
        com_port.set_stop_bits(stop_bits)?;
        com_port.set_flow_control(flow_control)?;
        Self::override_comm_timeouts(handle)?;

        let pipe = unsafe { NamedPipeClient::from_raw_handle(handle)? };

        Ok(Self {
            inner: com_port,
            pipe,
        })
    }

    pub fn get_ref(&self) -> &COMPort {
        &self.inner
    }

    pub fn get_mut(&mut self) -> &mut COMPort {
        &mut self.inner
    }

    pub async fn readable(&self) -> io::Result<()> {
        self.pipe.readable().await
    }

    pub async fn writable(&self) -> io::Result<()> {
        self.pipe.writable().await
    }

    /// Overrides timeout value set by serialport-rs so that the read end will
    /// never wake up with 0-byte payload.
    // [CF] Copied from https://github.com/berkowski/mio-serial/blob/38a3778324da5e312cfb31402bd89e52d0548a4c/src/lib.rs#L685-L702
    fn override_comm_timeouts(handle: RawHandle) -> io::Result<()> {
        let mut timeouts = COMMTIMEOUTS {
            // wait at most 1ms between two bytes (0 means no timeout)
            ReadIntervalTimeout: 1,
            // disable "total" timeout to wait at least 1 byte forever
            ReadTotalTimeoutMultiplier: 0,
            ReadTotalTimeoutConstant: 0,
            // write timeouts are just copied from serialport-rs
            WriteTotalTimeoutMultiplier: 0,
            WriteTotalTimeoutConstant: 0,
        };

        let r = unsafe { SetCommTimeouts(handle, &mut timeouts) };
        if r == 0 {
            return Err(io::Error::last_os_error());
        }
        Ok(())
    }
}

impl Read for WindowsSerialStream {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.pipe.try_read(buf)
    }
}

impl Write for WindowsSerialStream {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.pipe.try_write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.inner.flush()
    }
}

impl AsyncRead for WindowsSerialStream {
    fn poll_read(
        self: Pin<&mut Self>,
        cx: &mut Context<'_>,
        buf: &mut ReadBuf<'_>,
    ) -> Poll<io::Result<()>> {
        Pin::new(&mut self.get_mut().pipe).poll_read(cx, buf)
    }
}

impl AsyncWrite for WindowsSerialStream {
    fn poll_write(
        self: Pin<&mut Self>,
        cx: &mut Context<'_>,
        buf: &[u8],
    ) -> Poll<io::Result<usize>> {
        Pin::new(&mut self.get_mut().pipe).poll_write(cx, buf)
    }

    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
        Pin::new(&mut self.get_mut().pipe).poll_flush(cx)
    }

    fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
        Pin::new(&mut self.get_mut().pipe).poll_shutdown(cx)
    }
}
<commit_msg>Use `ManuallyDrop` to prevent freeing the Win32 handle twice.<commit_after>use std::ffi::OsStr;
use std::io::{self, Read, Write};
use std::mem::{self, ManuallyDrop};
use std::os::windows::ffi::OsStrExt;
use std::os::windows::io::{FromRawHandle, RawHandle};
use std::path::Path;
use std::pin::Pin;
use std::ptr;
use std::task::{Context, Poll};

use serialport::{COMPort, SerialPort, SerialPortBuilder};
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
use tokio::net::windows::named_pipe::NamedPipeClient;
use winapi::um::commapi::SetCommTimeouts;
use winapi::um::fileapi::{CreateFileW, OPEN_EXISTING};
use winapi::um::handleapi::INVALID_HANDLE_VALUE;
use winapi::um::winbase::{COMMTIMEOUTS, FILE_FLAG_OVERLAPPED};
use winapi::um::winnt::{FILE_ATTRIBUTE_NORMAL, GENERIC_READ, GENERIC_WRITE, HANDLE};

#[derive(Debug)]
pub(crate) struct WindowsSerialStream {
    // [EG] We use `ManuallyDrop` because both `COMPort` and `NamedPipeClient` take
    // ownership of the raw handle. To avoid closing the handle twice when
    // the `Serial` is dropped, we explicitly do **not** drop one of the
    // owned instances. Yes, it is a hack.
    inner: ManuallyDrop<COMPort>,

    // [CF] Named pipes and COM ports are actually two entirely different things that hardly have anything in common.
    // The only thing they share is the opaque `HANDLE` type that can be fed into `CreateFileW`, `ReadFile`, `WriteFile`, etc.
    //
    // Both `mio` and `tokio` don't yet have any code to work on arbitrary HANDLEs.
    // But they have code for dealing with named pipes, and we (ab)use that here to work on COM ports.
    pipe: NamedPipeClient,
}

impl WindowsSerialStream {
    /// Opens a COM port at the specified path
    // [CF] Copied from https://github.com/berkowski/mio-serial/blob/38a3778324da5e312cfb31402bd89e52d0548a4c/src/lib.rs#L113-L166
    // See remarks in the code for important changes!
    pub fn open(builder: &SerialPortBuilder) -> crate::Result<Self> {
        let (path, baud, parity, data_bits, stop_bits, flow_control) = {
            let com_port = serialport::COMPort::open(builder)?;
            let name = com_port.name().ok_or(crate::Error::new(
                crate::ErrorKind::NoDevice,
                "Empty device name",
            ))?;
            let baud = com_port.baud_rate()?;
            let parity = com_port.parity()?;
            let data_bits = com_port.data_bits()?;
            let stop_bits = com_port.stop_bits()?;
            let flow_control = com_port.flow_control()?;

            let mut path = Vec::<u16>::new();
            path.extend(OsStr::new("\\\\.\\").encode_wide());
            path.extend(Path::new(&name).as_os_str().encode_wide());
            path.push(0);

            (path, baud, parity, data_bits, stop_bits, flow_control)
        };

        let handle = unsafe {
            CreateFileW(
                path.as_ptr(),
                GENERIC_READ | GENERIC_WRITE,
                0,
                ptr::null_mut(),
                OPEN_EXISTING,
                FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
                0 as HANDLE,
            )
        };

        if handle == INVALID_HANDLE_VALUE {
            return Err(crate::Error::from(io::Error::last_os_error()));
        }
        let handle = unsafe { mem::transmute(handle) };

        // Construct NamedPipe and COMPort from Handle
        //
        // [CF] ATTENTION: First set the COM Port parameters, THEN create the NamedPipeClient.
        // If I do it the other way round (as mio-serial does), the runtime hangs
        // indefinitely when querying for read readiness!
        //
        let mut com_port = unsafe { serialport::COMPort::from_raw_handle(handle) };
        com_port.set_baud_rate(baud)?;
        com_port.set_parity(parity)?;
        com_port.set_data_bits(data_bits)?;
        com_port.set_stop_bits(stop_bits)?;
        com_port.set_flow_control(flow_control)?;
        Self::override_comm_timeouts(handle)?;

        let inner = ManuallyDrop::new(com_port);
        let pipe = unsafe { NamedPipeClient::from_raw_handle(handle)? };

        Ok(Self { inner, pipe })
    }

    pub fn get_ref(&self) -> &COMPort {
        &self.inner
    }

    pub fn get_mut(&mut self) -> &mut COMPort {
        &mut self.inner
    }

    pub async fn readable(&self) -> io::Result<()> {
        self.pipe.readable().await
    }

    pub async fn writable(&self) -> io::Result<()> {
        self.pipe.writable().await
    }

    /// Overrides timeout value set by serialport-rs so that the read end will
    /// never wake up with 0-byte payload.
    // [CF] Copied from https://github.com/berkowski/mio-serial/blob/38a3778324da5e312cfb31402bd89e52d0548a4c/src/lib.rs#L685-L702
    fn override_comm_timeouts(handle: RawHandle) -> io::Result<()> {
        let mut timeouts = COMMTIMEOUTS {
            // wait at most 1ms between two bytes (0 means no timeout)
            ReadIntervalTimeout: 1,
            // disable "total" timeout to wait at least 1 byte forever
            ReadTotalTimeoutMultiplier: 0,
            ReadTotalTimeoutConstant: 0,
            // write timeouts are just copied from serialport-rs
            WriteTotalTimeoutMultiplier: 0,
            WriteTotalTimeoutConstant: 0,
        };

        let r = unsafe { SetCommTimeouts(handle, &mut timeouts) };
        if r == 0 {
            return Err(io::Error::last_os_error());
        }
        Ok(())
    }
}

impl Read for WindowsSerialStream {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.pipe.try_read(buf)
    }
}

impl Write for WindowsSerialStream {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.pipe.try_write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.inner.flush()
    }
}

impl AsyncRead for WindowsSerialStream {
    fn poll_read(
        self: Pin<&mut Self>,
        cx: &mut Context<'_>,
        buf: &mut ReadBuf<'_>,
    ) -> Poll<io::Result<()>> {
        Pin::new(&mut self.get_mut().pipe).poll_read(cx, buf)
    }
}

impl AsyncWrite for WindowsSerialStream {
    fn poll_write(
        self: Pin<&mut Self>,
        cx: &mut Context<'_>,
        buf: &[u8],
    ) -> Poll<io::Result<usize>> {
        Pin::new(&mut self.get_mut().pipe).poll_write(cx, buf)
    }

    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
        Pin::new(&mut self.get_mut().pipe).poll_flush(cx)
    }

    fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
        Pin::new(&mut self.get_mut().pipe).poll_shutdown(cx)
    }
}
<|endoftext|>


<commit_before>/// Enables ANSI code support on Windows 10.
///
/// This uses Windows API calls to alter the properties of the console that
/// the program is running in.
///
/// https://msdn.microsoft.com/en-us/library/windows/desktop/mt638032(v=vs.85).aspx
///
/// Returns a `Result` with the Windows error code if unsuccessful.
#[cfg(windows)]
pub fn enable_ansi_support() -> Result<(), u32> {
    // ref: https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences#EXAMPLE_OF_ENABLING_VIRTUAL_TERMINAL_PROCESSING @@ https://archive.is/L7wRJ#76%

    use std::ffi::OsStr;
    use std::iter::once;
    use std::os::windows::ffi::OsStrExt;
    use std::ptr::null_mut;
    use winapi::um::consoleapi::{GetConsoleMode, SetConsoleMode};
    use winapi::um::errhandlingapi::GetLastError;
    use winapi::um::fileapi::{CreateFile2, OPEN_EXISTING};
    use winapi::um::handleapi::INVALID_HANDLE_VALUE;
    use winapi::um::winnt::{FILE_SHARE_WRITE, GENERIC_READ, GENERIC_WRITE};

    const ENABLE_VIRTUAL_TERMINAL_PROCESSING: u32 = 0x0004;

    unsafe {
        // ref: https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-createfile2
        // Using `CreateFile2("CONOUT$", ...)` to retrieve the console handle works correctly even if STDOUT and/or STDERR are redirected
        let console_out_name: Vec<u16> = OsStr::new("CONOUT$").encode_wide().chain(once(0)).collect();
        let console_handle = CreateFile2(
            console_out_name.as_ptr(),
            GENERIC_READ | GENERIC_WRITE,
            FILE_SHARE_WRITE,
            OPEN_EXISTING,
            null_mut(),
        );
        if console_handle == INVALID_HANDLE_VALUE
        {
            return Err(GetLastError());
        }

        // ref: https://docs.microsoft.com/en-us/windows/console/getconsolemode
        let mut console_mode: u32 = 0;
        if 0 == GetConsoleMode(console_handle, &mut console_mode)
        {
            return Err(GetLastError());
        }

        // VT processing not already enabled?
        if console_mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING == 0 {
            // https://docs.microsoft.com/en-us/windows/console/setconsolemode
            if 0 == SetConsoleMode(console_handle, console_mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING)
            {
                return Err(GetLastError());
            }
        }
    }

    return Ok(());
}
<commit_msg>change ~ increase windows early version compatibility<commit_after>/// Enables ANSI code support on Windows 10.
///
/// This uses Windows API calls to alter the properties of the console that
/// the program is running in.
///
/// https://msdn.microsoft.com/en-us/library/windows/desktop/mt638032(v=vs.85).aspx
///
/// Returns a `Result` with the Windows error code if unsuccessful.
#[cfg(windows)]
pub fn enable_ansi_support() -> Result<(), u32> {
    // ref: https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences#EXAMPLE_OF_ENABLING_VIRTUAL_TERMINAL_PROCESSING @@ https://archive.is/L7wRJ#76%

    use std::ffi::OsStr;
    use std::iter::once;
    use std::os::windows::ffi::OsStrExt;
    use std::ptr::null_mut;
    use winapi::um::consoleapi::{GetConsoleMode, SetConsoleMode};
    use winapi::um::errhandlingapi::GetLastError;
    use winapi::um::fileapi::{CreateFileW, OPEN_EXISTING};
    use winapi::um::handleapi::INVALID_HANDLE_VALUE;
    use winapi::um::winnt::{FILE_SHARE_WRITE, GENERIC_READ, GENERIC_WRITE};

    const ENABLE_VIRTUAL_TERMINAL_PROCESSING: u32 = 0x0004;

    unsafe {
        // ref: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew
        // Using `CreateFileW("CONOUT$", ...)` to retrieve the console handle works correctly even if STDOUT and/or STDERR are redirected
        let console_out_name: Vec<u16> = OsStr::new("CONOUT$").encode_wide().chain(once(0)).collect();
        let console_handle = CreateFileW(
            console_out_name.as_ptr(),
            GENERIC_READ | GENERIC_WRITE,
            FILE_SHARE_WRITE,
            null_mut(),
            OPEN_EXISTING,
            0,
            null_mut(),
        );
        if console_handle == INVALID_HANDLE_VALUE
        {
            return Err(GetLastError());
        }

        // ref: https://docs.microsoft.com/en-us/windows/console/getconsolemode
        let mut console_mode: u32 = 0;
        if 0 == GetConsoleMode(console_handle, &mut console_mode)
        {
            return Err(GetLastError());
        }

        // VT processing not already enabled?
        if console_mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING == 0 {
            // https://docs.microsoft.com/en-us/windows/console/setconsolemode
            if 0 == SetConsoleMode(console_handle, console_mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING)
            {
                return Err(GetLastError());
            }
        }
    }

    return Ok(());
}
<|endoftext|>


<commit_before>//! This is code from [Tungstenite project](https://github.com/snapview/tungstenite-rs)
use std::cmp::min;
use std::mem::uninitialized;
use std::ptr::copy_nonoverlapping;

/// Mask/unmask a frame.
#[inline]
pub fn apply_mask(buf: &mut [u8], mask: u32) {
    apply_mask_fast32(buf, mask)
}

/// A safe unoptimized mask application.
#[inline]
#[allow(dead_code)]
fn apply_mask_fallback(buf: &mut [u8], mask: &[u8; 4]) {
    for (i, byte) in buf.iter_mut().enumerate() {
        *byte ^= mask[i & 3];
    }
}

/// Faster version of `apply_mask()` which operates on 8-byte blocks.
#[inline]
#[cfg_attr(feature="cargo-clippy", allow(cast_lossless))]
fn apply_mask_fast32(buf: &mut [u8], mask_u32: u32) {
    let mut ptr = buf.as_mut_ptr();
    let mut len = buf.len();

    // Possible first unaligned block.
    let head = min(len, (4 - (ptr as usize & 3)) & 3);
    let mask_u32 = if head > 0 {
        unsafe {
            xor_mem(ptr, mask_u32, head);
            ptr = ptr.offset(head as isize);
        }
        len -= head;
        //let mask_u32 =
        if cfg!(target_endian = "big") {
            mask_u32.rotate_left(8 * head as u32)
        } else {
            mask_u32.rotate_right(8 * head as u32)
        }//;

        /*
        let head = min(len, (4 - (ptr as usize & 3)) & 3);
        if head > 0 {
            unsafe {
                xor_mem(ptr, mask_u32, head);
                ptr = ptr.offset(head as isize);
            }
            len -= head;
            if cfg!(target_endian = "big") {
                mask_u32.rotate_left(8 * head as u32)
            } else {
                mask_u32.rotate_right(8 * head as u32)
            }
        } else {
            mask_u32
        }*/
    } else {
        mask_u32
    };

    if len > 0 {
        debug_assert_eq!(ptr as usize % 4, 0);
    }

    // Properly aligned middle of the data.
    if len >= 8 {
        let mut mask_u64 = mask_u32 as u64;
        mask_u64 = mask_u64 << 32 | mask_u32 as u64;

        while len >= 8 {
            unsafe {
                *(ptr as *mut u64) ^= mask_u64;
                ptr = ptr.offset(8);
                len -= 8;
            }
        }
    }

    while len >= 4 {
        unsafe {
            *(ptr as *mut u32) ^= mask_u32;
            ptr = ptr.offset(4);
            len -= 4;
        }
    }

    // Possible last block.
    if len > 0 {
        unsafe { xor_mem(ptr, mask_u32, len); }
    }
}

#[inline]
// TODO: copy_nonoverlapping here compiles to call memcpy. While it is not so inefficient,
// it could be done better. The compiler does not see that len is limited to 3.
unsafe fn xor_mem(ptr: *mut u8, mask: u32, len: usize) {
    let mut b: u32 = uninitialized();
    #[allow(trivial_casts)]
    copy_nonoverlapping(ptr, &mut b as *mut _ as *mut u8, len);
    b ^= mask;
    #[allow(trivial_casts)]
    copy_nonoverlapping(&b as *const _ as *const u8, ptr, len);
}

#[cfg(test)]
mod tests {
    use std::ptr;
 use super::{apply_mask_fallback, apply_mask_fast32};

    #[test]
    fn test_apply_mask() {
        let mask = [
            0x6d, 0xb6, 0xb2, 0x80,
        ];
        let mask_u32: u32 = unsafe {ptr::read_unaligned(mask.as_ptr() as *const u32)};

        let unmasked = vec![
            0xf3, 0x00, 0x01, 0x02,  0x03, 0x80, 0x81, 0x82,
            0xff, 0xfe, 0x00, 0x17,  0x74, 0xf9, 0x12, 0x03,
        ];

        // Check masking with proper alignment.
        {
            let mut masked = unmasked.clone();
            apply_mask_fallback(&mut masked, &mask);

            let mut masked_fast = unmasked.clone();
            apply_mask_fast32(&mut masked_fast, mask_u32);

            assert_eq!(masked, masked_fast);
        }

        // Check masking without alignment.
        {
            let mut masked = unmasked.clone();
            apply_mask_fallback(&mut masked[1..], &mask);

            let mut masked_fast = unmasked.clone();
            apply_mask_fast32(&mut masked_fast[1..], mask_u32);

            assert_eq!(masked, masked_fast);
        }
    }
}
<commit_msg>aling mask to 8 bytes<commit_after>//! This is code from [Tungstenite project](https://github.com/snapview/tungstenite-rs)
use std::cmp::min;
use std::mem::uninitialized;
use std::ptr::copy_nonoverlapping;

/// Mask/unmask a frame.
#[inline]
pub fn apply_mask(buf: &mut [u8], mask: u32) {
    apply_mask_fast32(buf, mask)
}

/// A safe unoptimized mask application.
#[inline]
#[allow(dead_code)]
fn apply_mask_fallback(buf: &mut [u8], mask: &[u8; 4]) {
    for (i, byte) in buf.iter_mut().enumerate() {
        *byte ^= mask[i & 3];
    }
}

/// Faster version of `apply_mask()` which operates on 8-byte blocks.
#[inline]
#[cfg_attr(feature="cargo-clippy", allow(cast_lossless))]
fn apply_mask_fast32(buf: &mut [u8], mask_u32: u32) {
    let mut ptr = buf.as_mut_ptr();
    let mut len = buf.len();

    // Possible first unaligned block.
    let head = min(len, (8 - (ptr as usize & 0x7)) & 0x3);
    let mask_u32 = if head > 0 {
        let n = if head > 4 { head - 4 } else { head };

        let mask_u32 = if n > 0 {
            unsafe {
                xor_mem(ptr, mask_u32, n);
                ptr = ptr.offset(head as isize);
            }
            len -= n;
            if cfg!(target_endian = "big") {
                mask_u32.rotate_left(8 * n as u32)
            } else {
                mask_u32.rotate_right(8 * n as u32)
            }
        } else {
            mask_u32
        };

        if head > 4 {
            unsafe {
                *(ptr as *mut u32) ^= mask_u32;
                ptr = ptr.offset(4);
                len -= 4;
            }
        }
        mask_u32
    } else {
        mask_u32
    };

    if len > 0 {
        debug_assert_eq!(ptr as usize % 4, 0);
    }

    // Properly aligned middle of the data.
    if len >= 8 {
        let mut mask_u64 = mask_u32 as u64;
        mask_u64 = mask_u64 << 32 | mask_u32 as u64;

        while len >= 8 {
            unsafe {
                *(ptr as *mut u64) ^= mask_u64;
                ptr = ptr.offset(8);
                len -= 8;
            }
        }
    }

    while len >= 4 {
        unsafe {
            *(ptr as *mut u32) ^= mask_u32;
            ptr = ptr.offset(4);
            len -= 4;
        }
    }

    // Possible last block.
    if len > 0 {
        unsafe { xor_mem(ptr, mask_u32, len); }
    }
}

#[inline]
// TODO: copy_nonoverlapping here compiles to call memcpy. While it is not so inefficient,
// it could be done better. The compiler does not see that len is limited to 3.
unsafe fn xor_mem(ptr: *mut u8, mask: u32, len: usize) {
    let mut b: u32 = uninitialized();
    #[allow(trivial_casts)]
    copy_nonoverlapping(ptr, &mut b as *mut _ as *mut u8, len);
    b ^= mask;
    #[allow(trivial_casts)]
    copy_nonoverlapping(&b as *const _ as *const u8, ptr, len);
}

#[cfg(test)]
mod tests {
    use std::ptr;
 use super::{apply_mask_fallback, apply_mask_fast32};

    #[test]
    fn test_apply_mask() {
        let mask = [
            0x6d, 0xb6, 0xb2, 0x80,
        ];
        let mask_u32: u32 = unsafe {ptr::read_unaligned(mask.as_ptr() as *const u32)};

        let unmasked = vec![
            0xf3, 0x00, 0x01, 0x02,  0x03, 0x80, 0x81, 0x82,
            0xff, 0xfe, 0x00, 0x17,  0x74, 0xf9, 0x12, 0x03,
        ];

        // Check masking with proper alignment.
        {
            let mut masked = unmasked.clone();
            apply_mask_fallback(&mut masked, &mask);

            let mut masked_fast = unmasked.clone();
            apply_mask_fast32(&mut masked_fast, mask_u32);

            assert_eq!(masked, masked_fast);
        }

        // Check masking without alignment.
        {
            let mut masked = unmasked.clone();
            apply_mask_fallback(&mut masked[1..], &mask);

            let mut masked_fast = unmasked.clone();
            apply_mask_fast32(&mut masked_fast[1..], mask_u32);

            assert_eq!(masked, masked_fast);
        }
    }
}
<|endoftext|>


<commit_before>use std::io::Read;
use std::env;
use hyper::Client;
use hyper::header::Connection;
use std::collections::BTreeMap;
use rustc_serialize::json;
use std::fs::File;

static BASE_URL:    &'static str = "https://www.googleapis.com/youtube/v3";
lazy_static! {
    static ref API_KEY: String = {
        let opt_key = env::var("YOUTUBE_API_KEY");
        match opt_key {
            Ok(key) => key,
            Err(_) => {
                let mut f = File::open("youtube.txt").unwrap();
                let mut s = String::new();
                let _ = f.read_to_string(&mut s);
                s
            }
        }
    };
}

pub trait HasThumbnail {
    fn get_thumbnails(&self) -> &BTreeMap<String, Thumbnail>;
    fn get_thumbnail_url(&self) -> Option<String> {
        self.get_thumbnails().get(        "default" ).map(|t| t.url.to_string())
            .or(self.get_thumbnails().get("medium"  ).map(|t| t.url.to_string()))
            .or(self.get_thumbnails().get("high"    ).map(|t| t.url.to_string()))
            .or(self.get_thumbnails().get("standard").map(|t| t.url.to_string()))
            .or(self.get_thumbnails().get("maxres"  ).map(|t| t.url.to_string()))
    }
    fn get_artwork_url(&self) -> Option<String> {
        self.get_thumbnails().get(        "maxres"  ).map(|t| t.url.to_string())
            .or(self.get_thumbnails().get("standard").map(|t| t.url.to_string()))
            .or(self.get_thumbnails().get("high"    ).map(|t| t.url.to_string()))
            .or(self.get_thumbnails().get("medium"  ).map(|t| t.url.to_string()))
            .or(self.get_thumbnails().get("default" ).map(|t| t.url.to_string()))
    }
}

#[derive(Debug, Clone, RustcDecodable, RustcEncodable)]
pub struct Thumbnail {
    pub url:    String,
    pub width:  i32,
    pub height: i32
}

impl PartialEq for Thumbnail {
    fn eq(&self, t: &Thumbnail) -> bool {
        return self.url == t.url;
    }
}

#[allow(non_snake_case)]
#[derive(Debug, RustcDecodable, RustcEncodable)]
pub struct PlaylistItemResponse {
    pub kind:          String,
    pub etag:          String,
    pub nextPageToken: String,
    pub pageInfo:      BTreeMap<String, i32>,
    pub items:         Vec<PlaylistItem>,
}

#[allow(non_snake_case)]
#[derive(Debug, RustcDecodable, RustcEncodable)]
pub struct PlaylistItem {
    pub kind:    String,
    pub etag:    String,
    pub id:      String,
    pub snippet: PlaylistItemSnippet,
}

#[allow(non_snake_case)]
#[derive(Debug, RustcDecodable, RustcEncodable)]
pub struct PlaylistItemSnippet {
    pub title:         String,
    pub description:   String,
    pub publishedAt:   String,
    pub channelId:     String,
    pub channelTitle:  String,
    pub thumbnails:    BTreeMap<String, Thumbnail>,
    pub position:      i32,
    pub playlistId:    String,
    pub resourceId:    BTreeMap<String, String>
}

#[allow(non_snake_case)]
#[derive(Debug, RustcDecodable, RustcEncodable)]
pub struct VideoResponse {
    pub kind:          String,
    pub etag:          String,
    pub pageInfo:      BTreeMap<String, i32>,
    pub items:         Vec<Video>,
}

#[allow(non_snake_case)]
#[derive(Debug, Clone, RustcDecodable, RustcEncodable)]
pub struct Video {
    pub kind:    String,
    pub etag:    String,
    pub id:      String,
    pub snippet: VideoSnippet,
}

#[allow(non_snake_case)]
#[derive(Debug, Clone, RustcDecodable, RustcEncodable)]
pub struct VideoSnippet {
    pub title:                String,
    pub description:          String,
    pub publishedAt:          String,
    pub channelId:            String,
    pub channelTitle:         String,
    pub thumbnails:           BTreeMap<String, Thumbnail>,
    pub tags:                 Option<Vec<String>>,
    pub categoryId:           String,
    pub liveBroadcastContent: String,
}

impl HasThumbnail for PlaylistItemSnippet {
    fn get_thumbnails(&self) -> &BTreeMap<String, Thumbnail> {
        &self.thumbnails
    }
}

impl HasThumbnail for VideoSnippet {
    fn get_thumbnails(&self) -> &BTreeMap<String, Thumbnail> {
        &self.thumbnails
    }
}

pub fn fetch_playlist(id: &str) -> json::DecodeResult<PlaylistItemResponse> {
    let params = format!("key={}&part=snippet&playlistId={}", *API_KEY, id);
    let url    = format!("{}/{}?{}", BASE_URL, "playlistItems", params);
    let client = Client::new();
    let mut res = client.get(&url)
                        .header(Connection::close())
                        .send().unwrap();

    let mut body = String::new();
    res.read_to_string(&mut body).unwrap();
    json::decode::<PlaylistItemResponse>(&body)
}

pub fn fetch_video(id: &str) -> json::DecodeResult<Video> {
    let params = format!("key={}&part=snippet&id={}", *API_KEY, id);
    let url    = format!("{}/{}?{}", BASE_URL, "videos", params);
    let client = Client::new();
    let mut res = client.get(&url)
                        .header(Connection::close())
                        .send().unwrap();
    let mut body = String::new();
    res.read_to_string(&mut body).unwrap();
    let res = try!(json::decode::<VideoResponse>(&body));
    if res.items.len() > 0 {
        return Ok(res.items[0].clone());
    }
    Err(json::DecoderError::ApplicationError("track not found".to_string()))
}
<commit_msg>Specify `maxResult` parameter for playlist api<commit_after>use std::io::Read;
use std::env;
use hyper::Client;
use hyper::header::Connection;
use std::collections::BTreeMap;
use rustc_serialize::json;
use std::fs::File;

static BASE_URL:    &'static str = "https://www.googleapis.com/youtube/v3";
static MAX_RESULTS: i32          = 50;
lazy_static! {
    static ref API_KEY: String = {
        let opt_key = env::var("YOUTUBE_API_KEY");
        match opt_key {
            Ok(key) => key,
            Err(_) => {
                let mut f = File::open("youtube.txt").unwrap();
                let mut s = String::new();
                let _ = f.read_to_string(&mut s);
                s
            }
        }
    };
}

pub trait HasThumbnail {
    fn get_thumbnails(&self) -> &BTreeMap<String, Thumbnail>;
    fn get_thumbnail_url(&self) -> Option<String> {
        self.get_thumbnails().get(        "default" ).map(|t| t.url.to_string())
            .or(self.get_thumbnails().get("medium"  ).map(|t| t.url.to_string()))
            .or(self.get_thumbnails().get("high"    ).map(|t| t.url.to_string()))
            .or(self.get_thumbnails().get("standard").map(|t| t.url.to_string()))
            .or(self.get_thumbnails().get("maxres"  ).map(|t| t.url.to_string()))
    }
    fn get_artwork_url(&self) -> Option<String> {
        self.get_thumbnails().get(        "maxres"  ).map(|t| t.url.to_string())
            .or(self.get_thumbnails().get("standard").map(|t| t.url.to_string()))
            .or(self.get_thumbnails().get("high"    ).map(|t| t.url.to_string()))
            .or(self.get_thumbnails().get("medium"  ).map(|t| t.url.to_string()))
            .or(self.get_thumbnails().get("default" ).map(|t| t.url.to_string()))
    }
}

#[derive(Debug, Clone, RustcDecodable, RustcEncodable)]
pub struct Thumbnail {
    pub url:    String,
    pub width:  i32,
    pub height: i32
}

impl PartialEq for Thumbnail {
    fn eq(&self, t: &Thumbnail) -> bool {
        return self.url == t.url;
    }
}

#[allow(non_snake_case)]
#[derive(Debug, RustcDecodable, RustcEncodable)]
pub struct PlaylistItemResponse {
    pub kind:          String,
    pub etag:          String,
    pub nextPageToken: Option<String>,
    pub pageInfo:      BTreeMap<String, i32>,
    pub items:         Vec<PlaylistItem>,
}

#[allow(non_snake_case)]
#[derive(Debug, RustcDecodable, RustcEncodable)]
pub struct PlaylistItem {
    pub kind:    String,
    pub etag:    String,
    pub id:      String,
    pub snippet: PlaylistItemSnippet,
}

#[allow(non_snake_case)]
#[derive(Debug, RustcDecodable, RustcEncodable)]
pub struct PlaylistItemSnippet {
    pub title:         String,
    pub description:   String,
    pub publishedAt:   String,
    pub channelId:     String,
    pub channelTitle:  String,
    pub thumbnails:    BTreeMap<String, Thumbnail>,
    pub position:      i32,
    pub playlistId:    String,
    pub resourceId:    BTreeMap<String, String>
}

#[allow(non_snake_case)]
#[derive(Debug, RustcDecodable, RustcEncodable)]
pub struct VideoResponse {
    pub kind:          String,
    pub etag:          String,
    pub pageInfo:      BTreeMap<String, i32>,
    pub items:         Vec<Video>,
}

#[allow(non_snake_case)]
#[derive(Debug, Clone, RustcDecodable, RustcEncodable)]
pub struct Video {
    pub kind:    String,
    pub etag:    String,
    pub id:      String,
    pub snippet: VideoSnippet,
}

#[allow(non_snake_case)]
#[derive(Debug, Clone, RustcDecodable, RustcEncodable)]
pub struct VideoSnippet {
    pub title:                String,
    pub description:          String,
    pub publishedAt:          String,
    pub channelId:            String,
    pub channelTitle:         String,
    pub thumbnails:           BTreeMap<String, Thumbnail>,
    pub tags:                 Option<Vec<String>>,
    pub categoryId:           String,
    pub liveBroadcastContent: String,
}

impl HasThumbnail for PlaylistItemSnippet {
    fn get_thumbnails(&self) -> &BTreeMap<String, Thumbnail> {
        &self.thumbnails
    }
}

impl HasThumbnail for VideoSnippet {
    fn get_thumbnails(&self) -> &BTreeMap<String, Thumbnail> {
        &self.thumbnails
    }
}

pub fn fetch_playlist(id: &str) -> json::DecodeResult<PlaylistItemResponse> {
    let params = format!("key={}&part=snippet&playlistId={}&maxResults={}",
                         *API_KEY,
                         id,
                         MAX_RESULTS);
    let url    = format!("{}/{}?{}", BASE_URL, "playlistItems", params);
    let client = Client::new();
    let mut res = client.get(&url)
                        .header(Connection::close())
                        .send().unwrap();
    let mut body = String::new();
    res.read_to_string(&mut body).unwrap();
    json::decode::<PlaylistItemResponse>(&body)
}

pub fn fetch_video(id: &str) -> json::DecodeResult<Video> {
    let params = format!("key={}&part=snippet&id={}", *API_KEY, id);
    let url    = format!("{}/{}?{}", BASE_URL, "videos", params);
    let client = Client::new();
    let mut res = client.get(&url)
                        .header(Connection::close())
                        .send().unwrap();
    let mut body = String::new();
    res.read_to_string(&mut body).unwrap();
    let res = try!(json::decode::<VideoResponse>(&body));
    if res.items.len() > 0 {
        return Ok(res.items[0].clone());
    }
    Err(json::DecoderError::ApplicationError("track not found".to_string()))
}
<|endoftext|>