<commit_before>extern crate mmap;

use std::ptr;
use std::fs::OpenOptions;
use std::os::unix::fs::OpenOptionsExt;
use mmap::{MemoryMap, MapOption};
use std::os::unix::prelude::AsRawFd;

const BCM2708_PERI_BASE: u32 = 0x20000000;
const GPIO_BASE: u8 = (BCM2708_PERI_BASE + 0x200000) as u8;
const O_SYNC: u32 = 1052672;
const MAP_SHARED: i32 = 0x0001;
const BLOCK_SIZE: usize = (4*1024);

#[allow(dead_code)]
struct Bcm2835Peripheral {
    addr_p: *const u8,
    mem_fd: std::fs::File,
    map: mmap::MemoryMap,
    addr: *mut u8
}

fn main() {
    let mut gpio = Bcm2835Peripheral { addr_p: &GPIO_BASE, mem_fd: OpenOptions::new().create(true).open("temp.txt").unwrap(), map: MemoryMap::new(1024, &[]).unwrap(), addr: ptr::null_mut()};
    map_peripheral(&mut gpio);
    println!("{:?}", gpio.map.data());
    unmap_peripheral(&gpio);
}

fn map_peripheral(ref mut foo: &mut Bcm2835Peripheral) {
    let file = OpenOptions::new()
                    .read(true)
                    .write(true)
                    .mode(O_SYNC)
                    .open("/dev/mem")
                    .expect("unable to open /dev/mem, Are you root?");

    let map_opts = &[
        MapOption::MapNonStandardFlags(MAP_SHARED),
        MapOption::MapReadable,
        MapOption::MapWritable,
       // MapOption::MapAddr(foo.addr_p),
        MapOption::MapFd(file.as_raw_fd())
    ];

   let mmap = match MemoryMap::new(BLOCK_SIZE, map_opts) {
     Ok(mmap) => mmap,
     Err(e) => panic!("ERR: {}", e)
    };
    foo.map = mmap;
    foo.addr = foo.map.data();
    println!("{:?}", foo.map.data());
}

fn unmap_peripheral(foo: &Bcm2835Peripheral) {
    drop(&foo.mem_fd);
    drop(&foo.map);
    println!("{:?}", &foo.map.data());
}

fn in_gpio(foo: &Bcm2835Peripheral, y: isize) {
  unsafe {
      let k = &foo.addr.offset(y/10);
      **k &= (7<<(((y)%10)*3));
  }
}
<commit_msg>add 2 more gpio functions<commit_after>extern crate mmap;

use std::ptr;
use std::fs::OpenOptions;
use std::os::unix::fs::OpenOptionsExt;
use mmap::{MemoryMap, MapOption};
use std::os::unix::prelude::AsRawFd;

const BCM2708_PERI_BASE: u32 = 0x20000000;
const GPIO_BASE: u8 = (BCM2708_PERI_BASE + 0x200000) as u8;
const O_SYNC: u32 = 1052672;
const MAP_SHARED: i32 = 0x0001;
const BLOCK_SIZE: usize = (4*1024);

#[allow(dead_code)]
struct Bcm2835Peripheral {
    addr_p: *const u8,
    mem_fd: std::fs::File,
    map: mmap::MemoryMap,
    addr: *mut u8
}

fn main() {
    let mut gpio = Bcm2835Peripheral { addr_p: &GPIO_BASE, mem_fd: OpenOptions::new().create(true).open("temp.txt").unwrap(), map: MemoryMap::new(1024, &[]).unwrap(), addr: ptr::null_mut()};
    map_peripheral(&mut gpio);
    unmap_peripheral(&gpio);
}

fn map_peripheral(ref mut foo: &mut Bcm2835Peripheral) {
    foo.mem_fd = OpenOptions::new()
                    .read(true)
                    .write(true)
                    .mode(O_SYNC)
                    .open("/dev/mem")
                    .expect("unable to open /dev/mem, Are you root?");

    let map_opts = &[
        MapOption::MapNonStandardFlags(MAP_SHARED),
        MapOption::MapReadable,
        MapOption::MapWritable,
       // MapOption::MapAddr(foo.addr_p),
        MapOption::MapFd(foo.mem_fd.as_raw_fd())
    ];

   let mmap = match MemoryMap::new(BLOCK_SIZE, map_opts) {
     Ok(mmap) => mmap,
     Err(e) => panic!("ERR: {}", e)
    };
    foo.map = mmap;
    foo.addr = foo.map.data();
    println!("{:?}", foo.map.data());
}

fn unmap_peripheral(foo: &Bcm2835Peripheral) {
    drop(&foo.mem_fd);
    drop(&foo.map);
    println!("{:?}", &foo.map.data());
}

fn in_gpio(foo: &Bcm2835Peripheral, y: isize) {
  unsafe {
      let k = &foo.addr.offset(y/10);
      **k &= !(7<<(((y)%10)*3));
  }
}

fn out_gpio(foo: &Bcm2835Peripheral, y:isize) {
  unsafe {
      let k = &foo.addr.offset(y/10);
      **k |= (7<<(((y)%10)*3));
  }
}

fn set_gpio_alt(foo: &Bcm2835Peripheral, y:isize, a:u8) {
    unsafe {
        let k = &foo.addr.offset(y/10);
        **k |= match a {
            a if a<=3 => a+4,
            4         => 3,
            _         => 2,
        } << ((y % 10) * 3);
    }
}
<|endoftext|>


<commit_before>#![feature(io, slice_patterns, vec_resize)]
use std::collections::BTreeMap;
use std::fs;
use std::io;
use std::io::{Read, Write};
use std::path::Path;
use std::thread;

#[derive(Debug)]
enum Op {
    Add(u8),
    Mov(isize),
    In,
    Out,
    Loop(OpStream),

    // extra optimized ops
    Transfer(u8, Vec<(isize, u8)>)
}

use Op::*;

#[derive(Debug)]
struct OpStream {
    ops: Vec<Op>
}

impl OpStream {
    fn add(&mut self, op: Op) {
        self.ops.push(op);
    }

    fn optimize(&mut self) {
        let mut i = 0;
        while i < self.ops.len() {
            match &self.ops[i..] {
                [Add(a), Add(b), ..] => {
                    self.ops[i] = Add(a.wrapping_add(b));
                    self.ops.remove(i + 1);
                }
                [Mov(a), Mov(b), ..] => {
                    self.ops[i] = Mov(a + b);
                    self.ops.remove(i + 1);
                }
                [Add(0), ..] | [Mov(0), ..] => {
                    self.ops.remove(i);
                }
                [Loop(_), ..] => {
                    let mut maybe_new_op = None;

                    if let &mut Loop(ref mut stream) = &mut self.ops[i] {
                        stream.optimize();
                        maybe_new_op = stream.find_alternative();
                    }

                    if let Some(new_op) = maybe_new_op {
                        self.ops[i] = new_op;
                    }

                    i += 1
                }
                _ => i += 1
            }
        }
    }

    fn find_alternative(&self) -> Option<Op> {
        let mut map: BTreeMap<isize, u8> = BTreeMap::new();
        let mut rel_index = 0;

        for op in &self.ops[..] {
            match op {
                &Add(x) => {
                    let new_val = map.get(&rel_index).unwrap_or(&0).wrapping_add(x);
                    map.insert(rel_index, new_val);
                }
                &Mov(x) => {
                    rel_index += x;
                }
                _ => {
                    return None;
                }
            }
        }

        if rel_index != 0 {
            return None;
        }

        map.remove(&0).map(|summand| Transfer(summand, map.into_iter().collect()))
    }

    fn get(&self) -> &[Op] {
        &self.ops[..]
    }

    fn new() -> OpStream {
        OpStream { ops: Vec::new() }
    }
}

static ZERO: u8 = 0;

struct State {
    index: usize,
    memory: Vec<u8>
}

impl State {
    fn new() -> State {
        State { index: 0, memory: vec![] }
    }

    fn rel_index(&self, relative: isize) -> usize {
        (self.index as isize + relative) as usize
    }

    fn step(&mut self, op: &Op) -> bool {
        match op {
            &Add(i) => {
                self[0] = self[0].wrapping_add(i);
            }
            &Mov(n) => {
                self.index = self.rel_index(n);
            }
            &In => {
                let mut c = [0u8];
                if io::stdin().read(&mut c).unwrap() == 0 {
                    return false;
                }
                self[0] = c[0];
            }
            &Out => {
                io::stdout().write(&[self[0]]).unwrap();
            }
            &Loop(ref ops) => {
                while self[0] != 0 {
                    if !self.run(ops.get()) {
                        return false;
                    }
                }
            }
            &Transfer(d, ref map) => {
                if self[0] == 0 {
                    return true
                }

                let mut _v = self[0];
                let mut _n = 0;

                while _v != 0 {
                    _v = _v.wrapping_add(d);
                    if _v == self[0] {
                        // stalled: the current transfer will never complete
                        loop {
                            thread::park()
                        }
                    }
                    _n += 1
                }

                self[0] = 0;
                for &(k, v) in &map[..] {
                    self[k] = self[k].wrapping_add(v.wrapping_mul(_n));
                }
            }
        }
        return true;
    }

    fn run(&mut self, ops: &[Op]) -> bool {
        for op in ops {
            if !self.step(op) {
                return false;
            }
        }
        return true;
    }
}

impl std::ops::Index<isize> for State {
    type Output = u8;
    fn index(&self, _index: isize) -> &u8 {
        let idx = self.rel_index(_index);
        if idx >= self.memory.len() {
            &ZERO
        } else {
            &self.memory[idx]
        }
    }
}

impl std::ops::IndexMut<isize> for State {
    fn index_mut(&mut self, _index: isize) -> &mut u8 {
        let idx = self.rel_index(_index);
        if idx >= self.memory.len() {
            self.memory.resize(idx * 2 + 1, 0);
        }
        &mut self.memory[idx]
    }
}

fn main() {
    let filenames: Vec<String> = std::iter::FromIterator::from_iter(std::env::args());

    for filename in &filenames[1..] {
        match reader(&Path::new(filename)) {
            Ok(reader) => {
                let mut chars = reader.chars();

                let mut opstream = OpStream::new();
                while let Some(op) = parse(&mut chars, false) {
                    opstream.add(op);
                }

                opstream.optimize();

                State::new().run(opstream.get());
            }
            Err(e) => {
                writeln!(&mut io::stderr(), "Error while processing {}: {}", filename, e).unwrap();
            }
        }
    }
}

fn parse<T: io::Read>(mut chars: &mut io::Chars<T>, in_a_loop: bool) -> Option<Op> {
    loop {
        return match chars.next() {
            Some(Ok(c)) => match c {
                '+' => Some(Add(0x01)),
                '-' => Some(Add(0xff)),
                '<' => Some(Mov(-1)),
                '>' => Some(Mov(1)),
                ',' => Some(In),
                '.' => Some(Out),
                '[' => {
                    let mut childstream = OpStream::new();
                    while let Some(op) = parse(&mut chars, true) {
                        childstream.add(op);
                    }
                    Some(Loop(childstream))
                }
                ']' => {
                    if in_a_loop {
                        None
                    } else {
                        panic!("Stray loop terminator")
                    }
                }
                _ => continue
            },
            Some(e @ Err(_)) => panic!("{:?}", e),
            None => {
                if in_a_loop {
                    panic!("EOF within a loop")
                } else {
                    None
                }
            }
        }
    }
}

fn reader(path: &Path) -> Result<io::BufReader<fs::File>, io::Error> {
    fs::File::open(path).map(|f| io::BufReader::new(f))
}

#[cfg(test)]
mod tests {
    use std::io;
    use std::io::Read;
    use super::{parse, State, OpStream};
    use super::Op::*;

    macro_rules! assert_let(
        ($test:pat, $value:expr, $then:block) => (
            if let $test = $value {
                $then
            } else {
                panic!("{:?} doesn't match {}", $value, stringify!($test))
            }
        );
        ($test:pat, $value:expr) => (
            assert_let!($test, $value, {})
        );
    );

    #[test]
    fn test_state_index() {
        let mut state = State { index: 0, memory: vec![23, 0, 0, 0, 0, 42] };
        assert_eq!(23, state[0]);
        state.index = 5;
        assert_eq!(42, state[0]);
    }

    #[test]
    fn test_state_index_mut() {
        let mut state = State::new();
        state[0] = 23;
        assert_eq!(23, state.memory[state.index]);
        state.index = 5;
        state[0] = 42;
        assert_eq!(42, state.memory[state.index]);
    }

    #[test]
    fn test_state_step_add() {
        let mut state = State::new();
        state.step(&Add(23));
        assert_eq!(23, state[0]);
        state.step(&Add(42));
        assert_eq!(65, state[0]);
        state.step(&Add(190));
        assert_eq!(255, state[0]);
    }

    #[test]
    fn test_state_step_mov() {
        let mut state = State::new();
        state.step(&Mov(1));
        assert_eq!(1, state.index);
        state.step(&Mov(42));
        assert_eq!(43, state.index);
        state.step(&Mov(-1));
        assert_eq!(42, state.index);
    }

    #[test]
    fn test_state_step_loop() {
        let mut state = State::new();
        state[0] = 23;
        state.step(&Loop(OpStream { ops: vec![Add(1)] }));
        assert_eq!(0, state[0]);
    }

    #[test]
    fn test_state_step_transfer() {
        let mut state = State::new();
        state[0] = 15;
        state[1] = 7;
        state.step(&Transfer(5, vec![(1, 2)]));
        assert_eq!(0, state[0]);
        assert_eq!(1, state[1]);
    }

    #[test]
    fn test_opstream_new_empty() {
        let opstream = OpStream::new();
        assert_eq!(0, opstream.ops.len());
    }

    #[test]
    fn test_opstream_optimize() {
        let mut opstream = OpStream::new();
        opstream.add(Mov(1));
        opstream.add(Mov(1));
        opstream.add(Add(0x01));
        opstream.add(Add(0xff));
        opstream.add(Add(0xff));
        opstream.add(Mov(1));
        opstream.add(Mov(-1));
        let mut opstream2 = OpStream::new();
        opstream2.add(Mov(2));
        opstream2.add(Mov(3));
        opstream.add(Loop(opstream2));
        opstream.optimize();

        assert_let!([Mov(2), Add(0xff), Loop(ref s2)], &opstream.ops[..], {
            assert_let!([Mov(5)], &s2.ops[..]);
        });
    }

    #[test]
    fn test_parse() {
        let input = b"+-[+.,]+";
        let mut chars = io::BufReader::new(&input[..]).chars();
        assert_let!(Some(Add(0x01)), parse(&mut chars, false));
        assert_let!(Some(Add(0xff)), parse(&mut chars, false));
        assert_let!(Some(Loop(loop_op)), parse(&mut chars, false), {
            assert_let!([Add(1), Out, In], &loop_op.ops[..]);
        });
        assert_let!(Some(Add(1)), parse(&mut chars, false));
    }
}
<commit_msg>fix: don't skip elements when removing while optimizing<commit_after>#![feature(io, slice_patterns, vec_resize)]
use std::collections::BTreeMap;
use std::fs;
use std::io;
use std::io::{Read, Write};
use std::path::Path;
use std::thread;

#[derive(Debug)]
enum Op {
    Add(u8),
    Mov(isize),
    In,
    Out,
    Loop(OpStream),

    // extra optimized ops
    Transfer(u8, Vec<(isize, u8)>)
}

use Op::*;

#[derive(Debug)]
struct OpStream {
    ops: Vec<Op>
}

impl OpStream {
    fn add(&mut self, op: Op) {
        self.ops.push(op);
    }

    fn optimize(&mut self) {
        let mut i = 0;
        while i < self.ops.len() {
            match &self.ops[i..] {
                [Add(a), Add(b), ..] => {
                    self.ops[i] = Add(a.wrapping_add(b));
                    self.ops.remove(i + 1);
                }
                [Mov(a), Mov(b), ..] => {
                    self.ops[i] = Mov(a + b);
                    self.ops.remove(i + 1);
                }
                [Add(0), ..] | [Mov(0), ..] => {
                    self.ops.remove(i);
                    if i > 0 {
                        i -= 1;
                    }
                }
                [Loop(_), ..] => {
                    let mut maybe_new_op = None;

                    if let &mut Loop(ref mut stream) = &mut self.ops[i] {
                        stream.optimize();
                        maybe_new_op = stream.find_alternative();
                    }

                    if let Some(new_op) = maybe_new_op {
                        self.ops[i] = new_op;
                    }

                    i += 1
                }
                _ => i += 1
            }
        }
    }

    fn find_alternative(&self) -> Option<Op> {
        let mut map: BTreeMap<isize, u8> = BTreeMap::new();
        let mut rel_index = 0;

        for op in &self.ops[..] {
            match op {
                &Add(x) => {
                    let new_val = map.get(&rel_index).unwrap_or(&0).wrapping_add(x);
                    map.insert(rel_index, new_val);
                }
                &Mov(x) => {
                    rel_index += x;
                }
                _ => {
                    return None;
                }
            }
        }

        if rel_index != 0 {
            return None;
        }

        map.remove(&0).map(|summand| Transfer(summand, map.into_iter().collect()))
    }

    fn get(&self) -> &[Op] {
        &self.ops[..]
    }

    fn new() -> OpStream {
        OpStream { ops: Vec::new() }
    }
}

static ZERO: u8 = 0;

struct State {
    index: usize,
    memory: Vec<u8>
}

impl State {
    fn new() -> State {
        State { index: 0, memory: vec![] }
    }

    fn rel_index(&self, relative: isize) -> usize {
        (self.index as isize + relative) as usize
    }

    fn step(&mut self, op: &Op) -> bool {
        match op {
            &Add(i) => {
                self[0] = self[0].wrapping_add(i);
            }
            &Mov(n) => {
                self.index = self.rel_index(n);
            }
            &In => {
                let mut c = [0u8];
                if io::stdin().read(&mut c).unwrap() == 0 {
                    return false;
                }
                self[0] = c[0];
            }
            &Out => {
                io::stdout().write(&[self[0]]).unwrap();
            }
            &Loop(ref ops) => {
                while self[0] != 0 {
                    if !self.run(ops.get()) {
                        return false;
                    }
                }
            }
            &Transfer(d, ref map) => {
                if self[0] == 0 {
                    return true
                }

                let mut _v = self[0];
                let mut _n = 0;

                while _v != 0 {
                    _v = _v.wrapping_add(d);
                    if _v == self[0] {
                        // stalled: the current transfer will never complete
                        loop {
                            thread::park()
                        }
                    }
                    _n += 1
                }

                self[0] = 0;
                for &(k, v) in &map[..] {
                    self[k] = self[k].wrapping_add(v.wrapping_mul(_n));
                }
            }
        }
        return true;
    }

    fn run(&mut self, ops: &[Op]) -> bool {
        for op in ops {
            if !self.step(op) {
                return false;
            }
        }
        return true;
    }
}

impl std::ops::Index<isize> for State {
    type Output = u8;
    fn index(&self, _index: isize) -> &u8 {
        let idx = self.rel_index(_index);
        if idx >= self.memory.len() {
            &ZERO
        } else {
            &self.memory[idx]
        }
    }
}

impl std::ops::IndexMut<isize> for State {
    fn index_mut(&mut self, _index: isize) -> &mut u8 {
        let idx = self.rel_index(_index);
        if idx >= self.memory.len() {
            self.memory.resize(idx * 2 + 1, 0);
        }
        &mut self.memory[idx]
    }
}

fn main() {
    let filenames: Vec<String> = std::iter::FromIterator::from_iter(std::env::args());

    for filename in &filenames[1..] {
        match reader(&Path::new(filename)) {
            Ok(reader) => {
                let mut chars = reader.chars();

                let mut opstream = OpStream::new();
                while let Some(op) = parse(&mut chars, false) {
                    opstream.add(op);
                }

                opstream.optimize();

                State::new().run(opstream.get());
            }
            Err(e) => {
                writeln!(&mut io::stderr(), "Error while processing {}: {}", filename, e).unwrap();
            }
        }
    }
}

fn parse<T: io::Read>(mut chars: &mut io::Chars<T>, in_a_loop: bool) -> Option<Op> {
    loop {
        return match chars.next() {
            Some(Ok(c)) => match c {
                '+' => Some(Add(0x01)),
                '-' => Some(Add(0xff)),
                '<' => Some(Mov(-1)),
                '>' => Some(Mov(1)),
                ',' => Some(In),
                '.' => Some(Out),
                '[' => {
                    let mut childstream = OpStream::new();
                    while let Some(op) = parse(&mut chars, true) {
                        childstream.add(op);
                    }
                    Some(Loop(childstream))
                }
                ']' => {
                    if in_a_loop {
                        None
                    } else {
                        panic!("Stray loop terminator")
                    }
                }
                _ => continue
            },
            Some(e @ Err(_)) => panic!("{:?}", e),
            None => {
                if in_a_loop {
                    panic!("EOF within a loop")
                } else {
                    None
                }
            }
        }
    }
}

fn reader(path: &Path) -> Result<io::BufReader<fs::File>, io::Error> {
    fs::File::open(path).map(|f| io::BufReader::new(f))
}

#[cfg(test)]
mod tests {
    use std::io;
    use std::io::Read;
    use super::{parse, State, OpStream};
    use super::Op::*;

    macro_rules! assert_let(
        ($test:pat, $value:expr, $then:block) => (
            if let $test = $value {
                $then
            } else {
                panic!("{:?} doesn't match {}", $value, stringify!($test))
            }
        );
        ($test:pat, $value:expr) => (
            assert_let!($test, $value, {})
        );
    );

    #[test]
    fn test_state_index() {
        let mut state = State { index: 0, memory: vec![23, 0, 0, 0, 0, 42] };
        assert_eq!(23, state[0]);
        state.index = 5;
        assert_eq!(42, state[0]);
    }

    #[test]
    fn test_state_index_mut() {
        let mut state = State::new();
        state[0] = 23;
        assert_eq!(23, state.memory[state.index]);
        state.index = 5;
        state[0] = 42;
        assert_eq!(42, state.memory[state.index]);
    }

    #[test]
    fn test_state_step_add() {
        let mut state = State::new();
        state.step(&Add(23));
        assert_eq!(23, state[0]);
        state.step(&Add(42));
        assert_eq!(65, state[0]);
        state.step(&Add(190));
        assert_eq!(255, state[0]);
    }

    #[test]
    fn test_state_step_mov() {
        let mut state = State::new();
        state.step(&Mov(1));
        assert_eq!(1, state.index);
        state.step(&Mov(42));
        assert_eq!(43, state.index);
        state.step(&Mov(-1));
        assert_eq!(42, state.index);
    }

    #[test]
    fn test_state_step_loop() {
        let mut state = State::new();
        state[0] = 23;
        state.step(&Loop(OpStream { ops: vec![Add(1)] }));
        assert_eq!(0, state[0]);
    }

    #[test]
    fn test_state_step_transfer() {
        let mut state = State::new();
        state[0] = 15;
        state[1] = 7;
        state.step(&Transfer(5, vec![(1, 2)]));
        assert_eq!(0, state[0]);
        assert_eq!(1, state[1]);
    }

    #[test]
    fn test_opstream_new_empty() {
        let opstream = OpStream::new();
        assert_eq!(0, opstream.ops.len());
    }

    #[test]
    fn test_opstream_optimize() {
        let mut opstream = OpStream::new();
        opstream.add(Mov(1));
        opstream.add(Mov(1));
        opstream.add(Add(0x01));
        opstream.add(Add(0xff));
        opstream.add(Add(0xff));
        opstream.add(Mov(1));
        opstream.add(Mov(-1));
        let mut opstream2 = OpStream::new();
        opstream2.add(Mov(2));
        opstream2.add(Mov(3));
        opstream.add(Loop(opstream2));
        opstream.optimize();

        assert_let!([Mov(2), Add(0xff), Loop(ref s2)], &opstream.ops[..], {
            assert_let!([Mov(5)], &s2.ops[..]);
        });
    }

    #[test]
    fn test_parse() {
        let input = b"+-[+.,]+";
        let mut chars = io::BufReader::new(&input[..]).chars();
        assert_let!(Some(Add(0x01)), parse(&mut chars, false));
        assert_let!(Some(Add(0xff)), parse(&mut chars, false));
        assert_let!(Some(Loop(loop_op)), parse(&mut chars, false), {
            assert_let!([Add(1), Out, In], &loop_op.ops[..]);
        });
        assert_let!(Some(Add(1)), parse(&mut chars, false));
    }
}
<|endoftext|>


<commit_before>use encoding_rs::Encoding;
use html5ever::rcdom::RcDom;
use reqwest::blocking::Client;
use reqwest::header::{HeaderMap, HeaderValue, USER_AGENT};
use std::collections::HashMap;
use std::fs;
use std::io::{self, prelude::*, Error, Write};
use std::path::Path;
use std::process;
use std::time::Duration;
use url::Url;

use monolith::html::{
    add_favicon, create_metadata_tag, get_base_url, get_charset, has_favicon, html_to_dom,
    serialize_document, set_base_url, set_charset, walk_and_embed_assets,
};
use monolith::opts::Options;
use monolith::url::{create_data_url, resolve_url};
use monolith::utils::retrieve_asset;

mod macros;

enum Output {
    Stdout(io::Stdout),
    File(fs::File),
}

impl Output {
    fn new(file_path: &str) -> Result<Output, Error> {
        if file_path.is_empty() || file_path.eq("-") {
            Ok(Output::Stdout(io::stdout()))
        } else {
            Ok(Output::File(fs::File::create(file_path)?))
        }
    }

    fn write(&mut self, bytes: &Vec<u8>) -> Result<(), Error> {
        match self {
            Output::Stdout(stdout) => {
                stdout.write_all(bytes)?;
                // Ensure newline at end of output
                if bytes.last() != Some(&b"\n"[0]) {
                    stdout.write(b"\n")?;
                }
                stdout.flush()
            }
            Output::File(file) => {
                file.write_all(bytes)?;
                // Ensure newline at end of output
                if bytes.last() != Some(&b"\n"[0]) {
                    file.write(b"\n")?;
                }
                file.flush()
            }
        }
    }
}

pub fn read_stdin() -> Vec<u8> {
    let mut buffer: Vec<u8> = vec![];

    match io::stdin().lock().read_to_end(&mut buffer) {
        Ok(_) => buffer,
        Err(_) => buffer,
    }
}

fn main() {
    let options = Options::from_args();
    let mut target: String = str!(&options.target.clone());

    // Check if target was provided
    if target.len() == 0 {
        if !options.silent {
            eprintln!("No target specified");
        }
        process::exit(1);
    }

    // Check if custom charset is valid
    if let Some(custom_charset) = options.charset.clone() {
        if !Encoding::for_label_no_replacement(custom_charset.as_bytes()).is_some() {
            eprintln!("Unknown encoding: {}", &custom_charset);
            process::exit(1);
        }
    }

    let target_url: Url;
    let mut use_stdin: bool = false;

    // Determine exact target URL
    if target.clone() == "-" {
        // Read from pipe (stdin)
        use_stdin = true;
        // Set default target URL to an empty data URL; the user can set it via --base-url
        target_url = Url::parse("data:text/html,").unwrap();
    } else {
        match Url::parse(&target.clone()) {
            Ok(parsed_url) => {
                if parsed_url.scheme() == "data"
                    || parsed_url.scheme() == "file"
                    || (parsed_url.scheme() == "http" || parsed_url.scheme() == "https")
                {
                    target_url = parsed_url;
                } else {
                    if !options.silent {
                        eprintln!("Unsupported target URL type: {}", &parsed_url.scheme());
                    }
                    process::exit(1);
                }
            }
            Err(_err) => {
                // Failed to parse given base URL,
                // perhaps it's a filesystem path?
                let path: &Path = Path::new(&target);

                if path.exists() {
                    if path.is_file() {
                        match Url::from_file_path(fs::canonicalize(&path).unwrap()) {
                            Ok(file_url) => {
                                target_url = file_url;
                            }
                            Err(_err) => {
                                if !options.silent {
                                    eprintln!(
                                        "Could not generate file URL out of given path: {}",
                                        "err"
                                    );
                                }
                                process::exit(1);
                            }
                        }
                    } else {
                        if !options.silent {
                            eprintln!("Local target is not a file: {}", &options.target);
                        }
                        process::exit(1);
                    }
                } else {
                    // Last chance, now we do what browsers do:
                    // prepend "http://" and hope it points to a website
                    target.insert_str(0, "http://");
                    target_url = Url::parse(&target).unwrap();
                }
            }
        }
    }

    // Initialize client
    let mut cache = HashMap::new();
    let mut header_map = HeaderMap::new();
    if let Some(user_agent) = &options.user_agent {
        header_map.insert(
            USER_AGENT,
            HeaderValue::from_str(&user_agent).expect("Invalid User-Agent header specified"),
        );
    }
    let client = if options.timeout > 0 {
        Client::builder().timeout(Duration::from_secs(options.timeout))
    } else {
        // No timeout is default
        Client::builder()
    }
    .danger_accept_invalid_certs(options.insecure)
    .default_headers(header_map)
    .build()
    .expect("Failed to initialize HTTP client");

    // At first we assume that base URL is the same as target URL
    let mut base_url: Url = target_url.clone();

    let data: Vec<u8>;
    let mut document_encoding: String = str!();
    let mut dom: RcDom;

    // Retrieve target document
    if use_stdin {
        data = read_stdin();
    } else if target_url.scheme() == "file"
        || (target_url.scheme() == "http" || target_url.scheme() == "https")
        || target_url.scheme() == "data"
    {
        match retrieve_asset(&mut cache, &client, &target_url, &target_url, &options, 0) {
            Ok((retrieved_data, final_url, media_type, charset)) => {
                // Make sure the media type is text/html
                if !media_type.eq_ignore_ascii_case("text/html") {
                    if !options.silent {
                        eprintln!("Unsupported document media type");
                    }
                    process::exit(1);
                }

                if options.base_url.clone().unwrap_or(str!()).is_empty() {
                    base_url = final_url;
                }

                data = retrieved_data;
                document_encoding = charset;
            }
            Err(_) => {
                if !options.silent {
                    eprintln!("Could not retrieve target document");
                }
                process::exit(1);
            }
        }
    } else {
        process::exit(1);
    }

    // Initial parse
    dom = html_to_dom(&data, document_encoding.clone());

    // TODO: investigate if charset from filesystem/data URL/HTTP headers
    //       has say over what's specified in HTML

    // Attempt to determine document's charset
    if let Some(html_charset) = get_charset(&dom.document) {
        if !html_charset.is_empty() {
            // Check if the charset specified inside HTML is valid
            if let Some(encoding) = Encoding::for_label_no_replacement(html_charset.as_bytes()) {
                // No point in parsing HTML again with the same encoding as before
                if encoding.name() != "UTF-8" {
                    document_encoding = html_charset;
                    dom = html_to_dom(&data, document_encoding.clone());
                }
            }
        }
    }

    // Use custom base URL if specified, read and use what's in the DOM otherwise
    let custom_base_url: String = options.base_url.clone().unwrap_or(str!());
    if custom_base_url.is_empty() {
        // No custom base URL is specified
        // Try to see if document has BASE element
        if let Some(existing_base_url) = get_base_url(&dom.document) {
            base_url = resolve_url(&target_url, &existing_base_url);
        }
    } else {
        // Custom base URL provided
        match Url::parse(&custom_base_url) {
            Ok(parsed_url) => {
                if parsed_url.scheme() == "file" {
                    // File base URLs can only work with
                    // documents saved from filesystem
                    if target_url.scheme() == "file" {
                        base_url = parsed_url;
                    }
                } else {
                    base_url = parsed_url;
                }
            }
            Err(_) => {
                // Failed to parse given base URL, perhaps it's a filesystem path?
                if target_url.scheme() == "file" {
                    // Relative paths could work for documents saved from filesystem
                    let path: &Path = Path::new(&custom_base_url);
                    if path.exists() {
                        match Url::from_file_path(fs::canonicalize(&path).unwrap()) {
                            Ok(file_url) => {
                                base_url = file_url;
                            }
                            Err(_) => {
                                if !options.silent {
                                    eprintln!(
                                        "Could not map given path to base URL: {}",
                                        custom_base_url
                                    );
                                }
                                process::exit(1);
                            }
                        }
                    }
                }
            }
        }
    }

    // Traverse through the document and embed remote assets
    walk_and_embed_assets(&mut cache, &client, &base_url, &dom.document, &options, 0);

    // Update or add new BASE element to reroute network requests and hash-links
    if let Some(new_base_url) = options.base_url.clone() {
        dom = set_base_url(&dom.document, new_base_url);
    }

    // Request and embed /favicon.ico (unless it's already linked in the document)
    if !options.no_images
        && (target_url.scheme() == "http" || target_url.scheme() == "https")
        && !has_favicon(&dom.document)
    {
        let favicon_ico_url: Url = resolve_url(&base_url, "/favicon.ico");

        match retrieve_asset(
            &mut cache,
            &client,
            &target_url,
            &favicon_ico_url,
            &options,
            0,
        ) {
            Ok((data, final_url, media_type, charset)) => {
                let favicon_data_url: Url =
                    create_data_url(&media_type, &charset, &data, &final_url);
                dom = add_favicon(&dom.document, favicon_data_url.to_string());
            }
            Err(_) => {
                // Failed to retrieve /favicon.ico
            }
        }
    }

    // Save using specified charset, if given
    if let Some(custom_charset) = options.charset.clone() {
        document_encoding = custom_charset;
        dom = set_charset(dom, document_encoding.clone());
    }

    // Serialize DOM tree
    let mut result: Vec<u8> = serialize_document(dom, document_encoding, &options);

    // Prepend metadata comment tag
    if !options.no_metadata {
        let mut metadata_comment: String = create_metadata_tag(&target_url);
        metadata_comment += "\n";
        result.splice(0..0, metadata_comment.as_bytes().to_vec());
    }

    // Define output
    let mut output = Output::new(&options.output).expect("Could not prepare output");

    // Write result into stdout or file
    output.write(&result).expect("Could not write HTML output");
}
<commit_msg>fix use of wrong charset<commit_after>use encoding_rs::Encoding;
use html5ever::rcdom::RcDom;
use reqwest::blocking::Client;
use reqwest::header::{HeaderMap, HeaderValue, USER_AGENT};
use std::collections::HashMap;
use std::fs;
use std::io::{self, prelude::*, Error, Write};
use std::path::Path;
use std::process;
use std::time::Duration;
use url::Url;

use monolith::html::{
    add_favicon, create_metadata_tag, get_base_url, get_charset, has_favicon, html_to_dom,
    serialize_document, set_base_url, set_charset, walk_and_embed_assets,
};
use monolith::opts::Options;
use monolith::url::{create_data_url, resolve_url};
use monolith::utils::retrieve_asset;

mod macros;

enum Output {
    Stdout(io::Stdout),
    File(fs::File),
}

impl Output {
    fn new(file_path: &str) -> Result<Output, Error> {
        if file_path.is_empty() || file_path.eq("-") {
            Ok(Output::Stdout(io::stdout()))
        } else {
            Ok(Output::File(fs::File::create(file_path)?))
        }
    }

    fn write(&mut self, bytes: &Vec<u8>) -> Result<(), Error> {
        match self {
            Output::Stdout(stdout) => {
                stdout.write_all(bytes)?;
                // Ensure newline at end of output
                if bytes.last() != Some(&b"\n"[0]) {
                    stdout.write(b"\n")?;
                }
                stdout.flush()
            }
            Output::File(file) => {
                file.write_all(bytes)?;
                // Ensure newline at end of output
                if bytes.last() != Some(&b"\n"[0]) {
                    file.write(b"\n")?;
                }
                file.flush()
            }
        }
    }
}

pub fn read_stdin() -> Vec<u8> {
    let mut buffer: Vec<u8> = vec![];

    match io::stdin().lock().read_to_end(&mut buffer) {
        Ok(_) => buffer,
        Err(_) => buffer,
    }
}

fn main() {
    let options = Options::from_args();
    let mut target: String = str!(&options.target.clone());

    // Check if target was provided
    if target.len() == 0 {
        if !options.silent {
            eprintln!("No target specified");
        }
        process::exit(1);
    }

    // Check if custom charset is valid
    if let Some(custom_charset) = options.charset.clone() {
        if !Encoding::for_label_no_replacement(custom_charset.as_bytes()).is_some() {
            eprintln!("Unknown encoding: {}", &custom_charset);
            process::exit(1);
        }
    }

    let target_url: Url;
    let mut use_stdin: bool = false;

    // Determine exact target URL
    if target.clone() == "-" {
        // Read from pipe (stdin)
        use_stdin = true;
        // Set default target URL to an empty data URL; the user can set it via --base-url
        target_url = Url::parse("data:text/html,").unwrap();
    } else {
        match Url::parse(&target.clone()) {
            Ok(parsed_url) => {
                if parsed_url.scheme() == "data"
                    || parsed_url.scheme() == "file"
                    || (parsed_url.scheme() == "http" || parsed_url.scheme() == "https")
                {
                    target_url = parsed_url;
                } else {
                    if !options.silent {
                        eprintln!("Unsupported target URL type: {}", &parsed_url.scheme());
                    }
                    process::exit(1);
                }
            }
            Err(_err) => {
                // Failed to parse given base URL,
                // perhaps it's a filesystem path?
                let path: &Path = Path::new(&target);

                if path.exists() {
                    if path.is_file() {
                        match Url::from_file_path(fs::canonicalize(&path).unwrap()) {
                            Ok(file_url) => {
                                target_url = file_url;
                            }
                            Err(_err) => {
                                if !options.silent {
                                    eprintln!(
                                        "Could not generate file URL out of given path: {}",
                                        "err"
                                    );
                                }
                                process::exit(1);
                            }
                        }
                    } else {
                        if !options.silent {
                            eprintln!("Local target is not a file: {}", &options.target);
                        }
                        process::exit(1);
                    }
                } else {
                    // Last chance, now we do what browsers do:
                    // prepend "http://" and hope it points to a website
                    target.insert_str(0, "http://");
                    target_url = Url::parse(&target).unwrap();
                }
            }
        }
    }

    // Initialize client
    let mut cache = HashMap::new();
    let mut header_map = HeaderMap::new();
    if let Some(user_agent) = &options.user_agent {
        header_map.insert(
            USER_AGENT,
            HeaderValue::from_str(&user_agent).expect("Invalid User-Agent header specified"),
        );
    }
    let client = if options.timeout > 0 {
        Client::builder().timeout(Duration::from_secs(options.timeout))
    } else {
        // No timeout is default
        Client::builder()
    }
    .danger_accept_invalid_certs(options.insecure)
    .default_headers(header_map)
    .build()
    .expect("Failed to initialize HTTP client");

    // At first we assume that base URL is the same as target URL
    let mut base_url: Url = target_url.clone();

    let data: Vec<u8>;
    let mut document_encoding: String = str!();
    let mut dom: RcDom;

    // Retrieve target document
    if use_stdin {
        data = read_stdin();
    } else if target_url.scheme() == "file"
        || (target_url.scheme() == "http" || target_url.scheme() == "https")
        || target_url.scheme() == "data"
    {
        match retrieve_asset(&mut cache, &client, &target_url, &target_url, &options, 0) {
            Ok((retrieved_data, final_url, media_type, charset)) => {
                // Make sure the media type is text/html
                if !media_type.eq_ignore_ascii_case("text/html") {
                    if !options.silent {
                        eprintln!("Unsupported document media type");
                    }
                    process::exit(1);
                }

                if options.base_url.clone().unwrap_or(str!()).is_empty() {
                    base_url = final_url;
                }

                data = retrieved_data;
                document_encoding = charset;
            }
            Err(_) => {
                if !options.silent {
                    eprintln!("Could not retrieve target document");
                }
                process::exit(1);
            }
        }
    } else {
        process::exit(1);
    }

    // Initial parse
    dom = html_to_dom(&data, document_encoding.clone());

    // TODO: investigate if charset from filesystem/data URL/HTTP headers
    //       has say over what's specified in HTML

    // Attempt to determine document's charset
    if let Some(html_charset) = get_charset(&dom.document) {
        if !html_charset.is_empty() {
            // Check if the charset specified inside HTML is valid
            if let Some(encoding) = Encoding::for_label_no_replacement(html_charset.as_bytes()) {
                document_encoding = html_charset;
                dom = html_to_dom(&data, encoding.name().to_string());
            }
        }
    }

    // Use custom base URL if specified, read and use what's in the DOM otherwise
    let custom_base_url: String = options.base_url.clone().unwrap_or(str!());
    if custom_base_url.is_empty() {
        // No custom base URL is specified
        // Try to see if document has BASE element
        if let Some(existing_base_url) = get_base_url(&dom.document) {
            base_url = resolve_url(&target_url, &existing_base_url);
        }
    } else {
        // Custom base URL provided
        match Url::parse(&custom_base_url) {
            Ok(parsed_url) => {
                if parsed_url.scheme() == "file" {
                    // File base URLs can only work with
                    // documents saved from filesystem
                    if target_url.scheme() == "file" {
                        base_url = parsed_url;
                    }
                } else {
                    base_url = parsed_url;
                }
            }
            Err(_) => {
                // Failed to parse given base URL, perhaps it's a filesystem path?
                if target_url.scheme() == "file" {
                    // Relative paths could work for documents saved from filesystem
                    let path: &Path = Path::new(&custom_base_url);
                    if path.exists() {
                        match Url::from_file_path(fs::canonicalize(&path).unwrap()) {
                            Ok(file_url) => {
                                base_url = file_url;
                            }
                            Err(_) => {
                                if !options.silent {
                                    eprintln!(
                                        "Could not map given path to base URL: {}",
                                        custom_base_url
                                    );
                                }
                                process::exit(1);
                            }
                        }
                    }
                }
            }
        }
    }

    // Traverse through the document and embed remote assets
    walk_and_embed_assets(&mut cache, &client, &base_url, &dom.document, &options, 0);

    // Update or add new BASE element to reroute network requests and hash-links
    if let Some(new_base_url) = options.base_url.clone() {
        dom = set_base_url(&dom.document, new_base_url);
    }

    // Request and embed /favicon.ico (unless it's already linked in the document)
    if !options.no_images
        && (target_url.scheme() == "http" || target_url.scheme() == "https")
        && !has_favicon(&dom.document)
    {
        let favicon_ico_url: Url = resolve_url(&base_url, "/favicon.ico");

        match retrieve_asset(
            &mut cache,
            &client,
            &target_url,
            &favicon_ico_url,
            &options,
            0,
        ) {
            Ok((data, final_url, media_type, charset)) => {
                let favicon_data_url: Url =
                    create_data_url(&media_type, &charset, &data, &final_url);
                dom = add_favicon(&dom.document, favicon_data_url.to_string());
            }
            Err(_) => {
                // Failed to retrieve /favicon.ico
            }
        }
    }

    // Save using specified charset, if given
    if let Some(custom_charset) = options.charset.clone() {
        document_encoding = custom_charset;
        dom = set_charset(dom, document_encoding.clone());
    }

    // Serialize DOM tree
    let mut result: Vec<u8> = serialize_document(dom, document_encoding, &options);

    // Prepend metadata comment tag
    if !options.no_metadata {
        let mut metadata_comment: String = create_metadata_tag(&target_url);
        metadata_comment += "\n";
        result.splice(0..0, metadata_comment.as_bytes().to_vec());
    }

    // Define output
    let mut output = Output::new(&options.output).expect("Could not prepare output");

    // Write result into stdout or file
    output.write(&result).expect("Could not write HTML output");
}
<|endoftext|>


<commit_before>

#![feature(path_ext)]
extern crate mysql;
extern crate toml;
extern crate rustc_serialize;
#[macro_use]
extern crate lazy_static;

mod lib;

use mysql::conn::MyOpts;
use std::default::Default;
use mysql::conn::pool;
use mysql::error::MyError;
use mysql::conn::pool::MyPooledConn;
use mysql::value::from_value;

use std::error::Error;

use std::ascii::AsciiExt;

use toml::Table;
use toml::Value;

use lib::config;
use lib::downloader::DownloadDB;
use lib::downloader::Downloader;
use lib::downloader::DownloadError;
use lib::converter::Converter;

use std::fs::rename;

static VERSION : &'static str = "0.1"; // String not valid
static SLEEP_MS: u32 = 5000;

lazy_static! {
    static ref CONFIG: config::Config = {
        println!("Starting yayd-backend v{}",&VERSION);
        config::init_config() //return
    };

}

//#[repr(i8)] broken, enum not usable as of #10292
enum QueryCodes {
    Waiting = 0,
    InProgress = 1,
    Finished = 2,
    Failed = 3,
}

fn main() {
    let opts = mysql_options();
    let pool = match pool::MyPool::new(opts) {
        Ok(conn) => { println!("Connected successfully."); conn},
        Err(err) => panic!("Unable to establish a connection!\n{}",err),
    };
    let converter = Converter::new(&CONFIG.general.ffmpeg_bin, &CONFIG.codecs.audio, pool.clone());
    loop {
        if let Some(result) = request_entry(& pool) {
            if result.playlist {
                println!("Playlist not supported atm!");
                //TODO: set playlist entry to errg
            }
            let qid = result.qid.clone();                 //&QueryCodes::InProgress as i32
            set_query_code(&mut pool.get_conn().unwrap(), &1, &result.qid).ok().expect("Failed to set query code!");
            set_query_state(&pool.clone(),&qid, "started");
            let code = if handle_download(result, None, &converter) {
                2//QueryCodes::Finished as i32
            } else {
                3//QueryCodes::Failed as i32
            };
            set_query_code(&mut pool.get_conn().unwrap(), &code,&qid).ok().expect("Failed to set query code!");
            let state = if code == 2 {
                "finished"
            } else {
                "failed"
            };
            set_query_state(&pool.clone(),&qid, state);
        } else {
            println!("Pausing..");
            std::thread::sleep_ms(SLEEP_MS);
        }
    }

    println!("EOL!");
}

///Download handler
///Used by the playlist/file handler to download one file
///Based on the quality it needs to download audio & video splitted & convert them together
///In case of a playlist download it depends on the target download folder & if should bezipped
///In case of a DMCA we need to download the file via the socket connector,
///which will output a mp3, or if requested, a hard quality depending on the max-available of
///the video url. Thus in case of a DMCA we can't pick a quality anymore.
///Also the filename depends on the socket output then.
///
///If it's a non-zipped single file, the file is moved after a success download,convert etc to the
///main folder from which it should be downloadable.
///The original non-ascii & url_encode name of the file is stored in the DB
fn handle_download(downl_db: DownloadDB, folder: Option<String>, converter: &Converter) -> bool{

    let is_zipped = match folder {
        Some(_) => true,
        None => false,
    };

    let dbcopy = downl_db.clone(); //copy, all implement copy & no &'s in use
    let download = Downloader::new(downl_db, &CONFIG.general);
    //get filename, check for DMCA
    let mut dmca = false; // "succ." dmca -> file already downloaded
    let name = match download.get_file_name() { // get filename
        Ok(v) => v,
        Err(DownloadError::DMCAError) => { //now request via lib.. // k if( k == Err(DownloadError::DMCAError) )
            println!("DMCA error!");
            match download.lib_request_video() {
                Err(err) => { println!("Offliberty-call error {:?}", err); return false; },
                Ok(v) => { dmca = true; v },
            }
        },
        Err(e) => { // unknown error / video private etc.. abort
            println!("Unknown error: {:?}", e);
            //TODO: add error descr (change enum etc)
            set_query_state(&dbcopy.pool.clone(),&dbcopy.qid, "Error!");
            return false;
        },
    };

    let name_http_valid = url_encode(&name);

    println!("Filename: {}", name);

    if(dmca){
        //TODO: insert title name for file,
        //copy file to download folder
        return true;
    }

    let file_path = format_file_patH(&dbcopy.qid, folder.clone());
    let save_path = &format_save_path(folder.clone(),&name, &download);

    let is_splitted_format = is_split_container(&dbcopy.quality);
    let total_steps = if is_splitted_format {
        4
    } else {
        2
    };
    //update progress
    update_steps(&dbcopy.pool.clone(),&dbcopy.qid, 1, total_steps);

    //download video, which is video/audio(m4a)
    download.download_video(&file_path, false);

    update_steps(&dbcopy.pool.clone(),&dbcopy.qid, 2, total_steps);

    if is_splitted_format { // download audio file & convert together
        let audio_path = format_audio_path(&dbcopy.qid, folder.clone());

        println!("Downloading audio.. {}", audio_path);
        download.download_video(&audio_path, true);

        update_steps(&dbcopy.pool.clone(),&dbcopy.qid, 3, total_steps);

        match converter.merge_files(&dbcopy.qid,&file_path, &audio_path,&save_path) {
            Err(e) => {println!("merge error: {:?}",e); return false;},
            Ok(()) => {},
        }

        update_steps(&dbcopy.pool.clone(),&dbcopy.qid, 4, total_steps);

    }else{ // we're already done, only need to copy / convert to mp3 if requested
        if download.is_audio(){ // if audio-> convert m4a to mp3, which converts directly to downl. dir
            //TODO: convert -> saves already ?
        }else{
            //converter.
            //match rename(format!("{}/{}", folder));
            //TODO: move file
        }

    }

    if is_zipped { // add file to list, except it's for zip-compression later (=folder set)
        add_file_entry(&dbcopy.pool.clone(), &dbcopy.qid,&name_http_valid, &name);
    }

    //TODO: download file, convert if audio ?!
    true
}

fn update_steps(pool: & pool::MyPool, qid: &i64, step: i32, max_steps: i32){
    set_query_state(&pool,qid, &format!("{}|{}", step, max_steps));
}

fn get_file_ext<'a>(download: &Downloader) -> &'a str {
    match download.is_audio() {
        true => "mp3",
        false => "mp4",
    }
}

fn format_file_patH(qid: &i64, folder: Option<String>) -> String {
    match folder {
        Some(v) => format!("{}/{}/{}", &CONFIG.general.save_dir, v, qid),
        None => format!("{}/{}", &CONFIG.general.save_dir, qid),
    }
}

///Formats the audio path, based on the qid & optional folders
fn format_audio_path(qid: &i64, folder: Option<String>) -> String {
    match folder {
        Some(v) => format!("{}/{}/{}a", &CONFIG.general.save_dir, v, qid),
        None => format!("{}/{}a", &CONFIG.general.save_dir, qid),
    }
}

///Format save path, which stays inside the optional folder, if set for zipping later
fn format_save_path<'a>(folder: Option<String>, name: &str, download: &'a Downloader) -> String {
    match folder {
        Some(v) => format!("{}/{}/{}.{}", &CONFIG.general.save_dir, v, url_encode(name),get_file_ext(download)),
        None => format!("{}/{}.{}", &CONFIG.general.download_dir, url_encode(name),get_file_ext(download)),
    }
}

///Set the state of the current query, also in dependence of the code, see QueryCodes
fn set_query_state(pool: & pool::MyPool,qid: &i64 , state: &str){ // same here
    let mut conn = pool.get_conn().unwrap();
    let mut stmt = conn.prepare("UPDATE querydetails SET status = ? WHERE qid = ?").unwrap();
    let result = stmt.execute(&[&state,qid]); // why is this var needed ?!
    match result {
        Ok(_) => (),
        Err(why) => println!("Error setting query state: {}",why),
    }
}

fn add_file_entry(pool: & pool::MyPool, fid: &i64, name: &str, real_name: &str){
    let mut conn = pool.get_conn().unwrap();
    let mut stmt = conn.prepare("INSERT INTO files (fid,name,rname,valid) VALUES (?,?,?,?)").unwrap();
    let result = stmt.execute(&[fid,&real_name,&name,&1]); // why is this var needed ?!
    match result {
        Ok(_) => (),
        Err(why) => println!("Error adding file: {}",why),
    }
}

///Return whether the quality is a split container: video only
///as specified in the docs
fn is_split_container(quality: &i16) -> bool {
    match *quality {
        141 | 83 | 82 | 84 | 85 => false,
        _ => true,
    }
}

///Return an url-conform String
fn url_encode(input: &str) -> String {
    // iterator over input, apply function to each element(function
    input.chars().map(|char| {
        match char {
            ' ' | '?' | '!' | '\\' | '/' | '.' | '(' | ')' | '[' | ']' => '_',
            '&' => '-',
            c if c.is_ascii() => c,
            _ => '_'
        }
    }).collect()
    // match for each char, then do collect: loop through the iterator, collect all elements
    // into container FromIterator
}

///Request an entry from the DB that should be handled
fn request_entry(pool: & pool::MyPool) -> Option<DownloadDB> {
    let mut conn = pool.get_conn().unwrap();
    let mut stmt = conn.prepare("SELECT queries.qid,url,type,quality FROM querydetails \
                    INNER JOIN queries \
                    ON querydetails.qid = queries.qid \
                    WHERE querydetails.code = 0 \
                    ORDER BY queries.created \
                    LIMIT 1").unwrap();
    let mut result = stmt.execute(&[]).unwrap();
    let result = match result.next() {
        Some(val) => val.unwrap(),
        None => {return None; },
    };
    println!("Result: {:?}", result[0]);
    println!("result str: {}", result[1].into_str());
    let download_db = DownloadDB { url: from_value::<String>(&result[1]),
                                                quality: from_value::<i16>(&result[3]),
                                                qid: from_value::<i64>(&result[0]),
                                                audioquality: CONFIG.codecs.audio,
                                                folder: CONFIG.general.save_dir.clone(),
                                                pool: pool.clone(),
                                                playlist: false, //TEMP
                                                compress: false };
    Some(download_db)
}

fn set_query_code(conn: & mut MyPooledConn, code: &i8, qid: &i64) -> Result<(), DownloadError> { // same here
    let mut stmt = conn.prepare("UPDATE querydetails SET code = ? WHERE qid = ?").unwrap();
    let result = stmt.execute(&[code,qid]); // why is this var needed ?!
    match result {
        Ok(_) => Ok(()),
        Err(why) => Err(DownloadError::DBError(why.description().into())),
    }
}

///Set options for the connection
fn mysql_options() -> MyOpts {
    //let dbconfig = CONFIG.get("db").unwrap().clone();
    //let dbconfig = dbconfig.as_table().unwrap(); // shadow binding to workaround borrow / lifetime problems

    MyOpts {
        tcp_addr: Some(CONFIG.db.ip.clone()),
        tcp_port: CONFIG.db.port,
        user: Some(CONFIG.db.user.clone()),
        pass: Some(CONFIG.db.password.clone()),
        db_name: Some(CONFIG.db.db.clone()),
        ..Default::default() // set other to default
    }
}

///Converts a toml::Value to a Option<String> for mysql::MyOpts
#[allow(deprecated)]
fn get_option_string(table: & Table,key: & str) -> Option<String> {
    let val: Value = table.get(key).unwrap().clone();
    if let toml::Value::String(s) = val {
        Some(s)
    } else { unreachable!() }
}<commit_msg>+move_file *renamed download_video -> download_file +massive error catching improvement<commit_after>

#![feature(path_ext)]
extern crate mysql;
extern crate toml;
extern crate rustc_serialize;
#[macro_use]
extern crate lazy_static;

mod lib;

use mysql::conn::MyOpts;
use std::default::Default;
use mysql::conn::pool;
use mysql::error::MyError;
use mysql::conn::pool::MyPooledConn;
use mysql::value::from_value;

use std::error::Error;

use std::ascii::AsciiExt;

use toml::Table;
use toml::Value;

use lib::config;
use lib::downloader::DownloadDB;
use lib::downloader::Downloader;
use lib::downloader::DownloadError;
use lib::converter::Converter;

use std::fs::rename;

static VERSION : &'static str = "0.1"; // String not valid
static SLEEP_MS: u32 = 5000;

lazy_static! {
    static ref CONFIG: config::Config = {
        println!("Starting yayd-backend v{}",&VERSION);
        config::init_config() //return
    };

}

//#[repr(i8)] broken, enum not usable as of #10292
enum QueryCodes {
    Waiting = 0,
    InProgress = 1,
    Finished = 2,
    Failed = 3,
}

fn main() {
    let opts = mysql_options();
    let pool = match pool::MyPool::new(opts) {
        Ok(conn) => { println!("Connected successfully."); conn},
        Err(err) => panic!("Unable to establish a connection!\n{}",err),
    };
    let converter = Converter::new(&CONFIG.general.ffmpeg_bin, &CONFIG.codecs.audio, pool.clone());
    loop {
        if let Some(result) = request_entry(& pool) {
            if result.playlist {
                println!("Playlist not supported atm!");
                //TODO: set playlist entry to errg
            }
            let qid = result.qid.clone();                 //&QueryCodes::InProgress as i32
            set_query_code(&mut pool.get_conn().unwrap(), &1, &result.qid).ok().expect("Failed to set query code!");
            set_query_state(&pool.clone(),&qid, "started");
            let succes = match handle_download(result, None, &converter) {
                Ok(v) => v,
                Err(e) => {println!("Error: {:?}", e); false }
            };
            let code = if succes {
                2//QueryCodes::Finished as i32
            } else {
                3//QueryCodes::Failed as i32
            };
            set_query_code(&mut pool.get_conn().unwrap(), &code,&qid).ok().expect("Failed to set query code!");
            let state = if code == 2 {
                "finished"
            } else {
                "failed"
            };
            set_query_state(&pool.clone(),&qid, state);
        } else {
            println!("Pausing..");
            std::thread::sleep_ms(SLEEP_MS);
        }
    }

    println!("EOL!");
}

///Download handler
///Used by the playlist/file handler to download one file
///Based on the quality it needs to download audio & video splitted & convert them together
///In case of a playlist download it depends on the target download folder & if should bezipped
///In case of a DMCA we need to download the file via the socket connector,
///which will output a mp3, or if requested, a hard quality depending on the max-available of
///the video url. Thus in case of a DMCA we can't pick a quality anymore.
///Also the filename depends on the socket output then.
///
///If it's a non-zipped single file, the file is moved after a success download,convert etc to the
///main folder from which it should be downloadable.
///The original non-ascii & url_encode name of the file is stored in the DB
fn handle_download(downl_db: DownloadDB, folder: Option<String>, converter: &Converter) -> Result<bool,DownloadError>{
    //update progress
    let is_zipped = match folder {
        Some(_) => true,
        None => false,
    };

    let dbcopy = downl_db.clone(); //copy, all implement copy & no &'s in use
    update_steps(&dbcopy.pool.clone(),&dbcopy.qid, 1, 0);
    let download = Downloader::new(downl_db, &CONFIG.general);
    //get filename, check for DMCA
    let mut dmca = false; // "succ." dmca -> file already downloaded
    let name = match download.get_file_name() { // get filename
        Ok(v) => v,
        Err(DownloadError::DMCAError) => { //now request via lib.. // k if( k == Err(DownloadError::DMCAError) )
            println!("DMCA error!");
            match download.lib_request_video() {
                Err(err) => { println!("Offliberty-call error {:?}", err); return Err(err); },
                Ok(v) => { dmca = true; v },
            }
        },
        Err(e) => { // unknown error / video private etc.. abort
            println!("Unknown error: {:?}", e);
            return Err(e);
        },
    };

    let name_http_valid = url_encode(&name);

    println!("Filename: {}", name);

    if(dmca){
        //TODO: insert title name for file,
        //copy file to download folder
        return Ok(true);
    }

    let file_path = format_file_path(&dbcopy.qid, folder.clone());
    let save_path = &format_save_path(folder.clone(),&name, &download);

    let is_splitted_format = is_split_container(&dbcopy.quality);
    let total_steps = if is_splitted_format {
        4
    } else {
        2
    };
    update_steps(&dbcopy.pool.clone(),&dbcopy.qid, 2, total_steps);

    //download video, which is video/audio(m4a)
    try!(download.download_file(&file_path, false));

    if is_splitted_format { // download audio file & convert together
        update_steps(&dbcopy.pool.clone(),&dbcopy.qid, 3, total_steps);

        let audio_path = format_audio_path(&dbcopy.qid, folder.clone());

        println!("Downloading audio.. {}", audio_path);
        try!(download.download_file(&audio_path, true));

        update_steps(&dbcopy.pool.clone(),&dbcopy.qid, 4, total_steps);

        match converter.merge_files(&dbcopy.qid,&file_path, &audio_path,&save_path) {
            Err(e) => {println!("merge error: {:?}",e); return Err(e);},
            Ok(()) => {},
        }

    }else{ // we're already done, only need to copy / convert to mp3 if requested
        if download.is_audio(){ // if audio-> convert m4a to mp3, which converts directly to downl. dir
            //TODO: convert -> saves already ?
        }else{
            try!(move_file(&file_path, &save_path));
        }

    }

    if !is_zipped { // add file to list, except it's for zip-compression later (=folder set)
        add_file_entry(&dbcopy.pool.clone(), &dbcopy.qid,&name_http_valid, &name);
    }

    //TODO: download file, convert if audio ?!
    Ok(true)
}

fn update_steps(pool: & pool::MyPool, qid: &i64, step: i32, max_steps: i32){
    set_query_state(&pool,qid, &format!("{}|{}", step, max_steps));
}

fn get_file_ext<'a>(download: &Downloader) -> &'a str {
    match download.is_audio() {
        true => "mp3",
        false => "mp4",
    }
}

fn move_file(original: &str, destination: &str) -> Result<(),DownloadError> {
    match rename(original, destination) { // no try possible..
        Err(v) => Err(v.into()),
        Ok(_) => Ok(()),
    }
}

fn format_file_path(qid: &i64, folder: Option<String>) -> String {
    match folder {
        Some(v) => format!("{}/{}/{}", &CONFIG.general.save_dir, v, qid),
        None => format!("{}/{}", &CONFIG.general.save_dir, qid),
    }
}

///Formats the audio path, based on the qid & optional folders
fn format_audio_path(qid: &i64, folder: Option<String>) -> String {
    match folder {
        Some(v) => format!("{}/{}/{}a", &CONFIG.general.save_dir, v, qid),
        None => format!("{}/{}a", &CONFIG.general.save_dir, qid),
    }
}

///Format save path, which stays inside the optional folder, if set for zipping later
fn format_save_path<'a>(folder: Option<String>, name: &str, download: &'a Downloader) -> String {
    match folder {
        Some(v) => format!("{}/{}/{}.{}", &CONFIG.general.save_dir, v, url_encode(name),get_file_ext(download)),
        None => format!("{}/{}.{}", &CONFIG.general.download_dir, url_encode(name),get_file_ext(download)),
    }
}

///Set the state of the current query, also in dependence of the code, see QueryCodes
fn set_query_state(pool: & pool::MyPool,qid: &i64 , state: &str){ // same here
    let mut conn = pool.get_conn().unwrap();
    let mut stmt = conn.prepare("UPDATE querydetails SET status = ? , progress = 0 WHERE qid = ?").unwrap();
    let result = stmt.execute(&[&state,qid]); // why is this var needed ?!
    match result {
        Ok(_) => (),
        Err(why) => println!("Error setting query state: {}",why),
    }
}

fn add_file_entry(pool: & pool::MyPool, fid: &i64, name: &str, real_name: &str){
    let mut conn = pool.get_conn().unwrap();
    let mut stmt = conn.prepare("INSERT INTO files (fid,name,rname,valid) VALUES (?,?,?,?)").unwrap();
    let result = stmt.execute(&[fid,&real_name,&name,&1]); // why is this var needed ?!
    match result {
        Ok(_) => (),
        Err(why) => println!("Error adding file: {}",why),
    }
}

///Return whether the quality is a split container: video only
///as specified in the docs
fn is_split_container(quality: &i16) -> bool {
    match *quality {
        141 | 83 | 82 | 84 | 85 => false,
        _ => true,
    }
}

///Return an url-conform String
fn url_encode(input: &str) -> String {
    // iterator over input, apply function to each element(function
    input.chars().map(|char| {
        match char {
            ' ' | '?' | '!' | '\\' | '/' | '.' | '(' | ')' | '[' | ']' => '_',
            '&' => '-',
            c if c.is_ascii() => c,
            _ => '_'
        }
    }).collect()
    // match for each char, then do collect: loop through the iterator, collect all elements
    // into container FromIterator
}

///Request an entry from the DB that should be handled
fn request_entry(pool: & pool::MyPool) -> Option<DownloadDB> {
    let mut conn = pool.get_conn().unwrap();
    let mut stmt = conn.prepare("SELECT queries.qid,url,type,quality FROM querydetails \
                    INNER JOIN queries \
                    ON querydetails.qid = queries.qid \
                    WHERE querydetails.code = 0 \
                    ORDER BY queries.created \
                    LIMIT 1").unwrap();
    let mut result = stmt.execute(&[]).unwrap();
    let result = match result.next() {
        Some(val) => val.unwrap(),
        None => {return None; },
    };
    println!("Result: {:?}", result[0]);
    println!("result str: {}", result[1].into_str());
    let download_db = DownloadDB { url: from_value::<String>(&result[1]),
                                                quality: from_value::<i16>(&result[3]),
                                                qid: from_value::<i64>(&result[0]),
                                                audioquality: CONFIG.codecs.audio,
                                                folder: CONFIG.general.save_dir.clone(),
                                                pool: pool.clone(),
                                                playlist: false, //TEMP
                                                compress: false };
    Some(download_db)
}

fn set_query_code(conn: & mut MyPooledConn, code: &i8, qid: &i64) -> Result<(), DownloadError> { // same here
    let mut stmt = conn.prepare("UPDATE querydetails SET code = ? WHERE qid = ?").unwrap();
    let result = stmt.execute(&[code,qid]); // why is this var needed ?!
    match result {
        Ok(_) => Ok(()),
        Err(why) => Err(DownloadError::DBError(why.description().into())),
    }
}

///Set options for the connection
fn mysql_options() -> MyOpts {
    //let dbconfig = CONFIG.get("db").unwrap().clone();
    //let dbconfig = dbconfig.as_table().unwrap(); // shadow binding to workaround borrow / lifetime problems

    MyOpts {
        tcp_addr: Some(CONFIG.db.ip.clone()),
        tcp_port: CONFIG.db.port,
        user: Some(CONFIG.db.user.clone()),
        pass: Some(CONFIG.db.password.clone()),
        db_name: Some(CONFIG.db.db.clone()),
        ..Default::default() // set other to default
    }
}

///Converts a toml::Value to a Option<String> for mysql::MyOpts
#[allow(deprecated)]
fn get_option_string(table: & Table,key: & str) -> Option<String> {
    let val: Value = table.get(key).unwrap().clone();
    if let toml::Value::String(s) = val {
        Some(s)
    } else { unreachable!() }
}<|endoftext|>


<commit_before>#[macro_use]
extern crate clap;
extern crate toml;
extern crate semver;
extern crate term;

use std::io::{Read, Write};
use std::fs::{File, OpenOptions};
use std::path::Path;
use std::process::Command;
use semver::Version;
use cr_result::CrResult;
use config::Config;
use cargo_proj::CargoProj;
use utils::check_output;

mod git;
mod cr_result;
mod version_kind;
mod config;
mod cargo_proj;
mod cargo;
mod utils;

fn main() {
    set_stdout_stderr_colors();

    let mut exit_code = 0;
    execute().unwrap_or_else(|err| {
        let mut stderr = term::stderr().unwrap();
        writeln!(stderr, "{}", err).unwrap();
        exit_code = 1;
    });

    reset_stdout_stderr();
    std::process::exit(exit_code);
}

fn execute() -> CrResult<()> {
    let mut stdout = term::stdout().unwrap();

    let config = try!(Config::from_command_args());
    let mut cargo_proj = try!(CargoProj::find(&config.start_dir));

    try!(std::env::set_current_dir(try!(cargo_proj.root_dir())));

    try!(writeln!(stdout, "Checking git state ..."));
    try!(git::check_clean_state());

    let curr_version = cargo_proj.version().clone();
    let new_version = config.version_kind.increment(&curr_version);
    try!(cargo_proj.write_version(&new_version));

    try!(writeln!(stdout, "Building release ..."));
    try!(cargo::build_release());

    try!(writeln!(stdout, "Testing ..."));
    try!(cargo::test());

    if let Some(changelog) = cargo_proj.changelog() {
        try!(writeln!(stdout, "Updating changelog ..."));
        try!(update_changelog(changelog, &new_version));
    }

    try!(git::add_update());
    try!(git::commit(&commit_message(&cargo_proj)));
    try!(git::tag(&tag_name(&cargo_proj)));
    try!(git::push());

    try!(cargo::publish());

    Ok(())
}

/// Adds `new_version` at the top of the `changelog` and opens
/// `changelog` in the editor specified by `CARGO_RELEASE_EDITOR` (default: gvim).
fn update_changelog(changelog: &Path, new_version: &Version) -> CrResult<()> {
    let contents = {
        let mut file = try!(File::open(changelog));
        let mut contents = String::new();
        try!(file.read_to_string(&mut contents));
        contents
    };

    let contents = format!("{}\n\n{}", new_version, contents);
    {
        let mut file = try!(OpenOptions::new()
            .truncate(true)
            .read(true)
            .write(true)
            .open(changelog));

        try!(file.write_all(contents.as_bytes()));
    }

    let editor = std::env::var("CARGO_RELEASE_EDITOR").unwrap_or("gvim".to_string());
    let output = try!(Command::new(editor)
        .arg(changelog)
        .output());

    try!(check_output(&output));
    Ok(())
}

fn commit_message(proj: &CargoProj) -> String {
    format!("{} {}", proj.name(), proj.version())
}

fn tag_name(proj: &CargoProj) -> String {
    format!("{}-{}", proj.name(), proj.version())
}

fn set_stdout_stderr_colors() {
    let mut stdout = term::stdout().unwrap();
    stdout.fg(term::color::GREEN).unwrap();

    let mut stderr = term::stderr().unwrap();
    stderr.fg(term::color::RED).unwrap();
}

fn reset_stdout_stderr() {
    let mut stdout = term::stdout().unwrap();
    stdout.reset().unwrap();

    let mut stderr = term::stderr().unwrap();
    stderr.reset().unwrap();
}
<commit_msg>Add some additional output<commit_after>#[macro_use]
extern crate clap;
extern crate toml;
extern crate semver;
extern crate term;

use std::io::{Read, Write};
use std::fs::{File, OpenOptions};
use std::path::Path;
use std::process::Command;
use semver::Version;
use cr_result::CrResult;
use config::Config;
use cargo_proj::CargoProj;
use utils::check_output;

mod git;
mod cr_result;
mod version_kind;
mod config;
mod cargo_proj;
mod cargo;
mod utils;

fn main() {
    set_stdout_stderr_colors();

    let mut exit_code = 0;
    execute().unwrap_or_else(|err| {
        let mut stderr = term::stderr().unwrap();
        writeln!(stderr, "{}", err).unwrap();
        exit_code = 1;
    });

    reset_stdout_stderr();
    std::process::exit(exit_code);
}

fn execute() -> CrResult<()> {
    let mut stdout = term::stdout().unwrap();

    let config = try!(Config::from_command_args());
    let mut cargo_proj = try!(CargoProj::find(&config.start_dir));

    try!(std::env::set_current_dir(try!(cargo_proj.root_dir())));

    try!(writeln!(stdout, "Checking git state ..."));
    try!(git::check_clean_state());

    let curr_version = cargo_proj.version().clone();
    let new_version = config.version_kind.increment(&curr_version);
    try!(cargo_proj.write_version(&new_version));

    try!(writeln!(stdout, "Building release ..."));
    try!(cargo::build_release());

    try!(writeln!(stdout, "Testing ..."));
    try!(cargo::test());

    if let Some(changelog) = cargo_proj.changelog() {
        try!(writeln!(stdout, "Updating changelog ..."));
        try!(update_changelog(changelog, &new_version));
    }

    try!(writeln!(stdout, "Creating git commit ..."));
    try!(git::add_update());
    try!(git::commit(&commit_message(&cargo_proj)));
    try!(git::tag(&tag_name(&cargo_proj)));

    try!(writeln!(stdout, "Pushing git changes ..."));
    try!(git::push());

    try!(writeln!(stdout, "Publishing to crates.io ..."));
    try!(cargo::publish());

    Ok(())
}

/// Adds `new_version` at the top of the `changelog` and opens
/// `changelog` in the editor specified by `CARGO_RELEASE_EDITOR` (default: gvim).
fn update_changelog(changelog: &Path, new_version: &Version) -> CrResult<()> {
    let contents = {
        let mut file = try!(File::open(changelog));
        let mut contents = String::new();
        try!(file.read_to_string(&mut contents));
        contents
    };

    let contents = format!("{}\n\n{}", new_version, contents);
    {
        let mut file = try!(OpenOptions::new()
            .truncate(true)
            .read(true)
            .write(true)
            .open(changelog));

        try!(file.write_all(contents.as_bytes()));
    }

    let editor = std::env::var("CARGO_RELEASE_EDITOR").unwrap_or("gvim".to_string());
    let output = try!(Command::new(editor)
        .arg(changelog)
        .output());

    try!(check_output(&output));
    Ok(())
}

fn commit_message(proj: &CargoProj) -> String {
    format!("{} {}", proj.name(), proj.version())
}

fn tag_name(proj: &CargoProj) -> String {
    format!("{}-{}", proj.name(), proj.version())
}

fn set_stdout_stderr_colors() {
    let mut stdout = term::stdout().unwrap();
    stdout.fg(term::color::GREEN).unwrap();

    let mut stderr = term::stderr().unwrap();
    stderr.fg(term::color::RED).unwrap();
}

fn reset_stdout_stderr() {
    let mut stdout = term::stdout().unwrap();
    stdout.reset().unwrap();

    let mut stderr = term::stderr().unwrap();
    stderr.reset().unwrap();
}
<|endoftext|>


<commit_before>extern crate docopt;
extern crate xml;

mod library;
mod parser;

use docopt::Docopt;
use library::*;

static USAGE: &'static str = "
Usage: spore -d <dir> <lib>
";

fn main() {
    let args = Docopt::new(USAGE).unwrap()
        .parse().unwrap_or_else(|e| e.exit());
    let mut library = Library::new();
    library.read_file(args.get_str("<dir>"), args.get_str("<lib>"));
    library.check_resolved();
    show(&library);
}

#[allow(dead_code)]
fn show(library: &Library) {
    for namespace in &library.namespaces {
        println!("Namespace: {}", namespace);
        let prefix = format!("{}.", namespace);
        for (ref name, ref typ) in &library.types {
            if !name.starts_with(&prefix) {
                continue;
            }
            match *typ.borrow() {
                Type::Class(ref x) => println!("\tclass {}", x.name),
                Type::Record(ref x) => println!("\trecord {}", x.name),
                Type::Union(ref x) => println!("\tunion {}", x.name),
                Type::Interface(ref x) => println!("\tinterface {}", x.name),
                Type::Callback(ref x) => println!("\trecord {}", x.name),
                Type::Bitfield(ref x) => println!("\tbitfield {}", x.name),
                Type::Enumeration(ref x) => println!("\tenumeration {}", x.name),
                _ => println!("\t{} ???", name),
            }
        }
        for (ref name, ref c) in &library.constants {
            if !name.starts_with(&prefix) {
                continue;
            }
            println!("\tconst {} = {}", c.name, c.value);
        }
        for (ref name, ref f) in &library.functions {
            if !name.starts_with(&prefix) {
                continue;
            }
            println!("\tfunction {}", f.name);
        }
    }
}
<commit_msg>Mods after use<commit_after>extern crate docopt;
extern crate xml;

use docopt::Docopt;
use library::*;

mod library;
mod parser;

static USAGE: &'static str = "
Usage: spore -d <dir> <lib>
";

fn main() {
    let args = Docopt::new(USAGE).unwrap()
        .parse().unwrap_or_else(|e| e.exit());
    let mut library = Library::new();
    library.read_file(args.get_str("<dir>"), args.get_str("<lib>"));
    library.check_resolved();
    show(&library);
}

#[allow(dead_code)]
fn show(library: &Library) {
    for namespace in &library.namespaces {
        println!("Namespace: {}", namespace);
        let prefix = format!("{}.", namespace);
        for (ref name, ref typ) in &library.types {
            if !name.starts_with(&prefix) {
                continue;
            }
            match *typ.borrow() {
                Type::Class(ref x) => println!("\tclass {}", x.name),
                Type::Record(ref x) => println!("\trecord {}", x.name),
                Type::Union(ref x) => println!("\tunion {}", x.name),
                Type::Interface(ref x) => println!("\tinterface {}", x.name),
                Type::Callback(ref x) => println!("\trecord {}", x.name),
                Type::Bitfield(ref x) => println!("\tbitfield {}", x.name),
                Type::Enumeration(ref x) => println!("\tenumeration {}", x.name),
                _ => println!("\t{} ???", name),
            }
        }
        for (ref name, ref c) in &library.constants {
            if !name.starts_with(&prefix) {
                continue;
            }
            println!("\tconst {} = {}", c.name, c.value);
        }
        for (ref name, ref f) in &library.functions {
            if !name.starts_with(&prefix) {
                continue;
            }
            println!("\tfunction {}", f.name);
        }
    }
}
<|endoftext|>


<commit_before>#![allow(dead_code)]
#![allow(unused_variables)]
#![allow(unused_mut)]
#![allow(unused_imports)]

#![feature(phase)]

extern crate collections;

use std::os::{set_exit_status, getenv};
use std::path::Path;
use std::io;
use std::io::IoResult;
use std::io::IoError;
use std::io::IoErrorKind::{OtherIoError};
use std::io::process::Command;

use std::io::fs::{
   PathExtensions,
   mkdir_recursive,
   unlink,
   copy
};

use file_conflict::{
   ConflictType,
   Wuala,
   Dropbox,
   Conflict,
   ConflictingFile
};

use user_reply::UserReply::{
   TakeFile,
   MoveToTrash,
   ShowDiff,
   ShowDiffWith,
   ShowDiffBetween,
   Skip,
   Quit,
   Help
};

use app_result::{
   AppResult,
   AppError
};

use args::{
   ResolveWuala,
   ResolveDropbox,
   PrintHelp,
   InvalidUsage
};

mod app_result;
mod file_system;
mod file_conflict;
mod parser;
mod user_reply;
mod appdirs;
mod args;

fn main()
{
//   let cmd = ResolveWuala(Path::new("/home/dan/test/confsolve"));
   let cmd = args::get_command();
   match cmd {
      ResolveWuala(path) => {
         resolve_conflicts(Wuala, &path)
            .unwrap_or_else(|err| { exit_with_error(format!("{}", err)); });
      }

      ResolveDropbox(path) => {
         resolve_conflicts(Dropbox, &path)
            .unwrap_or_else(|err| { exit_with_error(format!("{}", err)); });
      }

      PrintHelp => args::print_help(),

      InvalidUsage => {
         exit_with_error("Invalid usage!".to_string());
         args::print_help();
      }
   }
}

fn exit_with_error(error: String)
{
   let mut stderr = io::stderr();
   let _ = writeln!(stderr, "confsolve: {}", error);
   set_exit_status(1);
}

/// Finds file conflicts of type `conf_type` starting at the directory `start_dir`,
/// recursively visiting every file, asking the user how each conflict should
/// be handled and then executing the user command.
fn resolve_conflicts(conf_type: ConflictType, start_dir: &Path) -> AppResult<()>
{
   let mut stdin = io::stdin();
   let confs = try!(file_conflict::find(conf_type, start_dir));
   for conf in confs.iter() {
      if ! conf.original_path.is_file() {
         println!("\nFound conflicts for the file '{}', but the file itself is missing! Skipping it.",
                  conf.original_path.display());
         continue;
      }

      let num_conf_files = conf.conflicting_files.len();
      println!("\n{}", conf);
      loop {
         print!("{}", "(T)ake File (NUM) | (M)ove to Trash | Show (D)iff (NUM [NUM]) | (S)kip | (Q)uit | (H)elp: ");
         let mut line = try!(stdin.read_line());
         match user_reply::parse(&line, num_conf_files) {
            Some(reply) => {
               match reply {
                  TakeFile(num) => {
                     let ref take_file = conf.conflicting_files[num - 1].path;
                     for conf_file in conf.conflicting_files.iter() {
                        if conf_file.path != *take_file {
                           try!(move_to_trash(&conf_file.path));
                        }
                     }

                     try!(move_to_trash(&conf.original_path));
                     try!(copy(take_file, &conf.original_path));
                  }

                  MoveToTrash => {
                     for conf_file in conf.conflicting_files.iter() {
                        try!(move_to_trash(&conf_file.path));
                     }
                  }

                  ShowDiff => {
                     try!(show_diff(&conf.original_path, &conf.conflicting_files[0].path));
                  }

                  ShowDiffWith(num) => {
                     try!(show_diff(&conf.original_path, &conf.conflicting_files[num - 1].path));
                  }

                  ShowDiffBetween(num1, num2) => {
                     try!(show_diff(&conf.conflicting_files[num1 - 1].path,
                                    &conf.conflicting_files[num2 - 1].path));
                  }

                  Skip => { break; }
                  Quit => { return Ok(()); }
                  Help => try!(print_runtime_help())
               }
            }

            None => {
               // remove newline at end of line
               line.pop();
               println!("\nInvalid user input: '{}' !\n", line);
            }
         }
      }
   }

   Ok(())
}

/// Calls the diff command specified by the environment variable `CONFSOLVE_DIFF`
/// or - if not defined - `gvimdiff -f` with the files `file1` and `file2`.
fn show_diff(file1: &Path, file2: &Path) -> AppResult<()>
{
   let diff_cmd_and_args = getenv("CONFSOLVE_DIFF").unwrap_or("gvimdiff -f".to_string());
   let diff_cmd_and_args = diff_cmd_and_args.as_slice().split(' ').collect::<Vec<&str>>();

   let diff_cmd = diff_cmd_and_args[0];

//   let mut args = diff_cmd_and_args.iter().collect::<Vec<&str>>();
//   let mut args = diff_cmd_and_args.iter().skip(1).collect::<Vec<&str>>();
//   let mut args = diff_cmd_and_args;
//   args.push(file1.as_str().unwrap());
//   args.push(file2.as_str().unwrap());

//   let mut cmd = Command::new(diff_cmd);
//   for arg in args.iter() {
//      cmd.arg(arg);
//   }
//
//   try!(cmd.output());
   Ok(())
}

/// Moves `file` into the trash directory of confsolve.
fn move_to_trash(file: &Path) -> AppResult<()>
{
   let filename = try!(file.filename_str()
      .ok_or(AppError::from_string(format!("Couldn't get filename from path '{}'!", file.display()))));

   let mut trash_file = try!(trash_dir());
   trash_file.set_filename(filename);

   try!(copy(file, &trash_file));
   try!(unlink(file));

   Ok(())
}

/// Returns the trash directory of confsolve, where all deleted/moved files are put into.
fn trash_dir() -> AppResult<Path>
{
   let mut dir = try!(appdirs::cache("confsolve")
      .ok_or(AppError::from_string(format!("Couldn't get cache directory!"))));

   dir.push("trash");
   if ! dir.is_dir() {
      try!(mkdir_recursive(&dir, io::USER_RWX));
   }

   Ok(dir)
}

fn print_runtime_help() -> AppResult<()>
{
   let trash_dir = try!(trash_dir());
   let dir_str   = trash_dir.display();

   println!("
Runtime Options:
   (T)ake File (NUM) => By pressing 't' and a number (e.g 't1'), the conflicting file with the
                        number NUM is used as the new version. A copy of the
                        current file and the other conflicting files is put
                        into the trash directory '{}'.

   (M)ove to Trash   => By pressing 'm', all conflicting files are
                        moved into the trash directory '{}'.

   Show (D)iff (NUM) => By pressing 'd' and a number (e.g 'd1'), the difference between the
                        current file and the conflicting file NUM is shown.
                        If there's only one conflicting file, then only pressing
                        'd' is sufficient.
                        By pressing 'd' and two numbers (e.g 'd1 2'), the difference between
                        the two conflicting files is shown.
                        The diff tool can be specified by the user by setting the environment
                        variable 'CONFSOLVE_DIFF'. The default diff tool is 'gvimdiff -f'.

   (S)kip            => By pressing 's', the current conflict is skipped
                        and the next one is shown.

   (Q)uit            => By pressing 'q', the application is quit.

   (H)elp            => By pressing 'h', this help is printed.\n", dir_str, dir_str);

   Ok(())
}
<commit_msg>Remove test code<commit_after>#![allow(dead_code)]
#![allow(unused_variables)]
#![allow(unused_mut)]
#![allow(unused_imports)]

#![feature(phase)]

extern crate collections;

use std::os::{set_exit_status, getenv};
use std::path::Path;
use std::io;
use std::io::IoResult;
use std::io::IoError;
use std::io::IoErrorKind::{OtherIoError};
use std::io::process::Command;

use std::io::fs::{
   PathExtensions,
   mkdir_recursive,
   unlink,
   copy
};

use file_conflict::{
   ConflictType,
   Wuala,
   Dropbox,
   Conflict,
   ConflictingFile
};

use user_reply::UserReply::{
   TakeFile,
   MoveToTrash,
   ShowDiff,
   ShowDiffWith,
   ShowDiffBetween,
   Skip,
   Quit,
   Help
};

use app_result::{
   AppResult,
   AppError
};

use args::{
   ResolveWuala,
   ResolveDropbox,
   PrintHelp,
   InvalidUsage
};

mod app_result;
mod file_system;
mod file_conflict;
mod parser;
mod user_reply;
mod appdirs;
mod args;

fn main()
{
   let cmd = args::get_command();
   match cmd {
      ResolveWuala(path) => {
         resolve_conflicts(Wuala, &path)
            .unwrap_or_else(|err| { exit_with_error(format!("{}", err)); });
      }

      ResolveDropbox(path) => {
         resolve_conflicts(Dropbox, &path)
            .unwrap_or_else(|err| { exit_with_error(format!("{}", err)); });
      }

      PrintHelp => args::print_help(),

      InvalidUsage => {
         exit_with_error("Invalid usage!".to_string());
         args::print_help();
      }
   }
}

fn exit_with_error(error: String)
{
   let mut stderr = io::stderr();
   let _ = writeln!(stderr, "confsolve: {}", error);
   set_exit_status(1);
}

/// Finds file conflicts of type `conf_type` starting at the directory `start_dir`,
/// recursively visiting every file, asking the user how each conflict should
/// be handled and then executing the user command.
fn resolve_conflicts(conf_type: ConflictType, start_dir: &Path) -> AppResult<()>
{
   let mut stdin = io::stdin();
   let confs = try!(file_conflict::find(conf_type, start_dir));
   for conf in confs.iter() {
      if ! conf.original_path.is_file() {
         println!("\nFound conflicts for the file '{}', but the file itself is missing! Skipping it.",
                  conf.original_path.display());
         continue;
      }

      let num_conf_files = conf.conflicting_files.len();
      println!("\n{}", conf);
      loop {
         print!("{}", "(T)ake File (NUM) | (M)ove to Trash | Show (D)iff (NUM [NUM]) | (S)kip | (Q)uit | (H)elp: ");
         let mut line = try!(stdin.read_line());
         match user_reply::parse(&line, num_conf_files) {
            Some(reply) => {
               match reply {
                  TakeFile(num) => {
                     let ref take_file = conf.conflicting_files[num - 1].path;
                     for conf_file in conf.conflicting_files.iter() {
                        if conf_file.path != *take_file {
                           try!(move_to_trash(&conf_file.path));
                        }
                     }

                     try!(move_to_trash(&conf.original_path));
                     try!(copy(take_file, &conf.original_path));
                  }

                  MoveToTrash => {
                     for conf_file in conf.conflicting_files.iter() {
                        try!(move_to_trash(&conf_file.path));
                     }
                  }

                  ShowDiff => {
                     try!(show_diff(&conf.original_path, &conf.conflicting_files[0].path));
                  }

                  ShowDiffWith(num) => {
                     try!(show_diff(&conf.original_path, &conf.conflicting_files[num - 1].path));
                  }

                  ShowDiffBetween(num1, num2) => {
                     try!(show_diff(&conf.conflicting_files[num1 - 1].path,
                                    &conf.conflicting_files[num2 - 1].path));
                  }

                  Skip => { break; }
                  Quit => { return Ok(()); }
                  Help => try!(print_runtime_help())
               }
            }

            None => {
               // remove newline at end of line
               line.pop();
               println!("\nInvalid user input: '{}' !\n", line);
            }
         }
      }
   }

   Ok(())
}

/// Calls the diff command specified by the environment variable `CONFSOLVE_DIFF`
/// or - if not defined - `gvimdiff -f` with the files `file1` and `file2`.
fn show_diff(file1: &Path, file2: &Path) -> AppResult<()>
{
   let diff_cmd_and_args = getenv("CONFSOLVE_DIFF").unwrap_or("gvimdiff -f".to_string());
   let diff_cmd_and_args = diff_cmd_and_args.as_slice().split(' ').collect::<Vec<&str>>();

   let diff_cmd = diff_cmd_and_args[0];

//   let mut args = diff_cmd_and_args.iter().collect::<Vec<&str>>();
//   let mut args = diff_cmd_and_args.iter().skip(1).collect::<Vec<&str>>();
//   let mut args = diff_cmd_and_args;
//   args.push(file1.as_str().unwrap());
//   args.push(file2.as_str().unwrap());

//   let mut cmd = Command::new(diff_cmd);
//   for arg in args.iter() {
//      cmd.arg(arg);
//   }
//
//   try!(cmd.output());
   Ok(())
}

/// Moves `file` into the trash directory of confsolve.
fn move_to_trash(file: &Path) -> AppResult<()>
{
   let filename = try!(file.filename_str()
      .ok_or(AppError::from_string(format!("Couldn't get filename from path '{}'!", file.display()))));

   let mut trash_file = try!(trash_dir());
   trash_file.set_filename(filename);

   try!(copy(file, &trash_file));
   try!(unlink(file));

   Ok(())
}

/// Returns the trash directory of confsolve, where all deleted/moved files are put into.
fn trash_dir() -> AppResult<Path>
{
   let mut dir = try!(appdirs::cache("confsolve")
      .ok_or(AppError::from_string(format!("Couldn't get cache directory!"))));

   dir.push("trash");
   if ! dir.is_dir() {
      try!(mkdir_recursive(&dir, io::USER_RWX));
   }

   Ok(dir)
}

fn print_runtime_help() -> AppResult<()>
{
   let trash_dir = try!(trash_dir());
   let dir_str   = trash_dir.display();

   println!("
Runtime Options:
   (T)ake File (NUM) => By pressing 't' and a number (e.g 't1'), the conflicting file with the
                        number NUM is used as the new version. A copy of the
                        current file and the other conflicting files is put
                        into the trash directory '{}'.

   (M)ove to Trash   => By pressing 'm', all conflicting files are
                        moved into the trash directory '{}'.

   Show (D)iff (NUM) => By pressing 'd' and a number (e.g 'd1'), the difference between the
                        current file and the conflicting file NUM is shown.
                        If there's only one conflicting file, then only pressing
                        'd' is sufficient.
                        By pressing 'd' and two numbers (e.g 'd1 2'), the difference between
                        the two conflicting files is shown.
                        The diff tool can be specified by the user by setting the environment
                        variable 'CONFSOLVE_DIFF'. The default diff tool is 'gvimdiff -f'.

   (S)kip            => By pressing 's', the current conflict is skipped
                        and the next one is shown.

   (Q)uit            => By pressing 'q', the application is quit.

   (H)elp            => By pressing 'h', this help is printed.\n", dir_str, dir_str);

   Ok(())
}
<|endoftext|>