<commit_before>/*
 * Copyright (c) 2014, David Renshaw (dwrenshaw@gmail.com)
 *
 * See the LICENSE file in the capnproto-rust root directory.
 */

use capnp::any::{AnyPointer};
use capnp::capability;
use capnp::capability::{RemotePromise, RequestHook, ClientHook, PipelineHook, Request, PipelineOp};
use capnp::common;
use capnp::layout::{FromStructReader, FromStructBuilder, HasStructSize};
use capnp::message::{DEFAULT_READER_OPTIONS, MessageReader, MessageBuilder, MallocMessageBuilder};
use capnp::serialize;
use capnp::serialize::{OwnedSpaceMessageReader};
use std;
use std::hashmap::HashMap;
use rpc_capnp::{Message, Return, CapDescriptor, PromisedAnswer};

type QuestionId = u32;
type AnswerId = QuestionId;
type ExportId = u32;
type ImportId = ExportId;

pub struct Question {
    chan : std::comm::Chan<~OwnedSpaceMessageReader>,
    is_awaiting_return : bool,
}

pub struct Answer {
    result_exports : ~[ExportId]
}

pub struct Export;

pub struct Import;

pub struct ImportTable<T> {
    slots : HashMap<u32, T>,
}

impl <T> ImportTable<T> {
    pub fn new() -> ImportTable<T> {
        ImportTable { slots : HashMap::new() }
    }
}

pub struct ExportTable<T> {
    slots : ~[T],
}

impl <T> ExportTable<T> {
    pub fn new() -> ExportTable<T> {
        ExportTable { slots : ~[] }
    }

    pub fn next(&mut self) -> u32 {
        fail!()
    }
}

pub struct RpcConnectionState {
    exports : ExportTable<Export>,
    questions : ExportTable<Question>,
    answers : ImportTable<Answer>,
    imports : ImportTable<Import>,
}

impl RpcConnectionState {
    pub fn new() -> RpcConnectionState {
        RpcConnectionState {
            exports : ExportTable::new(),
            questions : ExportTable::new(),
            answers : ImportTable::new(),
            imports : ImportTable::new(),
        }
    }

    pub fn run<T : std::io::Reader + Send, U : std::io::Writer + Send>(
        self, inpipe: T, outpipe: U)
         -> std::comm::SharedChan<RpcEvent> {

        let (port, chan) = std::comm::SharedChan::<RpcEvent>::new();

        let listener_chan = chan.clone();

        spawn(proc() {
                let mut r = inpipe;
                loop {
                    let message = box serialize::new_reader(
                        &mut r,
                        DEFAULT_READER_OPTIONS);
                    listener_chan.send(IncomingMessage(message));
                }
            });

        let loop_chan = chan.clone();

        spawn(proc() {
                let RpcConnectionState {mut questions, exports, answers, imports} = self;
                let mut outpipe = outpipe;
                loop {
                    match port.recv() {
                        IncomingMessage(mut message) => {
                            let mut the_cap_table : ~[Option<~ClientHook>] = ~[];
                            let mut question = None::<u32>;
                            // populate the cap table
                            {
                                let root = message.get_root::<Message::Reader>();

                                match root.which() {
                                    Some(Message::Return(ret)) => {
                                        println!("got a return with answer id {}", ret.get_answer_id());

                                        match ret.which() {
                                            Some(Return::Results(payload)) => {
                                                println!("with a payload");
                                                let cap_table = payload.get_cap_table();
                                                for ii in range(0, cap_table.size()) {
                                                    match cap_table[ii].which() {
                                                        Some(CapDescriptor::None(())) => {
                                                            the_cap_table.push(None)
                                                        }
                                                        Some(CapDescriptor::SenderHosted(id)) => {
                                                            the_cap_table.push(Some(
                                                                    (box ImportClient {
                                                                            channel : loop_chan.clone(),
                                                                            import_id : id})
                                                                        as ~ClientHook));
                                                            println!("sender hosted: {}", id);
                                                        }
                                                        _ => {}
                                                    }
                                                }
                                            }
                                            Some(Return::Exception(e)) => {
                                                println!("exception: {}", e.get_reason());
                                            }
                                            _ => {}
                                        }

                                        question = Some(ret.get_answer_id());
                                    }
                                    Some(Message::Unimplemented(_)) => {
                                        println!("unimplemented");
                                    }
                                    Some(Message::Abort(exc)) => {
                                        println!("abort: {}", exc.get_reason());
                                    }
                                    None => {
                                        println!("Nothing there");
                                    }
                                    _ => {
                                        println!("something else");
                                    }
                                }
                            }
                            message.init_cap_table(the_cap_table);

                            match question {
                                Some(id) => {
                                    questions.slots[id].chan.try_send(message);
                                }
                                None => {}
                            }

                        }
                        OutgoingMessage(mut m, chan) => {
                            let root = m.get_root::<Message::Builder>();
                            // add a question to the question table
                            match root.which() {
                                Some(Message::Which::Return(_)) => {}
                                Some(Message::Which::Call(call)) => {
                                    call.set_question_id(questions.slots.len() as u32);
                                    questions.slots.push(Question {is_awaiting_return : true,
                                                                   chan : chan} );
                                }
                                Some(Message::Which::Restore(res)) => {
                                    res.set_question_id(questions.slots.len() as u32);
                                    questions.slots.push(Question {is_awaiting_return : true,
                                                                   chan : chan} );
                                }
                                _ => {
                                    error!("NONE OF THOSE");
                                }
                            }

                            // send
                            serialize::write_message(&mut outpipe, m);
                        }
                        _ => {
                            println!("got another event");
                        }
                    }
                }});
        return chan;
    }
}

pub struct ImportClient {
    priv channel : std::comm::SharedChan<RpcEvent>,
    import_id : ImportId,
}

impl ClientHook for ImportClient {
    fn copy(&self) -> ~ClientHook {
        (box ImportClient {channel : self.channel.clone(),
                           import_id : self.import_id}) as ~ClientHook
    }

    fn new_call(&self, interface_id : u64, method_id : u16,
                _size_hint : Option<common::MessageSize>)
                -> capability::Request<AnyPointer::Builder, AnyPointer::Reader> {
        let mut message = box MallocMessageBuilder::new_default();
        {
            let root : Message::Builder = message.get_root();
            let call = root.init_call();
            call.set_interface_id(interface_id);
            call.set_method_id(method_id);
            let target = call.init_target();
            target.set_imported_cap(self.import_id);
        }
        let hook = box RpcRequest { channel : self.channel.clone(),
                                    message : message };
        Request::new(hook as ~RequestHook)
    }
}

pub struct PipelineClient {
    priv channel : std::comm::SharedChan<RpcEvent>,
    ops : ~[PipelineOp::Type],
//    question_id_port : std::comm::Port<ImportId>,
}

impl ClientHook for PipelineClient {
    fn copy(&self) -> ~ClientHook {
        (~PipelineClient { channel : self.channel.clone(),
                           ops : self.ops.clone() }) as ~ClientHook
    }

    fn new_call(&self, interface_id : u64, method_id : u16,
                _size_hint : Option<common::MessageSize>)
                -> capability::Request<AnyPointer::Builder, AnyPointer::Reader> {
        let mut message = box MallocMessageBuilder::new_default();
        {
            let root : Message::Builder = message.get_root();
            let call = root.init_call();
            call.set_interface_id(interface_id);
            call.set_method_id(method_id);
            let target = call.init_target();
            let promised_answer = target.init_promised_answer();
            promised_answer.set_question_id(0); //XXX
            let transform = promised_answer.init_transform(self.ops.len());
            for ii in range(0, self.ops.len()) {
                match self.ops[ii] {
                    PipelineOp::Noop => transform[ii].set_noop(()),
                    PipelineOp::GetPointerField(idx) => transform[ii].set_get_pointer_field(idx),
                }
            }
        }
        let hook = box RpcRequest { channel : self.channel.clone(),
                                    message : message };
        Request::new(hook as ~RequestHook)
    }
}

pub struct RpcRequest {
    priv channel : std::comm::SharedChan<RpcEvent>,
    priv message : ~MallocMessageBuilder,
}

impl RequestHook for RpcRequest {
    fn message<'a>(&'a mut self) -> &'a mut MallocMessageBuilder {
        &mut *self.message
    }
    fn send(~self) -> RemotePromise<AnyPointer::Reader> {
        let (port, chan) = std::comm::Chan::<~OwnedSpaceMessageReader>::new();

        let ~RpcRequest { channel, message } = self;
        channel.send(OutgoingMessage(message, chan));

        RemotePromise {port : port, result : None}
    }
}

pub struct RpcPipeline {
    _dummy : ()
}

impl PipelineHook for RpcPipeline {
    fn copy(&self) -> ~PipelineHook {
        fail!()
    }
    fn get_pipelined_cap(&self, ops : ~[PipelineOp::Type]) -> ~ClientHook {
        fail!()
    }
}

pub trait InitParams<'a, T> {
    fn init_params(&'a mut self) -> T;
}

impl <'a, Params : FromStructBuilder<'a> + HasStructSize, Results> InitParams<'a, Params>
for Request<Params, Results> {
    fn init_params(&'a mut self) -> Params {
        let message : Message::Builder = self.hook.message().get_root();
        match message.which() {
            Some(Message::Which::Call(call)) => {
                let params = call.init_params();
                params.get_content().init_as_struct()
            }
            _ => fail!(),
        }
    }
}

pub trait WaitForContent<'a, T> {
    fn wait(&'a mut self) -> T;
}

impl <'a, Results : FromStructReader<'a>> WaitForContent<'a, Results> for RemotePromise<Results> {
    fn wait(&'a mut self) -> Results {
        let message = self.port.recv();
        self.result = Some(message);
        match self.result {
            None => unreachable!(),
            Some(ref message) => {
                let root : Message::Reader = message.get_root();
                match root.which() {
                    Some(Message::Return(ret)) => {
                        match ret.which() {
                            Some(Return::Results(res)) => {
                                res.get_content().get_as_struct()
                            }
                            _ => fail!(),
                        }
                    }
                    _ => {fail!()}
                }
            }
        }
    }
}

pub enum RpcEvent {
    Nothing,
    IncomingMessage(~serialize::OwnedSpaceMessageReader),
    OutgoingMessage(~MallocMessageBuilder, std::comm::Chan<~OwnedSpaceMessageReader>)
}

<commit_msg>put writing in its own proc too<commit_after>/*
 * Copyright (c) 2014, David Renshaw (dwrenshaw@gmail.com)
 *
 * See the LICENSE file in the capnproto-rust root directory.
 */

use capnp::any::{AnyPointer};
use capnp::capability;
use capnp::capability::{RemotePromise, RequestHook, ClientHook, PipelineHook, Request, PipelineOp};
use capnp::common;
use capnp::layout::{FromStructReader, FromStructBuilder, HasStructSize};
use capnp::message::{DEFAULT_READER_OPTIONS, MessageReader, MessageBuilder, MallocMessageBuilder};
use capnp::serialize;
use capnp::serialize::{OwnedSpaceMessageReader};
use std;
use std::hashmap::HashMap;
use rpc_capnp::{Message, Return, CapDescriptor, PromisedAnswer};

type QuestionId = u32;
type AnswerId = QuestionId;
type ExportId = u32;
type ImportId = ExportId;

pub struct Question {
    chan : std::comm::Chan<~OwnedSpaceMessageReader>,
    is_awaiting_return : bool,
}

pub struct Answer {
    result_exports : ~[ExportId]
}

pub struct Export;

pub struct Import;

pub struct ImportTable<T> {
    slots : HashMap<u32, T>,
}

impl <T> ImportTable<T> {
    pub fn new() -> ImportTable<T> {
        ImportTable { slots : HashMap::new() }
    }
}

pub struct ExportTable<T> {
    slots : ~[T],
}

impl <T> ExportTable<T> {
    pub fn new() -> ExportTable<T> {
        ExportTable { slots : ~[] }
    }

    pub fn next(&mut self) -> u32 {
        fail!()
    }
}

pub struct RpcConnectionState {
    exports : ExportTable<Export>,
    questions : ExportTable<Question>,
    answers : ImportTable<Answer>,
    imports : ImportTable<Import>,
}

impl RpcConnectionState {
    pub fn new() -> RpcConnectionState {
        RpcConnectionState {
            exports : ExportTable::new(),
            questions : ExportTable::new(),
            answers : ImportTable::new(),
            imports : ImportTable::new(),
        }
    }

    pub fn run<T : std::io::Reader + Send, U : std::io::Writer + Send>(
        self, inpipe: T, outpipe: U)
         -> std::comm::SharedChan<RpcEvent> {

        let (port, chan) = std::comm::SharedChan::<RpcEvent>::new();

        let listener_chan = chan.clone();

        spawn(proc() {
                let mut r = inpipe;
                loop {
                    let message = box serialize::new_reader(
                        &mut r,
                        DEFAULT_READER_OPTIONS);
                    listener_chan.send(IncomingMessage(message));
                }
            });

        let loop_chan = chan.clone();

        let (writer_port, writer_chan) = std::comm::Chan::<~MallocMessageBuilder>::new();

        spawn(proc() {
                let mut w = outpipe;
                loop {
                    let message = writer_port.recv();
                    serialize::write_message(&mut w, message);
                }
            });

        spawn(proc() {
                let RpcConnectionState {mut questions, exports, answers, imports} = self;
                loop {
                    match port.recv() {
                        IncomingMessage(mut message) => {
                            let mut the_cap_table : ~[Option<~ClientHook>] = ~[];
                            let mut question = None::<u32>;
                            // populate the cap table
                            {
                                let root = message.get_root::<Message::Reader>();

                                match root.which() {
                                    Some(Message::Return(ret)) => {
                                        println!("got a return with answer id {}", ret.get_answer_id());

                                        match ret.which() {
                                            Some(Return::Results(payload)) => {
                                                println!("with a payload");
                                                let cap_table = payload.get_cap_table();
                                                for ii in range(0, cap_table.size()) {
                                                    match cap_table[ii].which() {
                                                        Some(CapDescriptor::None(())) => {
                                                            the_cap_table.push(None)
                                                        }
                                                        Some(CapDescriptor::SenderHosted(id)) => {
                                                            the_cap_table.push(Some(
                                                                    (box ImportClient {
                                                                            channel : loop_chan.clone(),
                                                                            import_id : id})
                                                                        as ~ClientHook));
                                                            println!("sender hosted: {}", id);
                                                        }
                                                        _ => {}
                                                    }
                                                }
                                            }
                                            Some(Return::Exception(e)) => {
                                                println!("exception: {}", e.get_reason());
                                            }
                                            _ => {}
                                        }

                                        question = Some(ret.get_answer_id());
                                    }
                                    Some(Message::Unimplemented(_)) => {
                                        println!("unimplemented");
                                    }
                                    Some(Message::Abort(exc)) => {
                                        println!("abort: {}", exc.get_reason());
                                    }
                                    None => {
                                        println!("Nothing there");
                                    }
                                    _ => {
                                        println!("something else");
                                    }
                                }
                            }
                            message.init_cap_table(the_cap_table);

                            match question {
                                Some(id) => {
                                    questions.slots[id].chan.try_send(message);
                                }
                                None => {}
                            }

                        }
                        OutgoingMessage(mut m, chan) => {
                            let root = m.get_root::<Message::Builder>();
                            // add a question to the question table
                            match root.which() {
                                Some(Message::Which::Return(_)) => {}
                                Some(Message::Which::Call(call)) => {
                                    call.set_question_id(questions.slots.len() as u32);
                                    questions.slots.push(Question {is_awaiting_return : true,
                                                                   chan : chan} );
                                }
                                Some(Message::Which::Restore(res)) => {
                                    res.set_question_id(questions.slots.len() as u32);
                                    questions.slots.push(Question {is_awaiting_return : true,
                                                                   chan : chan} );
                                }
                                _ => {
                                    error!("NONE OF THOSE");
                                }
                            }

                            // send
                            writer_chan.send(m);
                        }
                        _ => {
                            println!("got another event");
                        }
                    }
                }});
        return chan;
    }
}

pub struct ImportClient {
    priv channel : std::comm::SharedChan<RpcEvent>,
    import_id : ImportId,
}

impl ClientHook for ImportClient {
    fn copy(&self) -> ~ClientHook {
        (box ImportClient {channel : self.channel.clone(),
                           import_id : self.import_id}) as ~ClientHook
    }

    fn new_call(&self, interface_id : u64, method_id : u16,
                _size_hint : Option<common::MessageSize>)
                -> capability::Request<AnyPointer::Builder, AnyPointer::Reader> {
        let mut message = box MallocMessageBuilder::new_default();
        {
            let root : Message::Builder = message.get_root();
            let call = root.init_call();
            call.set_interface_id(interface_id);
            call.set_method_id(method_id);
            let target = call.init_target();
            target.set_imported_cap(self.import_id);
        }
        let hook = box RpcRequest { channel : self.channel.clone(),
                                    message : message };
        Request::new(hook as ~RequestHook)
    }
}

pub struct PipelineClient {
    priv channel : std::comm::SharedChan<RpcEvent>,
    ops : ~[PipelineOp::Type],
//    question_id_port : std::comm::Port<ImportId>,
}

impl ClientHook for PipelineClient {
    fn copy(&self) -> ~ClientHook {
        (~PipelineClient { channel : self.channel.clone(),
                           ops : self.ops.clone() }) as ~ClientHook
    }

    fn new_call(&self, interface_id : u64, method_id : u16,
                _size_hint : Option<common::MessageSize>)
                -> capability::Request<AnyPointer::Builder, AnyPointer::Reader> {
        let mut message = box MallocMessageBuilder::new_default();
        {
            let root : Message::Builder = message.get_root();
            let call = root.init_call();
            call.set_interface_id(interface_id);
            call.set_method_id(method_id);
            let target = call.init_target();
            let promised_answer = target.init_promised_answer();
            promised_answer.set_question_id(0); //XXX
            let transform = promised_answer.init_transform(self.ops.len());
            for ii in range(0, self.ops.len()) {
                match self.ops[ii] {
                    PipelineOp::Noop => transform[ii].set_noop(()),
                    PipelineOp::GetPointerField(idx) => transform[ii].set_get_pointer_field(idx),
                }
            }
        }
        let hook = box RpcRequest { channel : self.channel.clone(),
                                    message : message };
        Request::new(hook as ~RequestHook)
    }
}

pub struct RpcRequest {
    priv channel : std::comm::SharedChan<RpcEvent>,
    priv message : ~MallocMessageBuilder,
}

impl RequestHook for RpcRequest {
    fn message<'a>(&'a mut self) -> &'a mut MallocMessageBuilder {
        &mut *self.message
    }
    fn send(~self) -> RemotePromise<AnyPointer::Reader> {
        let (port, chan) = std::comm::Chan::<~OwnedSpaceMessageReader>::new();

        let ~RpcRequest { channel, message } = self;
        channel.send(OutgoingMessage(message, chan));

        RemotePromise {port : port, result : None}
    }
}

pub struct RpcPipeline {
    _dummy : ()
}

impl PipelineHook for RpcPipeline {
    fn copy(&self) -> ~PipelineHook {
        fail!()
    }
    fn get_pipelined_cap(&self, ops : ~[PipelineOp::Type]) -> ~ClientHook {
        fail!()
    }
}

pub trait InitParams<'a, T> {
    fn init_params(&'a mut self) -> T;
}

impl <'a, Params : FromStructBuilder<'a> + HasStructSize, Results> InitParams<'a, Params>
for Request<Params, Results> {
    fn init_params(&'a mut self) -> Params {
        let message : Message::Builder = self.hook.message().get_root();
        match message.which() {
            Some(Message::Which::Call(call)) => {
                let params = call.init_params();
                params.get_content().init_as_struct()
            }
            _ => fail!(),
        }
    }
}

pub trait WaitForContent<'a, T> {
    fn wait(&'a mut self) -> T;
}

impl <'a, Results : FromStructReader<'a>> WaitForContent<'a, Results> for RemotePromise<Results> {
    fn wait(&'a mut self) -> Results {
        let message = self.port.recv();
        self.result = Some(message);
        match self.result {
            None => unreachable!(),
            Some(ref message) => {
                let root : Message::Reader = message.get_root();
                match root.which() {
                    Some(Message::Return(ret)) => {
                        match ret.which() {
                            Some(Return::Results(res)) => {
                                res.get_content().get_as_struct()
                            }
                            _ => fail!(),
                        }
                    }
                    _ => {fail!()}
                }
            }
        }
    }
}

pub enum RpcEvent {
    Nothing,
    IncomingMessage(~serialize::OwnedSpaceMessageReader),
    OutgoingMessage(~MallocMessageBuilder, std::comm::Chan<~OwnedSpaceMessageReader>)
}

<|endoftext|>


<commit_before>use std::io::{ Write, LineWriter, Error };
use std::result::Result;
use std::fs::File;
use std::collections::HashMap;

static STACK_POINTER: &'static str = "SP";

lazy_static! {
    static ref MEMORY_SEGMENT_BASE: HashMap<&'static str, &'static str> = {
        let mut map = HashMap::new();
        map.insert("local", "LCL");
        map.insert("argument", "ARG");
        map.insert("this", "THIS");
        map.insert("that", "THAT");
        map.insert("temp", "5"); // this is very special segment
        map
    };
}

pub struct CodeWriter {
    target_descriptor: LineWriter<File>
}

impl CodeWriter {
    // Opens the output file, and gets ready to write into it
    pub fn new(target_file : String) -> Result<Self, Error> {
        let file = File::create(target_file)?;
        let line_writer = LineWriter::new(file);

        Ok( CodeWriter{ target_descriptor: line_writer } )
    }

    // Writes to the file assembly code which represents current arithmetic command
    pub fn write_arithmetic(&mut self, raw : &String, opcode : &String) {
        let helper_comment = format!("// {}\n", raw);
        self.target_descriptor.write(&helper_comment.into_bytes());

        let code = format!(
            "@{stack_pointer}
M=M-1
A=M
D=M // take first argument for operation

@{register1}
M=D

@{stack_pointer}
M=M-1
A=M
D=M // take second argument for operation

@{register1}
D=D-M // perform your action

@{stack_pointer}
A=M // move to memory cell pointed by stack_pointer
M=D // put operation result to this cell",
            stack_pointer=STACK_POINTER,
            register1="R13"
        );
        self.target_descriptor.write(&code.into_bytes());
    }

    // Writes assembly for push and pop commands
    pub fn write_push(&mut self, raw : &String, segment : &String, index : &usize) {
        let helper_comment = format!("// {}\n", raw);
        self.target_descriptor.write(&helper_comment.into_bytes());

        if segment.starts_with("constant") {
            let at = format!("@{}\n", index);
            self.target_descriptor.write(&at.into_bytes());

            let action = format!("D=A\n");
            self.target_descriptor.write(&action.into_bytes());

            let set_stack_pointer = format!("@{}\n", STACK_POINTER);
            self.target_descriptor.write(&set_stack_pointer.into_bytes());

            let move_address_to_current_stack_pointer = format!("A=M\n");
            self.target_descriptor.write(&move_address_to_current_stack_pointer.into_bytes());

            let move_value_to_stack = format!("M=D\n");
            self.target_descriptor.write(&move_value_to_stack.into_bytes());

            let set_stack_pointer = format!("@{}\n", STACK_POINTER);
            self.target_descriptor.write(&set_stack_pointer.into_bytes());

            let bump_stack_pointer = format!("M=M+1\n");
            self.target_descriptor.write(&bump_stack_pointer.into_bytes());
        } else if segment.starts_with("local") || segment.starts_with("argument") ||
        segment.starts_with("this") || segment.starts_with("that") || segment.starts_with("temp") {
            let code = format!(
                "@{index}
D=A // save shift from base to D register
@{local_base}
D=D+M // save shift + base_address to D register
A=D // set memory to shift + base_address
D=M // save content of memory to D register
@{stack_pointer} // set addr to stack_pointer
A=M // move to this cell
M=D // set content of this cell in stack to be conent of memory segment
@{stack_pointer}
M=M+1 // bump stack_pointe, so it points to new empty mem_cell
",
                index=index,
                local_base=MEMORY_SEGMENT_BASE.get::<str>(&segment).unwrap(),
                stack_pointer=STACK_POINTER
            );
            self.target_descriptor.write(&code.into_bytes());

        } else if segment.starts_with("static") {
            let code = format!(
                    "@Foo.{index}
D=M // save static var to D reg
@{stack_pointer}
M=D // go to current stack pointer
A=M // follow pointer to current free cell
M=D // fill this cell with static var value
@{stack_pointer}
M=M+1 // bump stack pointer
",
                index=index,
                stack_pointer=STACK_POINTER
            );
            self.target_descriptor.write(&code.into_bytes());
        } else if segment.starts_with("pointer") {
            let code = format!(
                "@{segment}
D=M // put this/that pointer base into D

@{stack_pointer}
A=M // Follow stack pointer to current free memory cell
M=D // Set value of this cell to be this/that

@{stack_pointer}
M=M+1 // bump stack pointer
",
                segment=if *index == (0 as usize) {
                    MEMORY_SEGMENT_BASE.get::<str>("this").unwrap()
                } else {
                    MEMORY_SEGMENT_BASE.get::<str>("that").unwrap()
                },
                stack_pointer=STACK_POINTER
            );

            self.target_descriptor.write(&code.into_bytes());
        }
    }

    // Writes assembly for push and pop commands
    pub fn write_pop(&mut self, raw : &String, segment : &String, index : &usize) {
        let helper_comment = format!("// {}\n", raw);
        self.target_descriptor.write(&helper_comment.into_bytes());

        if segment.starts_with("this") || segment.starts_with("that") ||
            segment.starts_with("local") || segment.starts_with("argument") || segment.starts_with("temp") {
                // works for local segment
                let code = format!(
                    "@{index}
D=A // save shift from base to D register
@{local_base}
D=D+M // save shift + base_address to D register
@{tmp_register}
M=D // save pop target to @R13
@{stack_pointer} // set addr to stack_pointer
M=M-1 // unbump stack_pointer, so it points to last real value
A=M // follow stack pointer and see what we have there
D=M // put
@{tmp_register}
A=M // jump to target we saved in R13
M=D // put value popped out of a stack to target memory cell
",
                    index=index,
                    local_base=MEMORY_SEGMENT_BASE.get::<str>(&segment).unwrap(),
                    tmp_register="R13",
                    stack_pointer=STACK_POINTER
                );
                self.target_descriptor.write(&code.into_bytes());
            } else if segment.starts_with("static") {
                let code = format!(
                    "@{stack_pointer}
M=M-1 // unbump stack pointer
A=M // follow pointer
D=M // save it in D register
@Foo.{index}
M=D // save D reg value in static var
",
                    index=index,
                    stack_pointer=STACK_POINTER
                );
                self.target_descriptor.write(&code.into_bytes());
            } else if segment.starts_with("pointer") {
                let code = format!(
                    "@{stack_pointer}
M=M-1 // unbump stack pointer to point to last real value
A=M // Follow stack pointer to memory cell
D=M // Set D register to contain required value

@{segment}
M=D // put D reg value to this/that
",
                    segment=if *index == (0 as usize) {
                        MEMORY_SEGMENT_BASE.get::<str>("this").unwrap()
                    } else {
                        MEMORY_SEGMENT_BASE.get::<str>("that").unwrap()
                    },
                    stack_pointer=STACK_POINTER
                );

                self.target_descriptor.write(&code.into_bytes());
            }
    }
}
<commit_msg>comment for future me to remind the place where to put action<commit_after>use std::io::{ Write, LineWriter, Error };
use std::result::Result;
use std::fs::File;
use std::collections::HashMap;

static STACK_POINTER: &'static str = "SP";

lazy_static! {
    static ref MEMORY_SEGMENT_BASE: HashMap<&'static str, &'static str> = {
        let mut map = HashMap::new();
        map.insert("local", "LCL");
        map.insert("argument", "ARG");
        map.insert("this", "THIS");
        map.insert("that", "THAT");
        map.insert("temp", "5"); // this is very special segment
        map
    };
}

pub struct CodeWriter {
    target_descriptor: LineWriter<File>
}

impl CodeWriter {
    // Opens the output file, and gets ready to write into it
    pub fn new(target_file : String) -> Result<Self, Error> {
        let file = File::create(target_file)?;
        let line_writer = LineWriter::new(file);

        Ok( CodeWriter{ target_descriptor: line_writer } )
    }

    // Writes to the file assembly code which represents current arithmetic command
    pub fn write_arithmetic(&mut self, raw : &String, opcode : &String) {
        let helper_comment = format!("// {}\n", raw);
        self.target_descriptor.write(&helper_comment.into_bytes());

        let code = format!(
            "@{stack_pointer}
M=M-1
A=M
D=M // take first argument for operation

@{register1}
M=D

@{stack_pointer}
M=M-1
A=M
D=M // take second argument for operation

@{register1}
D=D-M // perform your action (THIS LINE WILL DIFFER FOR DIFFERENT OPERATIONS)

@{stack_pointer}
A=M // move to memory cell pointed by stack_pointer
M=D // put operation result to this cell",
            stack_pointer=STACK_POINTER,
            register1="R13"
        );
        self.target_descriptor.write(&code.into_bytes());
    }

    // Writes assembly for push and pop commands
    pub fn write_push(&mut self, raw : &String, segment : &String, index : &usize) {
        let helper_comment = format!("// {}\n", raw);
        self.target_descriptor.write(&helper_comment.into_bytes());

        if segment.starts_with("constant") {
            let at = format!("@{}\n", index);
            self.target_descriptor.write(&at.into_bytes());

            let action = format!("D=A\n");
            self.target_descriptor.write(&action.into_bytes());

            let set_stack_pointer = format!("@{}\n", STACK_POINTER);
            self.target_descriptor.write(&set_stack_pointer.into_bytes());

            let move_address_to_current_stack_pointer = format!("A=M\n");
            self.target_descriptor.write(&move_address_to_current_stack_pointer.into_bytes());

            let move_value_to_stack = format!("M=D\n");
            self.target_descriptor.write(&move_value_to_stack.into_bytes());

            let set_stack_pointer = format!("@{}\n", STACK_POINTER);
            self.target_descriptor.write(&set_stack_pointer.into_bytes());

            let bump_stack_pointer = format!("M=M+1\n");
            self.target_descriptor.write(&bump_stack_pointer.into_bytes());
        } else if segment.starts_with("local") || segment.starts_with("argument") ||
        segment.starts_with("this") || segment.starts_with("that") || segment.starts_with("temp") {
            let code = format!(
                "@{index}
D=A // save shift from base to D register
@{local_base}
D=D+M // save shift + base_address to D register
A=D // set memory to shift + base_address
D=M // save content of memory to D register
@{stack_pointer} // set addr to stack_pointer
A=M // move to this cell
M=D // set content of this cell in stack to be conent of memory segment
@{stack_pointer}
M=M+1 // bump stack_pointe, so it points to new empty mem_cell
",
                index=index,
                local_base=MEMORY_SEGMENT_BASE.get::<str>(&segment).unwrap(),
                stack_pointer=STACK_POINTER
            );
            self.target_descriptor.write(&code.into_bytes());

        } else if segment.starts_with("static") {
            let code = format!(
                    "@Foo.{index}
D=M // save static var to D reg
@{stack_pointer}
M=D // go to current stack pointer
A=M // follow pointer to current free cell
M=D // fill this cell with static var value
@{stack_pointer}
M=M+1 // bump stack pointer
",
                index=index,
                stack_pointer=STACK_POINTER
            );
            self.target_descriptor.write(&code.into_bytes());
        } else if segment.starts_with("pointer") {
            let code = format!(
                "@{segment}
D=M // put this/that pointer base into D

@{stack_pointer}
A=M // Follow stack pointer to current free memory cell
M=D // Set value of this cell to be this/that

@{stack_pointer}
M=M+1 // bump stack pointer
",
                segment=if *index == (0 as usize) {
                    MEMORY_SEGMENT_BASE.get::<str>("this").unwrap()
                } else {
                    MEMORY_SEGMENT_BASE.get::<str>("that").unwrap()
                },
                stack_pointer=STACK_POINTER
            );

            self.target_descriptor.write(&code.into_bytes());
        }
    }

    // Writes assembly for push and pop commands
    pub fn write_pop(&mut self, raw : &String, segment : &String, index : &usize) {
        let helper_comment = format!("// {}\n", raw);
        self.target_descriptor.write(&helper_comment.into_bytes());

        if segment.starts_with("this") || segment.starts_with("that") ||
            segment.starts_with("local") || segment.starts_with("argument") || segment.starts_with("temp") {
                // works for local segment
                let code = format!(
                    "@{index}
D=A // save shift from base to D register
@{local_base}
D=D+M // save shift + base_address to D register
@{tmp_register}
M=D // save pop target to @R13
@{stack_pointer} // set addr to stack_pointer
M=M-1 // unbump stack_pointer, so it points to last real value
A=M // follow stack pointer and see what we have there
D=M // put
@{tmp_register}
A=M // jump to target we saved in R13
M=D // put value popped out of a stack to target memory cell
",
                    index=index,
                    local_base=MEMORY_SEGMENT_BASE.get::<str>(&segment).unwrap(),
                    tmp_register="R13",
                    stack_pointer=STACK_POINTER
                );
                self.target_descriptor.write(&code.into_bytes());
            } else if segment.starts_with("static") {
                let code = format!(
                    "@{stack_pointer}
M=M-1 // unbump stack pointer
A=M // follow pointer
D=M // save it in D register
@Foo.{index}
M=D // save D reg value in static var
",
                    index=index,
                    stack_pointer=STACK_POINTER
                );
                self.target_descriptor.write(&code.into_bytes());
            } else if segment.starts_with("pointer") {
                let code = format!(
                    "@{stack_pointer}
M=M-1 // unbump stack pointer to point to last real value
A=M // Follow stack pointer to memory cell
D=M // Set D register to contain required value

@{segment}
M=D // put D reg value to this/that
",
                    segment=if *index == (0 as usize) {
                        MEMORY_SEGMENT_BASE.get::<str>("this").unwrap()
                    } else {
                        MEMORY_SEGMENT_BASE.get::<str>("that").unwrap()
                    },
                    stack_pointer=STACK_POINTER
                );

                self.target_descriptor.write(&code.into_bytes());
            }
    }
}
<|endoftext|>


<commit_before>//! TODO Documentation

use std::{mem, ptr, slice};
use std::marker::PhantomData;

use wlroots_sys::{wlr_xcursor, wlr_xcursor_image, wlr_xcursor_theme, wlr_xcursor_theme_destroy,
                  wlr_xcursor_theme_get_cursor, wlr_xcursor_theme_load};

use utils::safe_as_cstring;

#[derive(Debug)]
pub struct XCursorTheme {
    theme: *mut wlr_xcursor_theme
}

#[derive(Debug)]
pub struct XCursor<'theme> {
    xcursor: *mut wlr_xcursor,
    phantom: PhantomData<&'theme XCursorTheme>
}

#[derive(Debug)]
pub struct XCursorImage<'cursor> {
    pub width: u32,
    pub height: u32,
    pub hotspot_x: u32,
    pub hotspot_y: u32,
    pub delay: u32,
    pub buffer: &'cursor [u8]
}

impl XCursorTheme {
    /// If no name is given, defaults to "default".
    pub fn load_theme(name: Option<String>, size: i32) -> Option<Self> {
        unsafe {
            let name_str = name.map(safe_as_cstring);
            let name_ptr = name_str.map(|s| s.as_ptr()).unwrap_or(ptr::null_mut());
            let theme = wlr_xcursor_theme_load(name_ptr, size);
            if theme.is_null() {
                None
            } else {
                Some(XCursorTheme { theme })
            }
        }
    }

    pub fn get_cursor<'theme>(&'theme self, name: String) -> Option<XCursor<'theme>> {
        let name_str = safe_as_cstring(name);
        let xcursor =
            unsafe { wlr_xcursor_theme_get_cursor(self.theme, name_str.as_ptr()) };
        if xcursor.is_null() {
            None
        } else {
            Some(XCursor { xcursor,
                           phantom: PhantomData })
        }
    }
}

impl Drop for XCursorTheme {
    fn drop(&mut self) {
        unsafe { wlr_xcursor_theme_destroy(self.theme) }
    }
}

impl<'theme> XCursor<'theme> {
    #[allow(dead_code)]
    pub(crate) unsafe fn as_ptr(&mut self) -> *mut wlr_xcursor {
        self.xcursor
    }

    pub fn images<'cursor>(&'cursor self) -> Vec<XCursorImage<'cursor>> {
        unsafe {
            let image_ptr = (*self.xcursor).images as *const *const wlr_xcursor_image;
            let length = (*self.xcursor).image_count;
            let cursors_slice: &'cursor [*const wlr_xcursor_image] =
                slice::from_raw_parts::<'cursor, *const wlr_xcursor_image>(image_ptr,
                                                                           length as usize);
            let mut result = Vec::with_capacity(cursors_slice.len());
            for cursor in cursors_slice {
                result.push(XCursorImage {
                    width: (**cursor).width,
                    height: (**cursor).height,
                    hotspot_x: (**cursor).hotspot_x,
                    hotspot_y: (**cursor).hotspot_y,
                    delay: (**cursor).delay,
                    buffer: slice::from_raw_parts::<'cursor, u8>(
                        (**cursor).buffer as *const u8,
                        (**cursor).width as usize * (**cursor).height as usize
                            * mem::size_of::<u32>()
                    )
                })
            }
            result
        }
    }
}
<commit_msg>Wrap rest of Xcursor/XcursorTheme functions<commit_after>//! TODO Documentation

use std::{mem, ptr, slice};
use std::marker::PhantomData;
use std::time::Duration;

use libc::{c_int, c_uint};
use wlroots_sys::{wlr_xcursor, wlr_xcursor_frame, wlr_xcursor_image, wlr_xcursor_theme,
                  wlr_xcursor_theme_destroy, wlr_xcursor_theme_get_cursor, wlr_xcursor_theme_load};

use utils::{c_to_rust_string, safe_as_cstring};

#[derive(Debug)]
pub struct XCursorTheme {
    theme: *mut wlr_xcursor_theme
}

#[derive(Debug)]
pub struct XCursor<'theme> {
    xcursor: *mut wlr_xcursor,
    phantom: PhantomData<&'theme XCursorTheme>
}

#[derive(Debug)]
pub struct XCursorImage<'cursor> {
    pub width: u32,
    pub height: u32,
    pub hotspot_x: u32,
    pub hotspot_y: u32,
    pub delay: u32,
    pub buffer: &'cursor [u8]
}

impl XCursorTheme {
    /// If no name is given, defaults to "default".
    pub fn load_theme(name: Option<String>, size: i32) -> Option<Self> {
        unsafe {
            let name_str = name.map(safe_as_cstring);
            let name_ptr = name_str.map(|s| s.as_ptr()).unwrap_or(ptr::null_mut());
            let theme = wlr_xcursor_theme_load(name_ptr, size);
            if theme.is_null() {
                None
            } else {
                Some(XCursorTheme { theme })
            }
        }
    }

    /// Get the name of this theme.
    ///
    /// If the name returned by wlroots was malformed, or non existant,
    /// then the value will be None.
    pub fn name(&self) -> Option<String> {
        unsafe { c_to_rust_string((*self.theme).name) }
    }

    /// Get the size of the images.
    pub fn size(&self) -> c_int {
        unsafe { (*self.theme).size }
    }

    /// Get the number of cursors in this theme.
    pub fn cursor_count(&self) -> c_uint {
        unsafe { (*self.theme).cursor_count }
    }

    /// Get the cursor with the provided name, if it exists.
    pub fn get_cursor<'theme>(&'theme self, name: String) -> Option<XCursor<'theme>> {
        let name_str = safe_as_cstring(name);
        let xcursor =
            unsafe { wlr_xcursor_theme_get_cursor(self.theme, name_str.as_ptr()) };
        if xcursor.is_null() {
            None
        } else {
            Some(XCursor { xcursor,
                           phantom: PhantomData })
        }
    }
}

impl Drop for XCursorTheme {
    fn drop(&mut self) {
        unsafe { wlr_xcursor_theme_destroy(self.theme) }
    }
}

impl<'theme> XCursor<'theme> {
    pub fn frame(&mut self, duration: Duration) -> c_int {
        unsafe {
            // TODO Is the correct unit of time?
            wlr_xcursor_frame(self.xcursor, duration.subsec_nanos())
        }
    }

    pub fn images<'cursor>(&'cursor self) -> Vec<XCursorImage<'cursor>> {
        unsafe {
            let image_ptr = (*self.xcursor).images as *const *const wlr_xcursor_image;
            let length = (*self.xcursor).image_count;
            let cursors_slice: &'cursor [*const wlr_xcursor_image] =
                slice::from_raw_parts::<'cursor, *const wlr_xcursor_image>(image_ptr,
                                                                           length as usize);
            let mut result = Vec::with_capacity(cursors_slice.len());
            for cursor in cursors_slice {
                result.push(XCursorImage {
                    width: (**cursor).width,
                    height: (**cursor).height,
                    hotspot_x: (**cursor).hotspot_x,
                    hotspot_y: (**cursor).hotspot_y,
                    delay: (**cursor).delay,
                    buffer: slice::from_raw_parts::<'cursor, u8>(
                        (**cursor).buffer as *const u8,
                        (**cursor).width as usize * (**cursor).height as usize
                            * mem::size_of::<u32>()
                    )
                })
            }
            result
        }
    }
}
<|endoftext|>


<commit_before>// This file is part of the uutils coreutils package.
//
// (c) Vsevolod Velichko <torkvemada@sorokdva.net>
// (c) Jian Zeng <anonymousknight96 AT gmail.com>
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

// spell-checker:ignore (ToDO) NEWROOT Userspec pstatus

#[macro_use]
extern crate uucore;
use clap::{crate_version, App, Arg};
use std::ffi::CString;
use std::io::Error;
use std::path::Path;
use std::process::Command;
use uucore::display::Quotable;
use uucore::libc::{self, chroot, setgid, setgroups, setuid};
use uucore::{entries, InvalidEncodingHandling};

static ABOUT: &str = "Run COMMAND with root directory set to NEWROOT.";
static SYNTAX: &str = "[OPTION]... NEWROOT [COMMAND [ARG]...]";

mod options {
    pub const NEWROOT: &str = "newroot";
    pub const USER: &str = "user";
    pub const GROUP: &str = "group";
    pub const GROUPS: &str = "groups";
    pub const USERSPEC: &str = "userspec";
    pub const COMMAND: &str = "command";
}

pub fn uumain(args: impl uucore::Args) -> i32 {
    let args = args
        .collect_str(InvalidEncodingHandling::ConvertLossy)
        .accept_any();

    let matches = uu_app().get_matches_from(args);

    let default_shell: &'static str = "/bin/sh";
    let default_option: &'static str = "-i";
    let user_shell = std::env::var("SHELL");

    let newroot: &Path = match matches.value_of(options::NEWROOT) {
        Some(v) => Path::new(v),
        None => crash!(
            1,
            "Missing operand: NEWROOT\nTry '{} --help' for more information.",
            uucore::execution_phrase()
        ),
    };

    if !newroot.is_dir() {
        crash!(
            1,
            "cannot change root directory to {}: no such directory",
            newroot.quote()
        );
    }

    let commands = match matches.values_of(options::COMMAND) {
        Some(v) => v.collect(),
        None => vec![],
    };

    // TODO: refactor the args and command matching
    // See: https://github.com/uutils/coreutils/pull/2365#discussion_r647849967
    let command: Vec<&str> = match commands.len() {
        0 => {
            let shell: &str = match user_shell {
                Err(_) => default_shell,
                Ok(ref s) => s.as_ref(),
            };
            vec![shell, default_option]
        }
        _ => commands,
    };

    set_context(newroot, &matches);

    assert!(!command.is_empty());
    let pstatus = Command::new(command[0])
        .args(&command[1..])
        .status()
        .unwrap_or_else(|e| {
            // TODO: Exit status:
            // 125 if chroot itself fails
            // 126 if command is found but cannot be invoked
            // 127 if command cannot be found
            crash!(
                1,
                "failed to run command {}: {}",
                command[0].to_string().quote(),
                e
            )
        });

    if pstatus.success() {
        0
    } else {
        pstatus.code().unwrap_or(-1)
    }
}

pub fn uu_app() -> App<'static, 'static> {
    App::new(uucore::util_name())
        .version(crate_version!())
        .about(ABOUT)
        .usage(SYNTAX)
        .arg(
            Arg::with_name(options::NEWROOT)
                .hidden(true)
                .required(true)
                .index(1),
        )
        .arg(
            Arg::with_name(options::USER)
                .short("u")
                .long(options::USER)
                .help("User (ID or name) to switch before running the program")
                .value_name("USER"),
        )
        .arg(
            Arg::with_name(options::GROUP)
                .short("g")
                .long(options::GROUP)
                .help("Group (ID or name) to switch to")
                .value_name("GROUP"),
        )
        .arg(
            Arg::with_name(options::GROUPS)
                .short("G")
                .long(options::GROUPS)
                .help("Comma-separated list of groups to switch to")
                .value_name("GROUP1,GROUP2..."),
        )
        .arg(
            Arg::with_name(options::USERSPEC)
                .long(options::USERSPEC)
                .help(
                    "Colon-separated user and group to switch to. \
                     Same as -u USER -g GROUP. \
                     Userspec has higher preference than -u and/or -g",
                )
                .value_name("USER:GROUP"),
        )
        .arg(
            Arg::with_name(options::COMMAND)
                .hidden(true)
                .multiple(true)
                .index(2),
        )
}

fn set_context(root: &Path, options: &clap::ArgMatches) {
    let userspec_str = options.value_of(options::USERSPEC);
    let user_str = options.value_of(options::USER).unwrap_or_default();
    let group_str = options.value_of(options::GROUP).unwrap_or_default();
    let groups_str = options.value_of(options::GROUPS).unwrap_or_default();
    let userspec = match userspec_str {
        Some(u) => {
            let s: Vec<&str> = u.split(':').collect();
            if s.len() != 2 || s.iter().any(|&spec| spec.is_empty()) {
                crash!(1, "invalid userspec: {}", u.quote())
            };
            s
        }
        None => Vec::new(),
    };

    let (user, group) = if userspec.is_empty() {
        (user_str, group_str)
    } else {
        (userspec[0], userspec[1])
    };

    enter_chroot(root);

    set_groups_from_str(groups_str);
    set_main_group(group);
    set_user(user);
}

fn enter_chroot(root: &Path) {
    std::env::set_current_dir(root).unwrap();
    let err = unsafe {
        chroot(CString::new(".").unwrap().as_bytes_with_nul().as_ptr() as *const libc::c_char)
    };
    if err != 0 {
        crash!(
            1,
            "cannot chroot to {}: {}",
            root.quote(),
            Error::last_os_error()
        )
    };
}

fn set_main_group(group: &str) {
    if !group.is_empty() {
        let group_id = match entries::grp2gid(group) {
            Ok(g) => g,
            _ => crash!(1, "no such group: {}", group.maybe_quote()),
        };
        let err = unsafe { setgid(group_id) };
        if err != 0 {
            crash!(
                1,
                "cannot set gid to {}: {}",
                group_id,
                Error::last_os_error()
            )
        }
    }
}

#[cfg(any(target_vendor = "apple", target_os = "freebsd"))]
fn set_groups(groups: Vec<libc::gid_t>) -> libc::c_int {
    unsafe { setgroups(groups.len() as libc::c_int, groups.as_ptr()) }
}

#[cfg(target_os = "linux")]
fn set_groups(groups: Vec<libc::gid_t>) -> libc::c_int {
    unsafe { setgroups(groups.len() as libc::size_t, groups.as_ptr()) }
}

fn set_groups_from_str(groups: &str) {
    if !groups.is_empty() {
        let groups_vec: Vec<libc::gid_t> = groups
            .split(',')
            .map(|x| match entries::grp2gid(x) {
                Ok(g) => g,
                _ => crash!(1, "no such group: {}", x),
            })
            .collect();
        let err = set_groups(groups_vec);
        if err != 0 {
            crash!(1, "cannot set groups: {}", Error::last_os_error())
        }
    }
}

fn set_user(user: &str) {
    if !user.is_empty() {
        let user_id = entries::usr2uid(user).unwrap();
        let err = unsafe { setuid(user_id as libc::uid_t) };
        if err != 0 {
            crash!(
                1,
                "cannot set user to {}: {}",
                user.maybe_quote(),
                Error::last_os_error()
            )
        }
    }
}
<commit_msg>chroot: move logic so it can be triggered by tests<commit_after>// This file is part of the uutils coreutils package.
//
// (c) Vsevolod Velichko <torkvemada@sorokdva.net>
// (c) Jian Zeng <anonymousknight96 AT gmail.com>
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

// spell-checker:ignore (ToDO) NEWROOT Userspec pstatus

#[macro_use]
extern crate uucore;
use clap::{crate_version, App, Arg};
use std::ffi::CString;
use std::io::Error;
use std::path::Path;
use std::process::Command;
use uucore::display::Quotable;
use uucore::libc::{self, chroot, setgid, setgroups, setuid};
use uucore::{entries, InvalidEncodingHandling};

static ABOUT: &str = "Run COMMAND with root directory set to NEWROOT.";
static SYNTAX: &str = "[OPTION]... NEWROOT [COMMAND [ARG]...]";

mod options {
    pub const NEWROOT: &str = "newroot";
    pub const USER: &str = "user";
    pub const GROUP: &str = "group";
    pub const GROUPS: &str = "groups";
    pub const USERSPEC: &str = "userspec";
    pub const COMMAND: &str = "command";
}

pub fn uumain(args: impl uucore::Args) -> i32 {
    let args = args
        .collect_str(InvalidEncodingHandling::ConvertLossy)
        .accept_any();

    let matches = uu_app().get_matches_from(args);

    let default_shell: &'static str = "/bin/sh";
    let default_option: &'static str = "-i";
    let user_shell = std::env::var("SHELL");

    let newroot: &Path = match matches.value_of(options::NEWROOT) {
        Some(v) => Path::new(v),
        None => crash!(
            1,
            "Missing operand: NEWROOT\nTry '{} --help' for more information.",
            uucore::execution_phrase()
        ),
    };

    if !newroot.is_dir() {
        crash!(
            1,
            "cannot change root directory to {}: no such directory",
            newroot.quote()
        );
    }

    let commands = match matches.values_of(options::COMMAND) {
        Some(v) => v.collect(),
        None => vec![],
    };

    // TODO: refactor the args and command matching
    // See: https://github.com/uutils/coreutils/pull/2365#discussion_r647849967
    let command: Vec<&str> = match commands.len() {
        0 => {
            let shell: &str = match user_shell {
                Err(_) => default_shell,
                Ok(ref s) => s.as_ref(),
            };
            vec![shell, default_option]
        }
        _ => commands,
    };

    assert!(!command.is_empty());
    let chroot_command = command[0];
    let chroot_args = &command[1..];

    // NOTE: Tests can only trigger code beyond this point if they're invoked with root permissions
    set_context(newroot, &matches);

    let pstatus = Command::new(chroot_command)
        .args(chroot_args)
        .status()
        .unwrap_or_else(|e| {
            // TODO: Exit status:
            // 125 if chroot itself fails
            // 126 if command is found but cannot be invoked
            // 127 if command cannot be found
            crash!(
                1,
                "failed to run command {}: {}",
                command[0].to_string().quote(),
                e
            )
        });

    if pstatus.success() {
        0
    } else {
        pstatus.code().unwrap_or(-1)
    }
}

pub fn uu_app() -> App<'static, 'static> {
    App::new(uucore::util_name())
        .version(crate_version!())
        .about(ABOUT)
        .usage(SYNTAX)
        .arg(
            Arg::with_name(options::NEWROOT)
                .hidden(true)
                .required(true)
                .index(1),
        )
        .arg(
            Arg::with_name(options::USER)
                .short("u")
                .long(options::USER)
                .help("User (ID or name) to switch before running the program")
                .value_name("USER"),
        )
        .arg(
            Arg::with_name(options::GROUP)
                .short("g")
                .long(options::GROUP)
                .help("Group (ID or name) to switch to")
                .value_name("GROUP"),
        )
        .arg(
            Arg::with_name(options::GROUPS)
                .short("G")
                .long(options::GROUPS)
                .help("Comma-separated list of groups to switch to")
                .value_name("GROUP1,GROUP2..."),
        )
        .arg(
            Arg::with_name(options::USERSPEC)
                .long(options::USERSPEC)
                .help(
                    "Colon-separated user and group to switch to. \
                     Same as -u USER -g GROUP. \
                     Userspec has higher preference than -u and/or -g",
                )
                .value_name("USER:GROUP"),
        )
        .arg(
            Arg::with_name(options::COMMAND)
                .hidden(true)
                .multiple(true)
                .index(2),
        )
}

fn set_context(root: &Path, options: &clap::ArgMatches) {
    let userspec_str = options.value_of(options::USERSPEC);
    let user_str = options.value_of(options::USER).unwrap_or_default();
    let group_str = options.value_of(options::GROUP).unwrap_or_default();
    let groups_str = options.value_of(options::GROUPS).unwrap_or_default();
    let userspec = match userspec_str {
        Some(u) => {
            let s: Vec<&str> = u.split(':').collect();
            if s.len() != 2 || s.iter().any(|&spec| spec.is_empty()) {
                crash!(1, "invalid userspec: {}", u.quote())
            };
            s
        }
        None => Vec::new(),
    };

    let (user, group) = if userspec.is_empty() {
        (user_str, group_str)
    } else {
        (userspec[0], userspec[1])
    };

    enter_chroot(root);

    set_groups_from_str(groups_str);
    set_main_group(group);
    set_user(user);
}

fn enter_chroot(root: &Path) {
    std::env::set_current_dir(root).unwrap();
    let err = unsafe {
        chroot(CString::new(".").unwrap().as_bytes_with_nul().as_ptr() as *const libc::c_char)
    };
    if err != 0 {
        crash!(
            1,
            "cannot chroot to {}: {}",
            root.quote(),
            Error::last_os_error()
        )
    };
}

fn set_main_group(group: &str) {
    if !group.is_empty() {
        let group_id = match entries::grp2gid(group) {
            Ok(g) => g,
            _ => crash!(1, "no such group: {}", group.maybe_quote()),
        };
        let err = unsafe { setgid(group_id) };
        if err != 0 {
            crash!(
                1,
                "cannot set gid to {}: {}",
                group_id,
                Error::last_os_error()
            )
        }
    }
}

#[cfg(any(target_vendor = "apple", target_os = "freebsd"))]
fn set_groups(groups: Vec<libc::gid_t>) -> libc::c_int {
    unsafe { setgroups(groups.len() as libc::c_int, groups.as_ptr()) }
}

#[cfg(target_os = "linux")]
fn set_groups(groups: Vec<libc::gid_t>) -> libc::c_int {
    unsafe { setgroups(groups.len() as libc::size_t, groups.as_ptr()) }
}

fn set_groups_from_str(groups: &str) {
    if !groups.is_empty() {
        let groups_vec: Vec<libc::gid_t> = groups
            .split(',')
            .map(|x| match entries::grp2gid(x) {
                Ok(g) => g,
                _ => crash!(1, "no such group: {}", x),
            })
            .collect();
        let err = set_groups(groups_vec);
        if err != 0 {
            crash!(1, "cannot set groups: {}", Error::last_os_error())
        }
    }
}

fn set_user(user: &str) {
    if !user.is_empty() {
        let user_id = entries::usr2uid(user).unwrap();
        let err = unsafe { setuid(user_id as libc::uid_t) };
        if err != 0 {
            crash!(
                1,
                "cannot set user to {}: {}",
                user.maybe_quote(),
                Error::last_os_error()
            )
        }
    }
}
<|endoftext|>


<commit_before>extern crate reqwest;
extern crate csv;
extern crate time;

use std::collections::HashMap;
use std::env;
use std::fs::{self, File};
use std::io::{self, Read, Write};
use std::path::{Path, PathBuf};
use super::{Airport, LatLon, Type, Runway, RunwayIdentifier, Frequencies};

error_chain! {
    errors {
        FieldNotFound(name: String) {
            description("data field not found")
            display("data field {} not found", name)
        }
    }

    foreign_links {
        Reqwest(reqwest::Error);
        Csv(csv::Error);
        Io(::std::io::Error);
        ParseInt(::std::num::ParseIntError);
        ParseFloat(::std::num::ParseFloatError);
    }
}

const DATA_HOME:        &str = "http://ourairports.com/data";
const AIRPORTS_FILE:    &str = "airports.csv";
const FREQUENCIES_FILE: &str = "airport-frequencies.csv";
const RUNWAYS_FILE:     &str = "runways.csv";

const DOWNLOAD_DATE_FILE: &str = "month-downloaded";

fn download_data_file(client: &reqwest::Client, name: &str) -> Result<Vec<u8>> {
    println!("downloading {}", name);

    let url = format!("{}/{}", DATA_HOME, name);
    let mut request  = client.get(&url)?.send()?;
    let mut csv_data = Vec::new();

    request.read_to_end(&mut csv_data)?;
    Ok(csv_data)
}

trait HashMapExtras<V> {
    fn get_field(&mut self, name: &str) -> Result<V>;
}

impl<V> HashMapExtras<V> for HashMap<String, V> {
    fn get_field(&mut self, name: &str) -> Result<V> {
        match self.remove(name) {
            Some(v) => Ok(v),
            None    => bail!(ErrorKind::FieldNotFound(name.into())),
        }
    }
}

pub struct DataFiles {
    data_dir: PathBuf,
}

impl DataFiles {
    pub fn new(data_path: &Path) -> Result<DataFiles> {
        let mut base_dir = env::current_exe()?;
        base_dir.pop();
        base_dir.push(data_path);

        Ok(DataFiles {
            data_dir: base_dir,
        })
    }

    pub fn ensure_updated_data(&self) -> Result<()> {
        let path = self.get_path_in_data(DOWNLOAD_DATE_FILE);

        if path.exists() {
            let mut file  = File::open(path)?;
            let mut month = String::new();

            file.read_to_string(&mut month)?;

            if time::now_utc().tm_mon != month.parse::<i32>()? {
                self.get_new_data()?;
            }
        } else {
            self.get_new_data()?;
        }

        Ok(())
    }

    fn save_data_to_file(&self, name: &str, data: &[u8]) -> Result<()> {
        let path = self.get_path_in_data(name);
        let mut file = File::create(path)?;

        file.write_all(data)?;
        Ok(())
    }

    fn download_and_save_data(&self, client: &reqwest::Client, name: &str) -> Result<()> {
        let data = download_data_file(client, name)?;
        self.save_data_to_file(name, &data)
    }

    fn retrieve_runway_data(&self, client: &reqwest::Client) -> Result<()> {
        // The airport runways file contains an extra comma at the end of the headers
        // that breaks CSV parsing, so we need to remove it before saving the file
        let mut runways = download_data_file(&client, RUNWAYS_FILE)?;
        let comma_pos   = runways.windows(2)
                                 .position(|b| b == &[b',', b'\n']);

        match comma_pos {
            Some(comma_pos) => runways[comma_pos] = b' ',
            None => println!("warning: extra comma no longer exists in {} file", RUNWAYS_FILE),
        }

        self.save_data_to_file(RUNWAYS_FILE, &runways)?;
        Ok(())
    }

    pub fn get_new_data(&self) -> Result<()> {
        match fs::create_dir(&self.data_dir) {
            Ok(_) => (),
            Err(e) => {
                match e.kind() {
                    io::ErrorKind::AlreadyExists => (),
                    _ => bail!(e),
                }
            },
        };

        let client = reqwest::Client::new()?;

        self.download_and_save_data(&client, AIRPORTS_FILE)?;
        self.download_and_save_data(&client, FREQUENCIES_FILE)?;
        self.retrieve_runway_data(&client)?;

        let mut date_file = File::create(
            self.get_path_in_data(DOWNLOAD_DATE_FILE))?;

        write!(date_file, "{}", time::now_utc().tm_mon)?;

        println!("data download complete");
        Ok(())
    }

    pub fn parse(&self) -> Result<Vec<Airport>> {
        let mut rdr = csv::Reader::from_path(
            self.get_path_in_data(AIRPORTS_FILE))?;

        let mut airports    = Vec::new();
        let mut runways     = self.parse_runways()?;
        let mut frequencies = self.parse_frequencies()?;

        for airport in rdr.deserialize() {
            let mut data: HashMap<String, String> = airport?;

            let icao = data.get_field("ident")?;

            airports.push(Airport {
                icao: icao.clone(),
                pos: LatLon {
                    lat: data.get_field("latitude_deg")?.parse()?,
                    lon: data.get_field("longitude_deg")?.parse()?,
                },
                _type: match Type::parse(&data.get_field("type")?) {
                    Some(t) => t,
                    None    => continue,
                },
                runways:     runways.remove(&icao),
                frequencies: frequencies.remove(&icao),
            });
        }

        Ok(airports)
    }

    fn parse_runways(&self) -> Result<HashMap<String, Vec<Runway>>> {
        let mut rdr = csv::Reader::from_path(
            self.get_path_in_data(RUNWAYS_FILE))?;

        let mut runways = HashMap::new();

        for runway in rdr.deserialize() {
            let mut data: HashMap<String, String> = runway?;

            let icao = data.get_field("airport_ident")?;
            let runway_list = runways.entry(icao).or_insert(Vec::new());

            runway_list.push(Runway {
                ident: RunwayIdentifier {
                    north: data.get_field("le_ident")?,
                    south: data.get_field("he_ident")?,
                },
                width:  data.get_field("width_ft")?.parse().ok(),
                length: data.get_field("length_ft")?.parse().ok(),
                closed: data.get_field("closed")?.parse::<i32>().ok().map(|val| val == 1),
            });
        }

        Ok(runways)
    }

    fn parse_frequencies(&self) -> Result<HashMap<String, Frequencies>> {
        let mut rdr = csv::Reader::from_path(
            self.get_path_in_data(FREQUENCIES_FILE))?;

        let mut frequencies = HashMap::new();
        let mut temp_freqs  = HashMap::new();
        let mut last_icao   = String::new();

        for freq_data in rdr.deserialize() {
            let mut data: HashMap<String, String> = freq_data?;
            let icao = data.get_field("airport_ident")?;

            temp_freqs.insert(
                data.get_field("type")?,
                data.get_field("frequency_mhz")?
            );

            if icao != last_icao {
                let app_dep = temp_freqs.remove("A/D");

                frequencies.insert(last_icao.clone(), Frequencies {
                    ground:    temp_freqs.remove("GND"),
                    tower:     temp_freqs.remove("TWR"),
                    departure: app_dep.clone().or(temp_freqs.remove("DEP")),
                    approach:  app_dep.clone().or(temp_freqs.remove("APP")),
                    atis:      temp_freqs.remove("ATIS"),
                });

                last_icao = icao;
                temp_freqs.clear();
            }
        }

        Ok(frequencies)
    }

    fn get_path_in_data(&self, file: &str) -> PathBuf {
        let mut path = self.data_dir.clone();
        path.push(file);

        path
    }
}<commit_msg>Simplify data directory creation Get airport type early for a very small performance improvement Remove even more missed duplicate code!<commit_after>extern crate reqwest;
extern crate csv;
extern crate time;

use std::collections::HashMap;
use std::env;
use std::fs::{self, File};
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
use super::{Airport, LatLon, Type, Runway, RunwayIdentifier, Frequencies};

error_chain! {
    errors {
        FieldNotFound(name: String) {
            description("data field not found")
            display("data field {} not found", name)
        }
    }

    foreign_links {
        Reqwest(reqwest::Error);
        Csv(csv::Error);
        Io(::std::io::Error);
        ParseInt(::std::num::ParseIntError);
        ParseFloat(::std::num::ParseFloatError);
    }
}

const DATA_HOME:        &str = "http://ourairports.com/data";
const AIRPORTS_FILE:    &str = "airports.csv";
const FREQUENCIES_FILE: &str = "airport-frequencies.csv";
const RUNWAYS_FILE:     &str = "runways.csv";

const DOWNLOAD_DATE_FILE: &str = "month-downloaded";

fn download_data_file(client: &reqwest::Client, name: &str) -> Result<Vec<u8>> {
    println!("downloading {}", name);

    let url = format!("{}/{}", DATA_HOME, name);
    let mut request  = client.get(&url)?.send()?;
    let mut csv_data = Vec::new();

    request.read_to_end(&mut csv_data)?;
    Ok(csv_data)
}

trait HashMapExtras<V> {
    fn get_field(&mut self, name: &str) -> Result<V>;
}

impl<V> HashMapExtras<V> for HashMap<String, V> {
    fn get_field(&mut self, name: &str) -> Result<V> {
        match self.remove(name) {
            Some(v) => Ok(v),
            None    => bail!(ErrorKind::FieldNotFound(name.into())),
        }
    }
}

pub struct DataFiles {
    data_dir: PathBuf,
}

impl DataFiles {
    pub fn new(data_path: &Path) -> Result<DataFiles> {
        let mut base_dir = env::current_exe()?;
        base_dir.pop();
        base_dir.push(data_path);

        Ok(DataFiles {
            data_dir: base_dir,
        })
    }

    pub fn ensure_updated_data(&self) -> Result<()> {
        let path = self.get_path_in_data(DOWNLOAD_DATE_FILE);

        if path.exists() {
            let mut file  = File::open(path)?;
            let mut month = String::new();

            file.read_to_string(&mut month)?;

            if time::now_utc().tm_mon != month.parse::<i32>()? {
                self.get_new_data()?;
            }
        } else {
            self.get_new_data()?;
        }

        Ok(())
    }

    fn save_data_to_file(&self, name: &str, data: &[u8]) -> Result<()> {
        let path = self.get_path_in_data(name);
        let mut file = File::create(path)?;

        file.write_all(data)?;
        Ok(())
    }

    fn download_and_save_data(&self, client: &reqwest::Client, name: &str) -> Result<()> {
        let data = download_data_file(client, name)?;
        self.save_data_to_file(name, &data)
    }

    fn retrieve_runway_data(&self, client: &reqwest::Client) -> Result<()> {
        // The airport runways file contains an extra comma at the end of the headers
        // that breaks CSV parsing, so we need to remove it before saving the file
        let mut runways = download_data_file(&client, RUNWAYS_FILE)?;
        let comma_pos   = runways.windows(2)
                                 .position(|b| b == &[b',', b'\n']);

        match comma_pos {
            Some(comma_pos) => runways[comma_pos] = b' ',
            None => println!("warning: extra comma no longer exists in {} file", RUNWAYS_FILE),
        }

        self.save_data_to_file(RUNWAYS_FILE, &runways)
    }

    pub fn get_new_data(&self) -> Result<()> {
        if !self.data_dir.exists() {
            fs::create_dir(&self.data_dir)?;
        }

        let client = reqwest::Client::new()?;

        self.download_and_save_data(&client, AIRPORTS_FILE)?;
        self.download_and_save_data(&client, FREQUENCIES_FILE)?;
        self.retrieve_runway_data(&client)?;

        let mut date_file = File::create(
            self.get_path_in_data(DOWNLOAD_DATE_FILE))?;

        write!(date_file, "{}", time::now_utc().tm_mon)?;

        println!("data download complete");
        Ok(())
    }

    pub fn parse(&self) -> Result<Vec<Airport>> {
        let mut rdr = csv::Reader::from_path(
            self.get_path_in_data(AIRPORTS_FILE))?;

        let mut airports    = Vec::new();
        let mut runways     = self.parse_runways()?;
        let mut frequencies = self.parse_frequencies()?;

        for airport in rdr.deserialize() {
            let mut data: HashMap<String, String> = airport?;
            let icao = data.get_field("ident")?;

            // Get the airport type early so we can skip ones we don't care about (like balloon ports)
            // and save a very small amount of time
            let _type = match Type::parse(&data.get_field("type")?) {
                Some(t) => t,
                None    => continue,
            };

            airports.push(Airport {
                icao: icao.clone(),
                pos: LatLon {
                    lat: data.get_field("latitude_deg")?.parse()?,
                    lon: data.get_field("longitude_deg")?.parse()?,
                },
                _type:       _type,
                runways:     runways.remove(&icao),
                frequencies: frequencies.remove(&icao),
            });
        }

        Ok(airports)
    }

    fn parse_runways(&self) -> Result<HashMap<String, Vec<Runway>>> {
        let mut rdr = csv::Reader::from_path(
            self.get_path_in_data(RUNWAYS_FILE))?;

        let mut runways = HashMap::new();

        for runway in rdr.deserialize() {
            let mut data: HashMap<String, String> = runway?;

            let icao = data.get_field("airport_ident")?;
            let runway_list = runways.entry(icao).or_insert(Vec::new());

            runway_list.push(Runway {
                ident: RunwayIdentifier {
                    north: data.get_field("le_ident")?,
                    south: data.get_field("he_ident")?,
                },
                width:  data.get_field("width_ft")?.parse().ok(),
                length: data.get_field("length_ft")?.parse().ok(),
                closed: data.get_field("closed")?.parse::<i32>().ok().map(|val| val == 1),
            });
        }

        Ok(runways)
    }

    fn parse_frequencies(&self) -> Result<HashMap<String, Frequencies>> {
        let mut rdr = csv::Reader::from_path(
            self.get_path_in_data(FREQUENCIES_FILE))?;

        let mut frequencies = HashMap::new();
        let mut temp_freqs  = HashMap::new();
        let mut last_icao   = String::new();

        for freq_data in rdr.deserialize() {
            let mut data: HashMap<String, String> = freq_data?;
            let icao = data.get_field("airport_ident")?;

            temp_freqs.insert(
                data.get_field("type")?,
                data.get_field("frequency_mhz")?
            );

            if icao != last_icao {
                let app_dep = temp_freqs.remove("A/D");

                frequencies.insert(last_icao.clone(), Frequencies {
                    ground:    temp_freqs.remove("GND"),
                    tower:     temp_freqs.remove("TWR"),
                    departure: app_dep.clone().or(temp_freqs.remove("DEP")),
                    approach:  app_dep.clone().or(temp_freqs.remove("APP")),
                    atis:      temp_freqs.remove("ATIS"),
                });

                last_icao = icao;
                temp_freqs.clear();
            }
        }

        Ok(frequencies)
    }

    fn get_path_in_data(&self, file: &str) -> PathBuf {
        let mut path = self.data_dir.clone();
        path.push(file);

        path
    }
}<|endoftext|>


<commit_before>use webcore::value::Reference;
use webapi::error::{IError, Error};

/// The `IDomException` interface represents an abnormal event which occurs as the result of
/// calling a web API.
///
/// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/DOMException)
pub trait IDomException: IError {}

/// A reference to a JavaScript `DOMException` object.
///
/// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/DOMException)
#[derive(Clone, Debug, ReferenceType)]
#[reference(instance_of = "DOMException")]
#[reference(subclass_of(Error))]
pub struct DomException( Reference );

// DOMException specification:
// https://heycam.github.io/webidl/#idl-DOMException

impl IError for DomException {}
impl IDomException for DomException {}

error_boilerplate! { DomException }

/// A trait representing a concrete DOMException.
pub trait ConcreteException: IDomException {
    /// A string representing the error type.
    ///
    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/DOMException/name)
    const ERROR_NAME: &'static str;
}

/// Occurs when an operation would result in an incorrect node tree.
#[derive(Clone, Debug, ReferenceType)]
#[reference(subclass_of(Error, DomException))]
pub struct HierarchyRequestError( Reference );

impl IError for HierarchyRequestError {}
impl IDomException for HierarchyRequestError {}

error_boilerplate! { HierarchyRequestError, name = "HierarchyRequestError" }

/// Occurs when an object does not support an operation or argument.
#[derive(Clone, Debug, ReferenceType)]
#[reference(subclass_of(Error, DomException))]
pub struct InvalidAccessError( Reference );

impl IError for InvalidAccessError {}
impl IDomException for InvalidAccessError {}

error_boilerplate! { InvalidAccessError, name = "InvalidAccessError" }

/// Occurs when the specified object cannot be found.
#[derive(Clone, Debug, ReferenceType)]
#[reference(subclass_of(Error, DomException))]
pub struct NotFoundError( Reference );

impl IError for NotFoundError {}
impl IDomException for NotFoundError {}

error_boilerplate! { NotFoundError, name = "NotFoundError" }

/// Occurs when the requested operation is insecure.
#[derive(Clone, Debug, ReferenceType)]
#[reference(subclass_of(Error, DomException))]
pub struct SecurityError( Reference );

impl IError for SecurityError {}
impl IDomException for SecurityError {}

error_boilerplate! { SecurityError, name = "SecurityError" }

/// Occurs when an argument does not match the expected pattern.
#[derive(Clone, Debug, ReferenceType)]
#[reference(subclass_of(Error, DomException))]
pub struct SyntaxError( Reference );

impl IError for SyntaxError {}
impl IDomException for SyntaxError {}

error_boilerplate! { SyntaxError, name = "SyntaxError" }

#[cfg(all(test, feature = "web_test"))]
mod test {
    use super::*;
    use webcore::try_from::TryInto;

    fn new_dom_exception(message: &str, name: &str) -> DomException {
        js!(
            return new DOMException(@{message}, @{name});
        ).try_into().unwrap()
    }

    #[test]
    fn test_error() {
        let name = "HierarchyRequestError";
        // Successful downcast.
        let err: DomException = new_dom_exception("foo", name);
        let err: HierarchyRequestError = err.try_into().expect("Expected HierarchyRequestError");
        assert_eq!(err.name(), name);

        // Failed downcast.
        let err: DomException = new_dom_exception("foo", name);
        let err: Result<SyntaxError, _> = err.try_into();
        assert!(err.is_err());
    }
}<commit_msg>Delete `ConcreteException` trait<commit_after>use webcore::value::Reference;
use webapi::error::{IError, Error};

/// The `IDomException` interface represents an abnormal event which occurs as the result of
/// calling a web API.
///
/// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/DOMException)
pub trait IDomException: IError {}

/// A reference to a JavaScript `DOMException` object.
///
/// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/DOMException)
#[derive(Clone, Debug, ReferenceType)]
#[reference(instance_of = "DOMException")]
#[reference(subclass_of(Error))]
pub struct DomException( Reference );

// DOMException specification:
// https://heycam.github.io/webidl/#idl-DOMException

impl IError for DomException {}
impl IDomException for DomException {}

error_boilerplate! { DomException }

/// Occurs when an operation would result in an incorrect node tree.
#[derive(Clone, Debug, ReferenceType)]
#[reference(subclass_of(Error, DomException))]
pub struct HierarchyRequestError( Reference );

impl IError for HierarchyRequestError {}
impl IDomException for HierarchyRequestError {}

error_boilerplate! { HierarchyRequestError, name = "HierarchyRequestError" }

/// Occurs when an object does not support an operation or argument.
#[derive(Clone, Debug, ReferenceType)]
#[reference(subclass_of(Error, DomException))]
pub struct InvalidAccessError( Reference );

impl IError for InvalidAccessError {}
impl IDomException for InvalidAccessError {}

error_boilerplate! { InvalidAccessError, name = "InvalidAccessError" }

/// Occurs when the specified object cannot be found.
#[derive(Clone, Debug, ReferenceType)]
#[reference(subclass_of(Error, DomException))]
pub struct NotFoundError( Reference );

impl IError for NotFoundError {}
impl IDomException for NotFoundError {}

error_boilerplate! { NotFoundError, name = "NotFoundError" }

/// Occurs when the requested operation is insecure.
#[derive(Clone, Debug, ReferenceType)]
#[reference(subclass_of(Error, DomException))]
pub struct SecurityError( Reference );

impl IError for SecurityError {}
impl IDomException for SecurityError {}

error_boilerplate! { SecurityError, name = "SecurityError" }

/// Occurs when an argument does not match the expected pattern.
#[derive(Clone, Debug, ReferenceType)]
#[reference(subclass_of(Error, DomException))]
pub struct SyntaxError( Reference );

impl IError for SyntaxError {}
impl IDomException for SyntaxError {}

error_boilerplate! { SyntaxError, name = "SyntaxError" }

#[cfg(all(test, feature = "web_test"))]
mod test {
    use super::*;
    use webcore::try_from::TryInto;

    fn new_dom_exception(message: &str, name: &str) -> DomException {
        js!(
            return new DOMException(@{message}, @{name});
        ).try_into().unwrap()
    }

    #[test]
    fn test_error() {
        let name = "HierarchyRequestError";
        // Successful downcast.
        let err: DomException = new_dom_exception("foo", name);
        let err: HierarchyRequestError = err.try_into().expect("Expected HierarchyRequestError");
        assert_eq!(err.name(), name);

        // Failed downcast.
        let err: DomException = new_dom_exception("foo", name);
        let err: Result<SyntaxError, _> = err.try_into();
        assert!(err.is_err());
    }
}<|endoftext|>


<commit_before>use orbclient::{Color, Renderer};
use std::cell::{Cell, RefCell};
use std::cmp::{min, max};
use std::sync::Arc;

use cell::CheckSet;
use event::Event;
use point::Point;
use rect::Rect;
use theme::{ITEM_BACKGROUND, ITEM_BORDER, ITEM_SELECTION};
use traits::{Border, Click, Place};
use widgets::Widget;

pub struct ProgressBar {
    pub rect: Cell<Rect>,
    pub bg: Color,
    pub fg: Color,
    pub fg_border: Color,
    pub border: Cell<bool>,
    pub border_radius: Cell<u32>,
    pub value: Cell<i32>,
    pub minimum: i32,
    pub maximum: i32,
    click_callback: RefCell<Option<Arc<Fn(&ProgressBar, Point)>>>,
    pressed: Cell<bool>,
}

impl ProgressBar {
    pub fn new() -> Arc<Self> {
        Arc::new(ProgressBar {
            rect: Cell::new(Rect::default()),
            bg: ITEM_BACKGROUND,
            fg: ITEM_SELECTION,
            fg_border: ITEM_BORDER,
            border: Cell::new(true),
            border_radius: Cell::new(0),
            value: Cell::new(0),
            minimum: 0,
            maximum: 100,
            click_callback: RefCell::new(None),
            pressed: Cell::new(false),
        })
    }

    pub fn value(&self, value: i32) -> &Self {
        self.value.set(value);
        self
    }
}

impl Border for ProgressBar {
    fn border(&self, enabled: bool) -> &Self {
        self.border.set(enabled);
        self
    }

    fn border_radius(&self, radius: u32) -> &Self {
        self.border_radius.set(radius);
        self
    }
}

impl Click for ProgressBar {
    fn emit_click(&self, point: Point) {
        if let Some(ref click_callback) = *self.click_callback.borrow() {
            click_callback(self, point);
        }
    }

    fn on_click<T: Fn(&Self, Point) + 'static>(&self, func: T) -> &Self {
        *self.click_callback.borrow_mut() = Some(Arc::new(func));
        self
    }
}

impl Place for ProgressBar {}

impl Widget for ProgressBar {
    fn rect(&self) -> &Cell<Rect> {
        &self.rect
    }

    fn draw(&self, renderer: &mut Renderer, _focused: bool) {
        let rect = self.rect.get();
        let progress_rect = Rect::new(rect.x,
                                rect.y,
                                ((rect.width as i32 *
                                  max(0, min(self.maximum, self.value.get() - self.minimum))) /
                                 max(1,
                                     self.maximum -
                                     self.minimum)) as u32,
                                rect.height);

        let b_r = self.border_radius.get();
        renderer.rounded_rect(rect.x, rect.y, rect.width, rect.height, b_r, true, self.bg);
        if progress_rect.width >= b_r * 2 {
            renderer.rounded_rect(progress_rect.x, progress_rect.y,
                                  progress_rect.width, progress_rect.height,
                                  b_r, true, self.fg);
        }
        if self.border.get() {
            renderer.rounded_rect(rect.x, rect.y, rect.width, rect.height, b_r, false, self.fg_border);
        }
    }

    fn event(&self, event: Event, focused: bool, redraw: &mut bool) -> bool {
        match event {
            Event::Mouse { point, left_button, .. } => {
                let mut click = false;

                let rect = self.rect.get();
                if rect.contains(point) {
                    if left_button {
                        if self.pressed.check_set(true) {
                            *redraw = true;
                        }
                    } else {
                        if self.pressed.check_set(false) {
                            click = true;
                            *redraw = true;
                        }
                    }
                } else {
                    if !left_button {
                        if self.pressed.check_set(false) {
                            *redraw = true;
                        }
                    }
                }

                if click {
                    let click_point: Point = point - rect.point();
                    self.emit_click(click_point);
                }
            }
            _ => (),
        }

        focused
    }
}
<commit_msg>Nicer progress bar progress_rect calculation.<commit_after>use orbclient::{Color, Renderer};
use std::cell::{Cell, RefCell};
use std::cmp::{min, max};
use std::sync::Arc;

use cell::CheckSet;
use event::Event;
use point::Point;
use rect::Rect;
use theme::{ITEM_BACKGROUND, ITEM_BORDER, ITEM_SELECTION};
use traits::{Border, Click, Place};
use widgets::Widget;

pub struct ProgressBar {
    pub rect: Cell<Rect>,
    pub bg: Color,
    pub fg: Color,
    pub fg_border: Color,
    pub border: Cell<bool>,
    pub border_radius: Cell<u32>,
    pub value: Cell<i32>,
    pub minimum: i32,
    pub maximum: i32,
    click_callback: RefCell<Option<Arc<Fn(&ProgressBar, Point)>>>,
    pressed: Cell<bool>,
}

impl ProgressBar {
    pub fn new() -> Arc<Self> {
        Arc::new(ProgressBar {
            rect: Cell::new(Rect::default()),
            bg: ITEM_BACKGROUND,
            fg: ITEM_SELECTION,
            fg_border: ITEM_BORDER,
            border: Cell::new(true),
            border_radius: Cell::new(0),
            value: Cell::new(0),
            minimum: 0,
            maximum: 100,
            click_callback: RefCell::new(None),
            pressed: Cell::new(false),
        })
    }

    pub fn value(&self, value: i32) -> &Self {
        self.value.set(value);
        self
    }
}

impl Border for ProgressBar {
    fn border(&self, enabled: bool) -> &Self {
        self.border.set(enabled);
        self
    }

    fn border_radius(&self, radius: u32) -> &Self {
        self.border_radius.set(radius);
        self
    }
}

impl Click for ProgressBar {
    fn emit_click(&self, point: Point) {
        if let Some(ref click_callback) = *self.click_callback.borrow() {
            click_callback(self, point);
        }
    }

    fn on_click<T: Fn(&Self, Point) + 'static>(&self, func: T) -> &Self {
        *self.click_callback.borrow_mut() = Some(Arc::new(func));
        self
    }
}

impl Place for ProgressBar {}

impl Widget for ProgressBar {
    fn rect(&self) -> &Cell<Rect> {
        &self.rect
    }

    fn draw(&self, renderer: &mut Renderer, _focused: bool) {
        let rect = self.rect.get();
        let progress_rect = Rect{
                                width: (rect.width as i32 *
                                        max(0, min(self.maximum, self.value.get() - self.minimum)) /
                                        max(1, self.maximum - self.minimum)) as u32,
                                ..self.rect.get()
                            };

        let b_r = self.border_radius.get();
        renderer.rounded_rect(rect.x, rect.y, rect.width, rect.height, b_r, true, self.bg);
        if progress_rect.width >= b_r * 2 {
            renderer.rounded_rect(progress_rect.x, progress_rect.y,
                                  progress_rect.width, progress_rect.height,
                                  b_r, true, self.fg);
        }
        if self.border.get() {
            renderer.rounded_rect(rect.x, rect.y, rect.width, rect.height, b_r, false, self.fg_border);
        }
    }

    fn event(&self, event: Event, focused: bool, redraw: &mut bool) -> bool {
        match event {
            Event::Mouse { point, left_button, .. } => {
                let mut click = false;

                let rect = self.rect.get();
                if rect.contains(point) {
                    if left_button {
                        if self.pressed.check_set(true) {
                            *redraw = true;
                        }
                    } else {
                        if self.pressed.check_set(false) {
                            click = true;
                            *redraw = true;
                        }
                    }
                } else {
                    if !left_button {
                        if self.pressed.check_set(false) {
                            *redraw = true;
                        }
                    }
                }

                if click {
                    let click_point: Point = point - rect.point();
                    self.emit_click(click_point);
                }
            }
            _ => (),
        }

        focused
    }
}
<|endoftext|>