<commit_before>// A variation on event-loop.rs.
//
// Here we:
//
// - Have the event loop be run by a `Resolver`, hiding away the event loop
// details from the writer of `main()`.
//
// - Transform the callback-based c-ares interface into a futures-style.
//
// This example is fleshed out in the
// [c-ares-resolver](https://github.com/dimbleby/c-ares-resolver) crate.
#[cfg(unix)]
mod example {
    extern crate c_ares;
    extern crate futures;
    extern crate mio;
    extern crate mio_extras;
    extern crate tokio;

    use std::collections::HashSet;
    use std::error::Error;
    use std::sync::{Arc, Mutex};
    use std::thread;
    use std::time::Duration;

    use self::futures::Future;
    use self::futures::future::lazy;
    use self::tokio::executor::current_thread;

    // The EventLoop will set up a mio::Poll and use it to wait for the following:
    //
    // -  messages telling it which file descriptors it should be interested in.
    //    These file descriptors are then registered (or deregistered) with the
    //    mio::Poll as required.
    //
    // -  events telling it that something has happened on one of these file
    //    descriptors.  When this happens, it tells the c_ares::Channel about it.
    //
    // -  a message telling it to shut down.
    struct EventLoop {
        poll: mio::Poll,
        msg_channel: mio_extras::channel::Receiver<Message>,
        tracked_fds: HashSet<c_ares::Socket>,
        ares_channel: Arc<Mutex<c_ares::Channel>>,
        quit: bool,
    }

    // Messages for the event loop.
    #[derive(Debug)]
    enum Message {
        // 'Notify me when this file descriptor becomes readable, or writable'.
        // The first bool is for 'readable' and the second is for 'writable'.  It's
        // allowed to set both of these - or neither, meaning 'I am no longer
        // interested in this file descriptor'.
        RegisterInterest(c_ares::Socket, bool, bool),

        // 'Shut down'.
        ShutDown,
    }

    // A token identifying that the message channel has become available for reading.
    //
    // We use Token(fd) for file descriptors, so this relies on zero not being a
    // valid file descriptor for c-ares to use.  Zero is stdin, so that's true.
    const CHANNEL: mio::Token = mio::Token(0);

    impl EventLoop {
        // Create a new event loop.
        pub fn new(
            ares_channel: Arc<Mutex<c_ares::Channel>>,
            rx: mio_extras::channel::Receiver<Message>,
        ) -> EventLoop {
            let poll = mio::Poll::new().expect("Failed to create poll");
            poll.register(&rx, CHANNEL, mio::Ready::readable(), mio::PollOpt::edge())
                .expect("failed to register channel with poll");

            EventLoop {
                poll,
                msg_channel: rx,
                tracked_fds: HashSet::<c_ares::Socket>::new(),
                ares_channel,
                quit: false,
            }
        }

        // Run the event loop.
        pub fn run(self) -> thread::JoinHandle<()> {
            thread::spawn(|| self.event_loop_thread())
        }

        // Event loop thread - waits for events, and handles them.
        fn event_loop_thread(mut self) {
            let mut events = mio::Events::with_capacity(16);
            loop {
                // Wait for something to happen.
                let timeout = Duration::from_millis(500);
                let results = self.poll
                    .poll(&mut events, Some(timeout))
                    .expect("poll failed");

                // Process whatever happened.
                match results {
                    0 => {
                        // No events - must be a timeout.  Tell c-ares about it.
                        self.ares_channel
                            .lock()
                            .unwrap()
                            .process_fd(c_ares::SOCKET_BAD, c_ares::SOCKET_BAD);
                    }
                    _ => {
                        // Process events.  One of them might have asked us to quit.
                        for event in &events {
                            self.handle_event(&event);
                            if self.quit {
                                return;
                            }
                        }
                    }
                }
            }
        }

        // Handle a single event.
        fn handle_event(&mut self, event: &mio::Event) {
            match event.token() {
                CHANNEL => {
                    // The channel is readable.
                    self.handle_messages()
                }

                mio::Token(fd) => {
                    // Sockets became readable or writable - tell c-ares.
                    let rfd = if event.readiness().is_readable() {
                        fd as c_ares::Socket
                    } else {
                        c_ares::SOCKET_BAD
                    };
                    let wfd = if event.readiness().is_writable() {
                        fd as c_ares::Socket
                    } else {
                        c_ares::SOCKET_BAD
                    };
                    self.ares_channel.lock().unwrap().process_fd(rfd, wfd);
                }
            }
        }

        // Process messages incoming on the channel.
        fn handle_messages(&mut self) {
            loop {
                match self.msg_channel.try_recv() {
                    Ok(Message::RegisterInterest(fd, readable, writable)) => {
                        // Instruction to do something with a file descriptor.
                        let efd = mio::unix::EventedFd(&fd);
                        if !readable && !writable {
                            self.tracked_fds.remove(&fd);
                            self.poll
                                .deregister(&efd)
                                .expect("failed to deregister interest");
                        } else {
                            let token = mio::Token(fd as usize);
                            let mut interest = mio::Ready::empty();
                            if readable {
                                interest.insert(mio::Ready::readable())
                            }
                            if writable {
                                interest.insert(mio::Ready::writable())
                            }
                            let register_result = if !self.tracked_fds.insert(fd) {
                                self.poll
                                    .reregister(&efd, token, interest, mio::PollOpt::level())
                            } else {
                                self.poll
                                    .register(&efd, token, interest, mio::PollOpt::level())
                            };
                            register_result.expect("failed to register interest");
                        }
                    }

                    Ok(Message::ShutDown) => {
                        // Instruction to shut down.
                        self.quit = true;
                        break;
                    }

                    // No more instructions.
                    Err(_) => break,
                }
            }
        }
    }

    // The type of future returned by methods on the Resolver.
    pub struct CAresFuture<T> {
        inner: futures::sync::oneshot::Receiver<c_ares::Result<T>>,
    }

    impl<T> CAresFuture<T> {
        fn new(promise: futures::sync::oneshot::Receiver<c_ares::Result<T>>) -> Self {
            CAresFuture { inner: promise }
        }
    }

    impl<T> Future for CAresFuture<T> {
        type Item = T;
        type Error = c_ares::Error;

        fn poll(&mut self) -> futures::Poll<Self::Item, Self::Error> {
            match self.inner.poll() {
                Ok(futures::Async::NotReady) => Ok(futures::Async::NotReady),
                Err(_) => Err(c_ares::Error::ECANCELLED),
                Ok(futures::Async::Ready(res)) => match res {
                    Ok(r) => Ok(futures::Async::Ready(r)),
                    Err(e) => Err(e),
                },
            }
        }
    }

    // The Resolver is the interface by which users make DNS queries.
    struct Resolver {
        ares_channel: Arc<Mutex<c_ares::Channel>>,
        event_loop_channel: mio_extras::channel::Sender<Message>,
        event_loop_handle: Option<thread::JoinHandle<()>>,
    }

    impl Resolver {
        // Create a new Resolver.
        pub fn new() -> Resolver {
            // Whenever c-ares tells us what to do with a file descriptor, we'll
            // send that request along, in a message to the event loop thread.
            let (tx, rx) = mio_extras::channel::channel();
            let tx_clone = tx.clone();
            let sock_callback = move |fd: c_ares::Socket, readable: bool, writable: bool| {
                let _ = tx_clone.send(Message::RegisterInterest(fd, readable, writable));
            };

            // Create a c_ares::Channel.
            let mut options = c_ares::Options::new();
            options
                .set_socket_state_callback(sock_callback)
                .set_flags(c_ares::Flags::STAYOPEN | c_ares::Flags::EDNS)
                .set_timeout(500)
                .set_tries(3);
            let mut ares_channel =
                c_ares::Channel::with_options(options).expect("Failed to create channel");
            ares_channel
                .set_servers(&["8.8.8.8"])
                .expect("Failed to set servers");
            let locked_channel = Arc::new(Mutex::new(ares_channel));

            // Create and run the event loop.
            let channel_clone = Arc::clone(&locked_channel);
            let event_loop = EventLoop::new(channel_clone, rx);
            let handle = event_loop.run();

            // Return the Resolver.
            Resolver {
                ares_channel: locked_channel,
                event_loop_channel: tx,
                event_loop_handle: Some(handle),
            }
        }

        // A CNAME query.  Returns a future that will resolve to hold the result.
        pub fn query_cname(&self, name: &str) -> CAresFuture<c_ares::CNameResults> {
            let (c, p) = futures::oneshot();
            self.ares_channel
                .lock()
                .unwrap()
                .query_cname(name, move |result| {
                    let _ = c.send(result);
                });
            CAresFuture::new(p)
        }

        // An MX query.  Returns a future that will resolve to hold the result.
        pub fn query_mx(&self, name: &str) -> CAresFuture<c_ares::MXResults> {
            let (c, p) = futures::oneshot();
            self.ares_channel
                .lock()
                .unwrap()
                .query_mx(name, move |result| {
                    let _ = c.send(result);
                });
            CAresFuture::new(p)
        }

        // A NAPTR query.  Returns a future that will resolve to hold the result.
        pub fn query_naptr(&self, name: &str) -> CAresFuture<c_ares::NAPTRResults> {
            let (c, p) = futures::oneshot();
            self.ares_channel
                .lock()
                .unwrap()
                .query_naptr(name, move |result| {
                    let _ = c.send(result);
                });
            CAresFuture::new(p)
        }
    }

    impl Drop for Resolver {
        fn drop(&mut self) {
            // Shut down the event loop and wait for it to finish.
            self.event_loop_channel
                .send(Message::ShutDown)
                .expect("failed to request event loop to shut down");
            if let Some(handle) = self.event_loop_handle.take() {
                handle.join().expect("failed to shut down event loop");
            }
        }
    }

    pub fn main() {
        // Create a Resolver, and some queries.
        let resolver = Resolver::new();
        let cname_query = resolver
            .query_cname("dimbleby.github.io")
            .map_err(|e| println!("CNAME lookup failed with error '{}'", e.description()))
            .map(|results| {
                println!();
                println!("Successful CNAME lookup...");
                println!("Hostname: {}", results.hostname());
                for alias in results.aliases() {
                    println!("Alias: {}", alias);
                }
                println!();
            });

        let mx_query = resolver
            .query_mx("gmail.com")
            .map_err(|e| println!("MX lookup failed with error '{}'", e.description()))
            .map(|results| {
                println!();
                println!("Successful MX lookup...");
                for result in &results {
                    println!("host {}, priority {}", result.host(), result.priority());
                }
                println!();
            });

        let naptr_query = resolver
            .query_naptr("apple.com")
            .map_err(|e| println!("NAPTR lookup failed with error '{}'", e.description()))
            .map(|results| {
                println!();
                println!("Successful NAPTR lookup...");
                for result in &results {
                    println!("flags: {}", result.flags());
                    println!("service name: {}", result.service_name());
                    println!("regular expression: {}", result.reg_exp());
                    println!("replacement pattern: {}", result.replacement_pattern());
                    println!("order: {}", result.order());
                    println!("preference: {}", result.preference());
                }
                println!();
            });

        // Execute the queries.
        current_thread::block_on_all(lazy(|| {
            current_thread::spawn(cname_query);
            current_thread::spawn(mx_query);
            current_thread::spawn(naptr_query);
            Ok::<(), ()>(())
        })).unwrap();
    }
}

#[cfg(unix)]
pub fn main() {
    example::main();
}

#[cfg(not(unix))]
pub fn main() {
    println!("this example is not supported on this platform");
}
<commit_msg>impl Future<commit_after>// A variation on event-loop.rs.
//
// Here we:
//
// - Have the event loop be run by a `Resolver`, hiding away the event loop
// details from the writer of `main()`.
//
// - Transform the callback-based c-ares interface into a futures-style.
//
// This example is fleshed out in the
// [c-ares-resolver](https://github.com/dimbleby/c-ares-resolver) crate.
#[cfg(unix)]
mod example {
    extern crate c_ares;
    extern crate futures;
    extern crate mio;
    extern crate mio_extras;
    extern crate tokio;

    use std::collections::HashSet;
    use std::error::Error;
    use std::sync::{Arc, Mutex};
    use std::thread;
    use std::time::Duration;

    use self::futures::Future;
    use self::futures::future::lazy;
    use self::tokio::executor::current_thread;

    // The EventLoop will set up a mio::Poll and use it to wait for the following:
    //
    // -  messages telling it which file descriptors it should be interested in.
    //    These file descriptors are then registered (or deregistered) with the
    //    mio::Poll as required.
    //
    // -  events telling it that something has happened on one of these file
    //    descriptors.  When this happens, it tells the c_ares::Channel about it.
    //
    // -  a message telling it to shut down.
    struct EventLoop {
        poll: mio::Poll,
        msg_channel: mio_extras::channel::Receiver<Message>,
        tracked_fds: HashSet<c_ares::Socket>,
        ares_channel: Arc<Mutex<c_ares::Channel>>,
        quit: bool,
    }

    // Messages for the event loop.
    #[derive(Debug)]
    enum Message {
        // 'Notify me when this file descriptor becomes readable, or writable'.
        // The first bool is for 'readable' and the second is for 'writable'.  It's
        // allowed to set both of these - or neither, meaning 'I am no longer
        // interested in this file descriptor'.
        RegisterInterest(c_ares::Socket, bool, bool),

        // 'Shut down'.
        ShutDown,
    }

    // A token identifying that the message channel has become available for reading.
    //
    // We use Token(fd) for file descriptors, so this relies on zero not being a
    // valid file descriptor for c-ares to use.  Zero is stdin, so that's true.
    const CHANNEL: mio::Token = mio::Token(0);

    impl EventLoop {
        // Create a new event loop.
        pub fn new(
            ares_channel: Arc<Mutex<c_ares::Channel>>,
            rx: mio_extras::channel::Receiver<Message>,
        ) -> EventLoop {
            let poll = mio::Poll::new().expect("Failed to create poll");
            poll.register(&rx, CHANNEL, mio::Ready::readable(), mio::PollOpt::edge())
                .expect("failed to register channel with poll");

            EventLoop {
                poll,
                msg_channel: rx,
                tracked_fds: HashSet::<c_ares::Socket>::new(),
                ares_channel,
                quit: false,
            }
        }

        // Run the event loop.
        pub fn run(self) -> thread::JoinHandle<()> {
            thread::spawn(|| self.event_loop_thread())
        }

        // Event loop thread - waits for events, and handles them.
        fn event_loop_thread(mut self) {
            let mut events = mio::Events::with_capacity(16);
            loop {
                // Wait for something to happen.
                let timeout = Duration::from_millis(500);
                let results = self.poll
                    .poll(&mut events, Some(timeout))
                    .expect("poll failed");

                // Process whatever happened.
                match results {
                    0 => {
                        // No events - must be a timeout.  Tell c-ares about it.
                        self.ares_channel
                            .lock()
                            .unwrap()
                            .process_fd(c_ares::SOCKET_BAD, c_ares::SOCKET_BAD);
                    }
                    _ => {
                        // Process events.  One of them might have asked us to quit.
                        for event in &events {
                            self.handle_event(&event);
                            if self.quit {
                                return;
                            }
                        }
                    }
                }
            }
        }

        // Handle a single event.
        fn handle_event(&mut self, event: &mio::Event) {
            match event.token() {
                CHANNEL => {
                    // The channel is readable.
                    self.handle_messages()
                }

                mio::Token(fd) => {
                    // Sockets became readable or writable - tell c-ares.
                    let rfd = if event.readiness().is_readable() {
                        fd as c_ares::Socket
                    } else {
                        c_ares::SOCKET_BAD
                    };
                    let wfd = if event.readiness().is_writable() {
                        fd as c_ares::Socket
                    } else {
                        c_ares::SOCKET_BAD
                    };
                    self.ares_channel.lock().unwrap().process_fd(rfd, wfd);
                }
            }
        }

        // Process messages incoming on the channel.
        fn handle_messages(&mut self) {
            loop {
                match self.msg_channel.try_recv() {
                    Ok(Message::RegisterInterest(fd, readable, writable)) => {
                        // Instruction to do something with a file descriptor.
                        let efd = mio::unix::EventedFd(&fd);
                        if !readable && !writable {
                            self.tracked_fds.remove(&fd);
                            self.poll
                                .deregister(&efd)
                                .expect("failed to deregister interest");
                        } else {
                            let token = mio::Token(fd as usize);
                            let mut interest = mio::Ready::empty();
                            if readable {
                                interest.insert(mio::Ready::readable())
                            }
                            if writable {
                                interest.insert(mio::Ready::writable())
                            }
                            let register_result = if !self.tracked_fds.insert(fd) {
                                self.poll
                                    .reregister(&efd, token, interest, mio::PollOpt::level())
                            } else {
                                self.poll
                                    .register(&efd, token, interest, mio::PollOpt::level())
                            };
                            register_result.expect("failed to register interest");
                        }
                    }

                    Ok(Message::ShutDown) => {
                        // Instruction to shut down.
                        self.quit = true;
                        break;
                    }

                    // No more instructions.
                    Err(_) => break,
                }
            }
        }
    }

    // The type of future returned by methods on the Resolver.
    struct CAresFuture<T> {
        inner: futures::sync::oneshot::Receiver<c_ares::Result<T>>,
    }

    impl<T> CAresFuture<T> {
        fn new(promise: futures::sync::oneshot::Receiver<c_ares::Result<T>>) -> Self {
            CAresFuture { inner: promise }
        }
    }

    impl<T> Future for CAresFuture<T> {
        type Item = T;
        type Error = c_ares::Error;

        fn poll(&mut self) -> futures::Poll<Self::Item, Self::Error> {
            match self.inner.poll() {
                Ok(futures::Async::NotReady) => Ok(futures::Async::NotReady),
                Err(_) => Err(c_ares::Error::ECANCELLED),
                Ok(futures::Async::Ready(res)) => match res {
                    Ok(r) => Ok(futures::Async::Ready(r)),
                    Err(e) => Err(e),
                },
            }
        }
    }

    // The Resolver is the interface by which users make DNS queries.
    struct Resolver {
        ares_channel: Arc<Mutex<c_ares::Channel>>,
        event_loop_channel: mio_extras::channel::Sender<Message>,
        event_loop_handle: Option<thread::JoinHandle<()>>,
    }

    impl Resolver {
        // Create a new Resolver.
        pub fn new() -> Resolver {
            // Whenever c-ares tells us what to do with a file descriptor, we'll
            // send that request along, in a message to the event loop thread.
            let (tx, rx) = mio_extras::channel::channel();
            let tx_clone = tx.clone();
            let sock_callback = move |fd: c_ares::Socket, readable: bool, writable: bool| {
                let _ = tx_clone.send(Message::RegisterInterest(fd, readable, writable));
            };

            // Create a c_ares::Channel.
            let mut options = c_ares::Options::new();
            options
                .set_socket_state_callback(sock_callback)
                .set_flags(c_ares::Flags::STAYOPEN | c_ares::Flags::EDNS)
                .set_timeout(500)
                .set_tries(3);
            let mut ares_channel =
                c_ares::Channel::with_options(options).expect("Failed to create channel");
            ares_channel
                .set_servers(&["8.8.8.8"])
                .expect("Failed to set servers");
            let locked_channel = Arc::new(Mutex::new(ares_channel));

            // Create and run the event loop.
            let channel_clone = Arc::clone(&locked_channel);
            let event_loop = EventLoop::new(channel_clone, rx);
            let handle = event_loop.run();

            // Return the Resolver.
            Resolver {
                ares_channel: locked_channel,
                event_loop_channel: tx,
                event_loop_handle: Some(handle),
            }
        }

        // A CNAME query.  Returns a future that will resolve to hold the result.
        pub fn query_cname(
            &self,
            name: &str,
        ) -> impl Future<Item = c_ares::CNameResults, Error = c_ares::Error> {
            let (c, p) = futures::oneshot();
            self.ares_channel
                .lock()
                .unwrap()
                .query_cname(name, move |result| {
                    let _ = c.send(result);
                });
            CAresFuture::new(p)
        }

        // An MX query.  Returns a future that will resolve to hold the result.
        pub fn query_mx(
            &self,
            name: &str,
        ) -> impl Future<Item = c_ares::MXResults, Error = c_ares::Error> {
            let (c, p) = futures::oneshot();
            self.ares_channel
                .lock()
                .unwrap()
                .query_mx(name, move |result| {
                    let _ = c.send(result);
                });
            CAresFuture::new(p)
        }

        // A NAPTR query.  Returns a future that will resolve to hold the result.
        pub fn query_naptr(
            &self,
            name: &str,
        ) -> impl Future<Item = c_ares::NAPTRResults, Error = c_ares::Error> {
            let (c, p) = futures::oneshot();
            self.ares_channel
                .lock()
                .unwrap()
                .query_naptr(name, move |result| {
                    let _ = c.send(result);
                });
            CAresFuture::new(p)
        }
    }

    impl Drop for Resolver {
        fn drop(&mut self) {
            // Shut down the event loop and wait for it to finish.
            self.event_loop_channel
                .send(Message::ShutDown)
                .expect("failed to request event loop to shut down");
            if let Some(handle) = self.event_loop_handle.take() {
                handle.join().expect("failed to shut down event loop");
            }
        }
    }

    pub fn main() {
        // Create a Resolver, and some queries.
        let resolver = Resolver::new();
        let cname_query = resolver
            .query_cname("dimbleby.github.io")
            .map_err(|e| println!("CNAME lookup failed with error '{}'", e.description()))
            .map(|results| {
                println!();
                println!("Successful CNAME lookup...");
                println!("Hostname: {}", results.hostname());
                for alias in results.aliases() {
                    println!("Alias: {}", alias);
                }
                println!();
            });

        let mx_query = resolver
            .query_mx("gmail.com")
            .map_err(|e| println!("MX lookup failed with error '{}'", e.description()))
            .map(|results| {
                println!();
                println!("Successful MX lookup...");
                for result in &results {
                    println!("host {}, priority {}", result.host(), result.priority());
                }
                println!();
            });

        let naptr_query = resolver
            .query_naptr("apple.com")
            .map_err(|e| println!("NAPTR lookup failed with error '{}'", e.description()))
            .map(|results| {
                println!();
                println!("Successful NAPTR lookup...");
                for result in &results {
                    println!("flags: {}", result.flags());
                    println!("service name: {}", result.service_name());
                    println!("regular expression: {}", result.reg_exp());
                    println!("replacement pattern: {}", result.replacement_pattern());
                    println!("order: {}", result.order());
                    println!("preference: {}", result.preference());
                }
                println!();
            });

        // Execute the queries.
        current_thread::block_on_all(lazy(|| {
            current_thread::spawn(cname_query);
            current_thread::spawn(mx_query);
            current_thread::spawn(naptr_query);
            Ok::<(), ()>(())
        })).unwrap();
    }
}

#[cfg(unix)]
pub fn main() {
    example::main();
}

#[cfg(not(unix))]
pub fn main() {
    println!("this example is not supported on this platform");
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate wlroots;

use std::cell::{Cell, RefCell};
use std::rc::Rc;
use wlroots::{AxisEvent, ButtonEvent, Compositor, CompositorBuilder, Cursor, InputManagerHandler,
              KeyEvent, KeyboardHandler, MotionEvent, OutputBuilder, OutputBuilderResult,
              OutputHandler, OutputLayout, OutputManagerHandler, PointerHandler, XCursorTheme};
use wlroots::types::{KeyboardHandle, OutputHandle, PointerHandle};
use wlroots::wlroots_sys::gl;
use wlroots::wlroots_sys::wlr_button_state::WLR_BUTTON_RELEASED;
use wlroots::xkbcommon::xkb::keysyms::KEY_Escape;

struct OutputManager {
    color: Rc<Cell<[f32; 4]>>,
    cursor: Rc<RefCell<Cursor>>
}

struct Output {
    color: Rc<Cell<[f32; 4]>>
}

struct InputManager {
    color: Rc<Cell<[f32; 4]>>,
    cursor: Rc<RefCell<Cursor>>
}

struct Pointer {
    color: Rc<Cell<[f32; 4]>>,
    default_color: [f32; 4],
    cursor: Rc<RefCell<Cursor>>
}

struct ExKeyboardHandler;

impl OutputManagerHandler for OutputManager {
    fn output_added<'output>(&mut self,
                             _: &mut Compositor,
                             builder: OutputBuilder<'output>)
                             -> Option<OutputBuilderResult<'output>> {
        let mut result = builder.build_best_mode(Output { color: self.color.clone() });
        let mut cursor;
        {
            let output = &mut result.output;
            cursor = self.cursor.borrow_mut();
            {
                let xcursor = cursor.xcursor().expect("XCursor was not set!");
                let image = &xcursor.images()[0];
                // TODO use output config if present instead of auto
                let layout = cursor
                    .output_layout()
                    .as_ref()
                    .expect("Could not get output layout");
                output.add_layout_auto(layout.clone());
                if output.set_cursor(image).is_err() {
                    wlr_log!(L_DEBUG, "Failed to set hardware cursor");
                    return None;
                }
            }
        }
        let (x, y) = cursor.coords();
        // https://en.wikipedia.org/wiki/Mouse_warping
        cursor.warp(None, x, y);
        Some(result)
    }
}

impl KeyboardHandler for ExKeyboardHandler {
    fn on_key(&mut self,
              compositor: &mut Compositor,
              _: &mut KeyboardHandle,
              key_event: &mut KeyEvent) {
        for key in key_event.input_keys() {
            if key == KEY_Escape {
                compositor.terminate()
            }
        }
    }
}

impl PointerHandler for Pointer {
    fn on_motion(&mut self, _: &mut Compositor, _: &mut PointerHandle, event: &MotionEvent) {
        let (delta_x, delta_y) = event.delta();
        self.cursor
            .borrow_mut()
            .move_to(&event.device(), delta_x, delta_y);
    }

    fn on_button(&mut self, _: &mut Compositor, _: &mut PointerHandle, event: &ButtonEvent) {
        if event.state() == WLR_BUTTON_RELEASED {
            self.color.set(self.default_color.clone())
        } else {
            let mut red: [f32; 4] = [0.25, 0.25, 0.25, 1.0];
            red[event.button() as usize % 3] = 1.0;
            self.color.set(red);
        }
    }

    fn on_axis(&mut self, _: &mut Compositor, _: &mut PointerHandle, event: &AxisEvent) {
        for color_byte in &mut self.default_color[..3] {
            *color_byte += if event.delta() > 0.0 { -0.05 } else { 0.05 };
            if *color_byte > 1.0 {
                *color_byte = 1.0
            }
            if *color_byte < 0.0 {
                *color_byte = 0.0
            }
        }
        self.color.set(self.default_color)
    }
}

impl OutputHandler for Output {
    fn output_frame(&mut self, _: &mut Compositor, output: &mut OutputHandle) {
        output.make_current();
        unsafe {
            gl::ClearColor(self.color.get()[0],
                           self.color.get()[1],
                           self.color.get()[2],
                           1.0);
            gl::Clear(gl::COLOR_BUFFER_BIT);
        }
        output.swap_buffers();
    }
}

impl InputManagerHandler for InputManager {
    fn pointer_added(&mut self,
                     _: &mut Compositor,
                     _: &mut PointerHandle)
                     -> Option<Box<PointerHandler>> {
        Some(Box::new(Pointer {
                          color: self.color.clone(),
                          default_color: self.color.get(),
                          cursor: self.cursor.clone()
                      }))
    }

    fn keyboard_added(&mut self,
                      _: &mut Compositor,
                      _: &mut KeyboardHandle)
                      -> Option<Box<KeyboardHandler>> {
        Some(Box::new(ExKeyboardHandler))
    }
}

fn managers(mut cursor: Cursor) -> (OutputManager, InputManager) {
    let layout = Rc::new(RefCell::new(OutputLayout::new()));
    // TODO Ensure this can be safe...
    // e.g what's stopping me from simply dropping layout now that I gave it to
    // cursor?
    cursor.attach_output_layout(layout);
    let cursor = Rc::new(RefCell::new(cursor));
    let color = Rc::new(Cell::new([0.25, 0.25, 0.25, 1.0]));
    (OutputManager {
         color: color.clone(),
         cursor: cursor.clone()
     },
     InputManager {
         color: color.clone(),
         cursor: cursor.clone()
     })
}

fn main() {
    let mut cursor = Cursor::new().expect("Could not create cursor");
    let xcursor_theme = XCursorTheme::load_theme(None, 16).expect("Could not load theme");
    let xcursor = xcursor_theme
        .get_cursor("left_ptr".into())
        .expect("Could not load cursor from theme");
    cursor.set_xcursor(Some(xcursor));

    let (output_manager, input_manager) = managers(cursor);
    let compositor = CompositorBuilder::new()
        .build_auto((), Box::new(input_manager), Box::new(output_manager));
    compositor.run();
}
<commit_msg>pointer example stores all state in compositor<commit_after>#[macro_use]
extern crate wlroots;

use std::cell::RefCell;
use std::rc::Rc;
use wlroots::{AxisEvent, ButtonEvent, Compositor, CompositorBuilder, Cursor, InputManagerHandler,
              KeyEvent, KeyboardHandler, MotionEvent, OutputBuilder, OutputBuilderResult,
              OutputHandler, OutputLayout, OutputManagerHandler, PointerHandler, XCursorTheme};
use wlroots::types::{KeyboardHandle, OutputHandle, PointerHandle};
use wlroots::wlroots_sys::gl;
use wlroots::wlroots_sys::wlr_button_state::WLR_BUTTON_RELEASED;
use wlroots::xkbcommon::xkb::keysyms::KEY_Escape;

struct State {
    color: [f32; 4],
    default_color: [f32; 4],
    cursor: Cursor
}

impl State {
    fn new(cursor: Cursor) -> Self {
        State {
            color: [0.25, 0.25, 0.25, 1.0],
            default_color: [0.25, 0.25, 0.25, 1.0],
            cursor
        }
    }
}

compositor_data!(State);

struct OutputManager;

struct Output;

struct InputManager;

struct Pointer;

struct ExKeyboardHandler;

impl OutputManagerHandler for OutputManager {
    fn output_added<'output>(&mut self,
                             compositor: &mut Compositor,
                             builder: OutputBuilder<'output>)
                             -> Option<OutputBuilderResult<'output>> {
        let mut result = builder.build_best_mode(Output);
        let state: &mut State = compositor.into();
        let cursor = &mut state.cursor;
        // TODO use output config if present instead of auto
        {
            let layout = cursor
                .output_layout()
                .as_ref()
                .expect("Could not get output layout");
            result.output.add_layout_auto(layout.clone());
            let xcursor = cursor.xcursor().expect("XCursor was not set!");
            let image = &xcursor.images()[0];
            if result.output.set_cursor(image).is_err() {
                wlr_log!(L_DEBUG, "Failed to set hardware cursor");
                return None;
            }
        }
        let (x, y) = cursor.coords();
        // https://en.wikipedia.org/wiki/Mouse_warping
        cursor.warp(None, x, y);
        Some(result)
    }
}

impl KeyboardHandler for ExKeyboardHandler {
    fn on_key(&mut self,
              compositor: &mut Compositor,
              _: &mut KeyboardHandle,
              key_event: &mut KeyEvent) {
        for key in key_event.input_keys() {
            if key == KEY_Escape {
                compositor.terminate()
            }
        }
    }
}

impl PointerHandler for Pointer {
    fn on_motion(&mut self,
                 compositor: &mut Compositor,
                 _: &mut PointerHandle,
                 event: &MotionEvent) {
        let state: &mut State = compositor.into();
        let (delta_x, delta_y) = event.delta();
        state.cursor.move_to(&event.device(), delta_x, delta_y);
    }

    fn on_button(&mut self,
                 compositor: &mut Compositor,
                 _: &mut PointerHandle,
                 event: &ButtonEvent) {
        let state: &mut State = compositor.into();
        if event.state() == WLR_BUTTON_RELEASED {
            state.color = state.default_color;
        } else {
            state.color = [0.25, 0.25, 0.25, 1.0];
            state.color[event.button() as usize % 3] = 1.0;
        }
    }

    fn on_axis(&mut self, compositor: &mut Compositor, _: &mut PointerHandle, event: &AxisEvent) {
        let state: &mut State = compositor.into();
        for color_byte in &mut state.default_color[..3] {
            *color_byte += if event.delta() > 0.0 { -0.05 } else { 0.05 };
            if *color_byte > 1.0 {
                *color_byte = 1.0
            }
            if *color_byte < 0.0 {
                *color_byte = 0.0
            }
        }
        state.color = state.default_color.clone()
    }
}

impl OutputHandler for Output {
    fn output_frame(&mut self, compositor: &mut Compositor, output: &mut OutputHandle) {
        let state: &mut State = compositor.into();
        output.make_current();
        unsafe {
            gl::ClearColor(state.color[0], state.color[1], state.color[2], 1.0);
            gl::Clear(gl::COLOR_BUFFER_BIT);
        }
        output.swap_buffers();
    }
}

impl InputManagerHandler for InputManager {
    fn pointer_added(&mut self,
                     _: &mut Compositor,
                     _: &mut PointerHandle)
                     -> Option<Box<PointerHandler>> {
        Some(Box::new(Pointer))
    }

    fn keyboard_added(&mut self,
                      _: &mut Compositor,
                      _: &mut KeyboardHandle)
                      -> Option<Box<KeyboardHandler>> {
        Some(Box::new(ExKeyboardHandler))
    }
}

fn main() {
    let mut cursor = Cursor::new().expect("Could not create cursor");
    let xcursor_theme = XCursorTheme::load_theme(None, 16).expect("Could not load theme");
    let xcursor = xcursor_theme
        .get_cursor("left_ptr".into())
        .expect("Could not load cursor from theme");
    let layout = Rc::new(RefCell::new(OutputLayout::new()));
    cursor.set_xcursor(Some(xcursor));

    cursor.attach_output_layout(layout);
    let compositor = CompositorBuilder::new().build_auto(State::new(cursor),
                                                         Box::new(InputManager),
                                                         Box::new(OutputManager));
    compositor.run();
}
<|endoftext|>


<commit_before>use prolog_parser::ast::*;

use prolog::machine::machine_indices::*;
use prolog::machine::machine_state::*;
use prolog::num::bigint::BigInt;

use std::rc::Rc;

pub(super) type MachineStub = Vec<HeapCellValue>;

#[derive(Clone, Copy)]
enum ErrorProvenance {
    Constructed, // if constructed, offset the addresses.
    Received     // otherwise, preserve the addresses.
}

pub(super) struct MachineError {
    stub: MachineStub,
    from: ErrorProvenance
}

impl MachineError {
    pub(super) fn functor_stub(name: ClauseName, arity: usize) -> MachineStub {
        let name = HeapCellValue::Addr(Addr::Con(Constant::Atom(name, None)));
        functor!("/", 2, [name, heap_integer!(arity)], (400, YFX))
    }

    pub(super) fn evaluation_error(eval_error: EvalError) -> Self {
        let stub = functor!("evaluation_error", 1, [heap_atom!(eval_error.as_str())]);
        MachineError { stub, from: ErrorProvenance::Received }
    }

    pub(super) fn type_error(valid_type: ValidType, culprit: Addr) -> Self {
        let stub = functor!("type_error", 2, [heap_atom!(valid_type.as_str()),
                                              HeapCellValue::Addr(culprit)]);

        MachineError { stub, from: ErrorProvenance::Received }
    }

    pub(super)
    fn module_resolution_error(h: usize, mod_name: ClauseName, name: ClauseName, arity: usize) -> Self
    {
        let mod_name = HeapCellValue::Addr(Addr::Con(Constant::Atom(mod_name, None)));
        let name = HeapCellValue::Addr(Addr::Con(Constant::Atom(name, None)));

        let mut stub = functor!("evaluation_error", 1, [HeapCellValue::Addr(Addr::HeapCell(h + 2))]);

        stub.append(&mut functor!("/", 2, [HeapCellValue::Addr(Addr::HeapCell(h + 2 + 3)),
                                           heap_integer!(arity)],
                                  (400, YFX)));
        stub.append(&mut functor!(":", 2, [mod_name, name], (600, XFY)));

        MachineError { stub, from: ErrorProvenance::Constructed }
    }

    pub(super) fn existence_error(h: usize, name: ClauseName, arity: usize) -> Self {
        let mut stub = functor!("existence_error", 2, [heap_atom!("procedure"), heap_str!(3 + h)]);
        stub.append(&mut Self::functor_stub(name, arity));

        MachineError { stub, from: ErrorProvenance::Constructed }
    }

    // so far, this function is only called wrt dynamic database
    // transactions. their inapplicable error cases have been left
    // unhandled.
    pub(super) fn session_error(h: usize, err: SessionError) -> Self {
        match err {
            SessionError::ParserError(err) => Self::syntax_error(h, err),
            SessionError::CannotOverwriteDynamicClause(pred_str)
          | SessionError::CannotOverwriteBuiltIn(pred_str)
          | SessionError::CannotOverwriteImport(pred_str) =>
                Self::permission_error(PermissionError::Modify, pred_str),
            SessionError::ModuleDoesNotContainExport =>
                Self::permission_error(PermissionError::Access,
                                      clause_name!("module_does_not_contain_claimed_export")),
            SessionError::ModuleNotFound =>
                Self::permission_error(PermissionError::Access,
                                       clause_name!("module_does_not_exist")),
            _ => unreachable!()
        }
    }

    pub(super) fn permission_error(err: PermissionError, pred_str: ClauseName) -> Self {
        let pred_str = HeapCellValue::Addr(Addr::Con(Constant::Atom(pred_str, None)));

        let err = vec![heap_atom!(err.as_str()), pred_str];
        let mut stub = functor!("permission_error", 2);

        stub.extend(err.into_iter());

        MachineError { stub, from: ErrorProvenance::Constructed }
    }

    pub(super) fn syntax_error(h: usize, err: ParserError) -> Self {
        let err = vec![heap_atom!(err.as_str())];

        let mut stub = if err.len() == 1 {
            functor!("syntax_error", 1)
        } else {
            functor!("syntax_error", 1, [heap_str!(h + 2)])
        };

        stub.extend(err.into_iter());

        MachineError { stub, from: ErrorProvenance::Constructed }
    }

    pub(super) fn domain_error(error: DomainError, culprit: Addr) -> Self {
        let stub = functor!("domain_error", 2, [heap_atom!(error.as_str()),
                                                HeapCellValue::Addr(culprit)]);
        MachineError { stub, from: ErrorProvenance::Received }
    }

    pub(super) fn instantiation_error() -> Self {
        let stub = functor!("instantiation_error");
        MachineError { stub, from: ErrorProvenance::Received }
    }

    pub(super) fn representation_error(flag: RepFlag) -> Self {
        let stub = functor!("representation_error", 1, [heap_atom!(flag.as_str())]);
        MachineError { stub, from: ErrorProvenance::Received }
    }

    fn into_iter(self, offset: usize) -> Box<Iterator<Item=HeapCellValue>> {
        match self.from {
            ErrorProvenance::Constructed =>
                Box::new(self.stub.into_iter().map(move |hcv| {
                    match hcv {
                        HeapCellValue::Addr(addr) => HeapCellValue::Addr(addr + offset),
                        hcv => hcv
                    }
                })),
            ErrorProvenance::Received =>
                Box::new(self.stub.into_iter())
        }
    }

    fn len(&self) -> usize {
        self.stub.len()
    }
}

#[derive(Clone, Copy)]
pub enum PermissionError {
    Access,
    Modify,
}

impl PermissionError {
    pub fn as_str(self) -> &'static str {
        match self {
            PermissionError::Access => "access",
            PermissionError::Modify => "modify"
        }
    }
}

// from 7.12.2 b) of 13211-1:1995
#[derive(Clone, Copy)]
pub enum ValidType {
    Atom,
    Atomic,
    Boolean,
//    Byte,
    Callable,
//    Character,
    Compound,
//    Evaluable,
//    InByte,
//    InCharacter,
    Integer,
    List,
//    Number,
    Pair,
//    PredicateIndicator,
//    Variable
}

impl ValidType {
    pub fn as_str(self) -> &'static str {
        match self {
            ValidType::Atom => "atom",
            ValidType::Atomic => "atomic",
            ValidType::Boolean => "boolean",
//            ValidType::Byte => "byte",
            ValidType::Callable => "callable",
//            ValidType::Character => "character",
            ValidType::Compound => "compound",
//            ValidType::Evaluable => "evaluable",
//            ValidType::InByte => "in_byte",
//            ValidType::InCharacter => "in_character",
            ValidType::Integer => "integer",
            ValidType::List => "list",
//            ValidType::Number => "number",
            ValidType::Pair => "pair",
//            ValidType::PredicateIndicator => "predicate_indicator",
//            ValidType::Variable => "variable"
        }
    }
}

#[derive(Clone, Copy)]
pub enum DomainError {
    NotLessThanZero
}

impl DomainError {
    pub fn as_str(self) -> &'static str {
        match self {
            DomainError::NotLessThanZero => "not_less_than_zero"
        }
    }
}

// from 7.12.2 f) of 13211-1:1995
#[derive(Clone, Copy)]
pub enum RepFlag {
//    Character,
//    CharacterCode,
//    InCharacterCode,
    MaxArity,
//    MaxInteger,
//    MinInteger
}

impl RepFlag {
    pub fn as_str(self) -> &'static str {
        match self {
//            RepFlag::Character => "character",
//            RepFlag::CharacterCode => "character_code",
//            RepFlag::InCharacterCode => "in_character_code",
            RepFlag::MaxArity => "max_arity",
//            RepFlag::MaxInteger => "max_integer",
//            RepFlag::MinInteger => "min_integer"
        }
    }
}

// from 7.12.2 g) of 13211-1:1995
#[derive(Clone, Copy)]
pub enum EvalError {
//    FloatOverflow,
//    IntOverflow,
//    Undefined,
//    Underflow,
    ZeroDivisor,
    NoRoots
}

impl EvalError {
    pub fn as_str(self) -> &'static str {
        match self {
//            EvalError::FloatOverflow => "float_overflow",
//            EvalError::IntOverflow => "int_overflow",
//            EvalError::Undefined => "undefined",
//            EvalError::Underflow => "underflow",
            EvalError::ZeroDivisor => "zero_divisor",
            EvalError::NoRoots => "no_roots"
        }
    }
}

// used by '$skip_max_list'.
pub(super) enum CycleSearchResult {
    EmptyList,
    NotList,
    PartialList(usize, usize), // the list length (up to max), and an offset into the heap.
    ProperList(usize), // the list length.
    UntouchedList(usize) // the address of an uniterated Addr::Lis(address).
}

impl MachineState {
    // see 8.4.3 of Draft Technical Corrigendum 2.
    pub(super) fn check_sort_errors(&self) -> CallResult {
        let stub   = MachineError::functor_stub(clause_name!("sort"), 2);
        let list   = self.store(self.deref(self[temp_v!(1)].clone()));
        let sorted = self.store(self.deref(self[temp_v!(2)].clone()));

        match self.detect_cycles(list.clone()) {
            CycleSearchResult::PartialList(..) =>
                return Err(self.error_form(MachineError::instantiation_error(), stub)),
            CycleSearchResult::NotList =>
                return Err(self.error_form(MachineError::type_error(ValidType::List, list), stub)),
            _ => {}
        };

        match self.detect_cycles(sorted.clone()) {
            CycleSearchResult::NotList if !sorted.is_ref() =>
                Err(self.error_form(MachineError::type_error(ValidType::List, sorted), stub)),
            _ => Ok(())
        }
    }

    fn check_for_list_pairs(&self, list: Addr) -> CallResult {
        let stub = MachineError::functor_stub(clause_name!("keysort"), 2);

        match self.detect_cycles(list.clone()) {
            CycleSearchResult::NotList if !list.is_ref() =>
                Err(self.error_form(MachineError::type_error(ValidType::List, list), stub)),
            _ => {
                let mut addr = list;

                while let Addr::Lis(l) = self.store(self.deref(addr)) {
                    let mut new_l = l;

                    loop {
                        match self.heap[new_l].clone() {
                            HeapCellValue::Addr(Addr::Str(l)) => new_l = l,
                            HeapCellValue::NamedStr(2, ref name, Some(_))
                                if name.as_str() == "-" => break,
                            HeapCellValue::Addr(Addr::HeapCell(_)) => break,
                            HeapCellValue::Addr(Addr::StackCell(..)) => break,
                            _ => return Err(self.error_form(MachineError::type_error(ValidType::Pair,
                                                                                Addr::HeapCell(l)),
                                                            stub))
                        };
                    }

                    addr = Addr::HeapCell(l + 1);
                }

                Ok(())
            }
        }
    }

    // see 8.4.4 of Draft Technical Corrigendum 2.
    pub(super) fn check_keysort_errors(&self) -> CallResult {
        let stub   = MachineError::functor_stub(clause_name!("keysort"), 2);
        let pairs  = self.store(self.deref(self[temp_v!(1)].clone()));
        let sorted = self.store(self.deref(self[temp_v!(2)].clone()));

        match self.detect_cycles(pairs.clone()) {
            CycleSearchResult::PartialList(..) =>
                Err(self.error_form(MachineError::instantiation_error(), stub)),
            CycleSearchResult::NotList =>
                Err(self.error_form(MachineError::type_error(ValidType::List, pairs), stub)),
            _ => Ok(())
        }?;

        self.check_for_list_pairs(sorted)
    }

    pub(super) fn error_form(&self, err: MachineError, src: MachineStub) -> MachineStub {
        let h = self.heap.h;
        let mut stub = vec![HeapCellValue::NamedStr(2, clause_name!("error"), None),
                            HeapCellValue::Addr(Addr::HeapCell(h + 3)),
                            HeapCellValue::Addr(Addr::HeapCell(h + 3 + err.len()))];

        stub.extend(err.into_iter(3));
        stub.extend(src.into_iter());

        stub
    }

    pub(super) fn throw_exception(&mut self, err: MachineStub) {
        let h = self.heap.h;

        self.ball.boundary = 0;
        self.ball.stub.truncate(0);

        self.heap.append(err);

        self.registers[1] = Addr::HeapCell(h);

        self.set_ball();
        self.unwind_stack();
    }
}

pub enum SessionError {
    CannotOverwriteBuiltIn(ClauseName),
    CannotOverwriteDynamicClause(ClauseName),
    CannotOverwriteImport(ClauseName),
    ModuleDoesNotContainExport,
    ModuleNotFound,
    NamelessEntry,
    OpIsInfixAndPostFix,
    ParserError(ParserError),
    QueryFailure,
    QueryFailureWithException(ClauseName),
    UserPrompt
}

pub enum EvalSession {
    EntrySuccess,
    Error(SessionError),
    InitialQuerySuccess(AllocVarDict, HeapVarDict),
    SubsequentQuerySuccess,
}

impl From<SessionError> for EvalSession {
    fn from(err: SessionError) -> Self {
        EvalSession::Error(err)
    }
}

impl From<ParserError> for SessionError {
    fn from(err: ParserError) -> Self {
        SessionError::ParserError(err)
    }
}

impl From<ParserError> for EvalSession {
    fn from(err: ParserError) -> Self {
        EvalSession::from(SessionError::ParserError(err))
    }
}
<commit_msg>improve Rust-generated permissions errors<commit_after>use prolog_parser::ast::*;

use prolog::machine::machine_indices::*;
use prolog::machine::machine_state::*;
use prolog::num::bigint::BigInt;

use std::rc::Rc;

pub(super) type MachineStub = Vec<HeapCellValue>;

#[derive(Clone, Copy)]
enum ErrorProvenance {
    Constructed, // if constructed, offset the addresses.
    Received     // otherwise, preserve the addresses.
}

pub(super) struct MachineError {
    stub: MachineStub,
    from: ErrorProvenance
}

impl MachineError {
    pub(super) fn functor_stub(name: ClauseName, arity: usize) -> MachineStub {
        let name = HeapCellValue::Addr(Addr::Con(Constant::Atom(name, None)));
        functor!("/", 2, [name, heap_integer!(arity)], (400, YFX))
    }

    pub(super) fn evaluation_error(eval_error: EvalError) -> Self {
        let stub = functor!("evaluation_error", 1, [heap_atom!(eval_error.as_str())]);
        MachineError { stub, from: ErrorProvenance::Received }
    }

    pub(super) fn type_error(valid_type: ValidType, culprit: Addr) -> Self {
        let stub = functor!("type_error", 2, [heap_atom!(valid_type.as_str()),
                                              HeapCellValue::Addr(culprit)]);

        MachineError { stub, from: ErrorProvenance::Received }
    }

    pub(super)
    fn module_resolution_error(h: usize, mod_name: ClauseName, name: ClauseName, arity: usize) -> Self
    {
        let mod_name = HeapCellValue::Addr(Addr::Con(Constant::Atom(mod_name, None)));
        let name = HeapCellValue::Addr(Addr::Con(Constant::Atom(name, None)));

        let mut stub = functor!("evaluation_error", 1, [HeapCellValue::Addr(Addr::HeapCell(h + 2))]);

        stub.append(&mut functor!("/", 2, [HeapCellValue::Addr(Addr::HeapCell(h + 2 + 3)),
                                           heap_integer!(arity)],
                                  (400, YFX)));
        stub.append(&mut functor!(":", 2, [mod_name, name], (600, XFY)));

        MachineError { stub, from: ErrorProvenance::Constructed }
    }

    pub(super) fn existence_error(h: usize, name: ClauseName, arity: usize) -> Self {
        let mut stub = functor!("existence_error", 2, [heap_atom!("procedure"), heap_str!(3 + h)]);
        stub.append(&mut Self::functor_stub(name, arity));

        MachineError { stub, from: ErrorProvenance::Constructed }
    }

    // so far, this function is only called wrt dynamic database
    // transactions. their inapplicable error cases have been left
    // unhandled.
    pub(super) fn session_error(h: usize, err: SessionError) -> Self {
        match err {
            SessionError::ParserError(err) => Self::syntax_error(h, err),
            SessionError::CannotOverwriteDynamicClause(pred_str)
          | SessionError::CannotOverwriteBuiltIn(pred_str)
          | SessionError::CannotOverwriteImport(pred_str) =>
                Self::permission_error(PermissionError::Modify, pred_str),
            SessionError::ModuleDoesNotContainExport =>
                Self::permission_error(PermissionError::Access,
                                      clause_name!("module_does_not_contain_claimed_export")),
            SessionError::ModuleNotFound =>
                Self::permission_error(PermissionError::Access,
                                       clause_name!("module_does_not_exist")),
            _ => unreachable!()
        }
    }

    pub(super)
    fn permission_error(err: PermissionError, pred_str: ClauseName) -> Self
    {
        let pred_str = HeapCellValue::Addr(Addr::Con(Constant::Atom(pred_str, None)));

        let err = vec![heap_atom!(err.as_str()), heap_atom!("private_procedure"), pred_str];
        let mut stub = functor!("permission_error", 3);

        stub.extend(err.into_iter());

        MachineError { stub, from: ErrorProvenance::Constructed }
    }

    pub(super) fn syntax_error(h: usize, err: ParserError) -> Self {
        let err = vec![heap_atom!(err.as_str())];

        let mut stub = if err.len() == 1 {
            functor!("syntax_error", 1)
        } else {
            functor!("syntax_error", 1, [heap_str!(h + 2)])
        };

        stub.extend(err.into_iter());

        MachineError { stub, from: ErrorProvenance::Constructed }
    }

    pub(super) fn domain_error(error: DomainError, culprit: Addr) -> Self {
        let stub = functor!("domain_error", 2, [heap_atom!(error.as_str()),
                                                HeapCellValue::Addr(culprit)]);
        MachineError { stub, from: ErrorProvenance::Received }
    }

    pub(super) fn instantiation_error() -> Self {
        let stub = functor!("instantiation_error");
        MachineError { stub, from: ErrorProvenance::Received }
    }

    pub(super) fn representation_error(flag: RepFlag) -> Self {
        let stub = functor!("representation_error", 1, [heap_atom!(flag.as_str())]);
        MachineError { stub, from: ErrorProvenance::Received }
    }

    fn into_iter(self, offset: usize) -> Box<Iterator<Item=HeapCellValue>> {
        match self.from {
            ErrorProvenance::Constructed =>
                Box::new(self.stub.into_iter().map(move |hcv| {
                    match hcv {
                        HeapCellValue::Addr(addr) => HeapCellValue::Addr(addr + offset),
                        hcv => hcv
                    }
                })),
            ErrorProvenance::Received =>
                Box::new(self.stub.into_iter())
        }
    }

    fn len(&self) -> usize {
        self.stub.len()
    }
}

#[derive(Clone, Copy)]
pub enum PermissionError {
    Access,
    Modify,
}

impl PermissionError {
    pub fn as_str(self) -> &'static str {
        match self {
            PermissionError::Access => "access",
            PermissionError::Modify => "modify"
        }
    }
}

// from 7.12.2 b) of 13211-1:1995
#[derive(Clone, Copy)]
pub enum ValidType {
    Atom,
    Atomic,
    Boolean,
//    Byte,
    Callable,
//    Character,
    Compound,
//    Evaluable,
//    InByte,
//    InCharacter,
    Integer,
    List,
//    Number,
    Pair,
//    PredicateIndicator,
//    Variable
}

impl ValidType {
    pub fn as_str(self) -> &'static str {
        match self {
            ValidType::Atom => "atom",
            ValidType::Atomic => "atomic",
            ValidType::Boolean => "boolean",
//            ValidType::Byte => "byte",
            ValidType::Callable => "callable",
//            ValidType::Character => "character",
            ValidType::Compound => "compound",
//            ValidType::Evaluable => "evaluable",
//            ValidType::InByte => "in_byte",
//            ValidType::InCharacter => "in_character",
            ValidType::Integer => "integer",
            ValidType::List => "list",
//            ValidType::Number => "number",
            ValidType::Pair => "pair",
//            ValidType::PredicateIndicator => "predicate_indicator",
//            ValidType::Variable => "variable"
        }
    }
}

#[derive(Clone, Copy)]
pub enum DomainError {
    NotLessThanZero
}

impl DomainError {
    pub fn as_str(self) -> &'static str {
        match self {
            DomainError::NotLessThanZero => "not_less_than_zero"
        }
    }
}

// from 7.12.2 f) of 13211-1:1995
#[derive(Clone, Copy)]
pub enum RepFlag {
//    Character,
//    CharacterCode,
//    InCharacterCode,
    MaxArity,
//    MaxInteger,
//    MinInteger
}

impl RepFlag {
    pub fn as_str(self) -> &'static str {
        match self {
//            RepFlag::Character => "character",
//            RepFlag::CharacterCode => "character_code",
//            RepFlag::InCharacterCode => "in_character_code",
            RepFlag::MaxArity => "max_arity",
//            RepFlag::MaxInteger => "max_integer",
//            RepFlag::MinInteger => "min_integer"
        }
    }
}

// from 7.12.2 g) of 13211-1:1995
#[derive(Clone, Copy)]
pub enum EvalError {
//    FloatOverflow,
//    IntOverflow,
//    Undefined,
//    Underflow,
    ZeroDivisor,
    NoRoots
}

impl EvalError {
    pub fn as_str(self) -> &'static str {
        match self {
//            EvalError::FloatOverflow => "float_overflow",
//            EvalError::IntOverflow => "int_overflow",
//            EvalError::Undefined => "undefined",
//            EvalError::Underflow => "underflow",
            EvalError::ZeroDivisor => "zero_divisor",
            EvalError::NoRoots => "no_roots"
        }
    }
}

// used by '$skip_max_list'.
pub(super) enum CycleSearchResult {
    EmptyList,
    NotList,
    PartialList(usize, usize), // the list length (up to max), and an offset into the heap.
    ProperList(usize), // the list length.
    UntouchedList(usize) // the address of an uniterated Addr::Lis(address).
}

impl MachineState {
    // see 8.4.3 of Draft Technical Corrigendum 2.
    pub(super) fn check_sort_errors(&self) -> CallResult {
        let stub   = MachineError::functor_stub(clause_name!("sort"), 2);
        let list   = self.store(self.deref(self[temp_v!(1)].clone()));
        let sorted = self.store(self.deref(self[temp_v!(2)].clone()));

        match self.detect_cycles(list.clone()) {
            CycleSearchResult::PartialList(..) =>
                return Err(self.error_form(MachineError::instantiation_error(), stub)),
            CycleSearchResult::NotList =>
                return Err(self.error_form(MachineError::type_error(ValidType::List, list), stub)),
            _ => {}
        };

        match self.detect_cycles(sorted.clone()) {
            CycleSearchResult::NotList if !sorted.is_ref() =>
                Err(self.error_form(MachineError::type_error(ValidType::List, sorted), stub)),
            _ => Ok(())
        }
    }

    fn check_for_list_pairs(&self, list: Addr) -> CallResult {
        let stub = MachineError::functor_stub(clause_name!("keysort"), 2);

        match self.detect_cycles(list.clone()) {
            CycleSearchResult::NotList if !list.is_ref() =>
                Err(self.error_form(MachineError::type_error(ValidType::List, list), stub)),
            _ => {
                let mut addr = list;

                while let Addr::Lis(l) = self.store(self.deref(addr)) {
                    let mut new_l = l;

                    loop {
                        match self.heap[new_l].clone() {
                            HeapCellValue::Addr(Addr::Str(l)) => new_l = l,
                            HeapCellValue::NamedStr(2, ref name, Some(_))
                                if name.as_str() == "-" => break,
                            HeapCellValue::Addr(Addr::HeapCell(_)) => break,
                            HeapCellValue::Addr(Addr::StackCell(..)) => break,
                            _ => return Err(self.error_form(MachineError::type_error(ValidType::Pair,
                                                                                Addr::HeapCell(l)),
                                                            stub))
                        };
                    }

                    addr = Addr::HeapCell(l + 1);
                }

                Ok(())
            }
        }
    }

    // see 8.4.4 of Draft Technical Corrigendum 2.
    pub(super) fn check_keysort_errors(&self) -> CallResult {
        let stub   = MachineError::functor_stub(clause_name!("keysort"), 2);
        let pairs  = self.store(self.deref(self[temp_v!(1)].clone()));
        let sorted = self.store(self.deref(self[temp_v!(2)].clone()));

        match self.detect_cycles(pairs.clone()) {
            CycleSearchResult::PartialList(..) =>
                Err(self.error_form(MachineError::instantiation_error(), stub)),
            CycleSearchResult::NotList =>
                Err(self.error_form(MachineError::type_error(ValidType::List, pairs), stub)),
            _ => Ok(())
        }?;

        self.check_for_list_pairs(sorted)
    }

    pub(super) fn error_form(&self, err: MachineError, src: MachineStub) -> MachineStub {
        let h = self.heap.h;
        let mut stub = vec![HeapCellValue::NamedStr(2, clause_name!("error"), None),
                            HeapCellValue::Addr(Addr::HeapCell(h + 3)),
                            HeapCellValue::Addr(Addr::HeapCell(h + 3 + err.len()))];

        stub.extend(err.into_iter(3));
        stub.extend(src.into_iter());

        stub
    }

    pub(super) fn throw_exception(&mut self, err: MachineStub) {
        let h = self.heap.h;

        self.ball.boundary = 0;
        self.ball.stub.truncate(0);

        self.heap.append(err);

        self.registers[1] = Addr::HeapCell(h);

        self.set_ball();
        self.unwind_stack();
    }
}

pub enum SessionError {
    CannotOverwriteBuiltIn(ClauseName),
    CannotOverwriteDynamicClause(ClauseName),
    CannotOverwriteImport(ClauseName),
    ModuleDoesNotContainExport,
    ModuleNotFound,
    NamelessEntry,
    OpIsInfixAndPostFix,
    ParserError(ParserError),
    QueryFailure,
    QueryFailureWithException(ClauseName),
    UserPrompt
}

pub enum EvalSession {
    EntrySuccess,
    Error(SessionError),
    InitialQuerySuccess(AllocVarDict, HeapVarDict),
    SubsequentQuerySuccess,
}

impl From<SessionError> for EvalSession {
    fn from(err: SessionError) -> Self {
        EvalSession::Error(err)
    }
}

impl From<ParserError> for SessionError {
    fn from(err: ParserError) -> Self {
        SessionError::ParserError(err)
    }
}

impl From<ParserError> for EvalSession {
    fn from(err: ParserError) -> Self {
        EvalSession::from(SessionError::ParserError(err))
    }
}
<|endoftext|>


<commit_before>// Copyright 2015-2017 Benjamin Fry <benjaminfry@me.com>
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

//! System configuration loading
//!
//! This module is resposible for parsing and returning the configuration from
//!  the host system. It will read from the default location on each operating
//!  system, e.g. most Unixes have this written to `/etc/resolv.conf`
#![allow(missing_docs)]

/// resolv.conf parser
// TODO: make crate only...
mod resolv_conf_ast;

use std::fs::File;
use std::io;
use std::io::Read;
use std::path::Path;

use trust_dns::rr::Name;

use config::*;
use self::resolv_conf_ast::*;

pub(crate) mod resolv_conf {
    #![allow(unused)]
    // lalrpop from the build script generates the grammar to this file,
    //  see build.rs for the resolver for details.
    include!(concat!(env!("OUT_DIR"), "/system_conf/resolv_conf.rs"));
}

#[cfg(unix)]
pub(crate) fn read_system_conf() -> io::Result<(ResolverConfig, ResolverOpts)> {
    read_resolv_conf("/etc/resolv.conf")
}

#[cfg(not(unix))]
pub(crate) fn read_system_conf() -> io::Result<(ResolverConfig, ResolverOpts)> {
    Err(io::Error::new(
        io::ErrorKind::NotFound,
        format!(
            "Unsupported OS, pass explicit configuration to Resolver::new(..)"
        ),
    ))
}

pub fn read_resolv_conf<P: AsRef<Path>>(path: P) -> io::Result<(ResolverConfig, ResolverOpts)> {
    let mut data = String::new();
    let mut file = File::open(path)?;
    file.read_to_string(&mut data)?;

    // TODO: what to do with these errors?
    let mut errors = Vec::new();
    let conf = resolv_conf::parse_config(&mut errors, &data).map_err(|e| {
        io::Error::new(
            io::ErrorKind::Other,
            format!("Error parsing resolv.conf: {:?}", e),
        )
    })?;

    Ok(into_resolver_config(conf))
}

pub fn into_resolver_config(config_opts: Vec<ConfigOption>) -> (ResolverConfig, ResolverOpts) {
    let mut domain = Option::None::<Name>;
    let mut search = Option::None::<Vec<Name>>;
    let mut nameservers = Vec::<NameServerConfig>::new();
    let mut options = Option::None::<ResolverOpts>;

    for config_opt in config_opts {
        match config_opt {
            ConfigOption::Basic(BasicOption::Domain(name)) => domain = Some(name),
            ConfigOption::Basic(BasicOption::Search(names)) => search = Some(names),
            ConfigOption::Basic(nameserver) => {
                nameserver.push_nameserver(&mut nameservers).ok();
            }
            ConfigOption::Advanced(advanced_opts) => {
                options = Some(advanced_opts.into_iter().fold(
                    ResolverOpts::default(),
                    |mut ropts, advanced| {
                        match advanced {
                            AdvancedOption::NumberOfDots(ndots) => ropts.ndots = ndots as usize,
                            AdvancedOption::Timeout(dur) => ropts.timeout = dur,
                            AdvancedOption::Attempts(attempts) => {
                                ropts.attempts = attempts as usize
                            }
                            AdvancedOption::Unknown(..) => (),
                        }
                        ropts
                    },
                ))
            }
            //_ => (),
        }
    }

    let config = ResolverConfig::from_parts(
        domain.unwrap_or_else(Name::root),
        search.unwrap_or_else(Vec::new),
        nameservers,
    );

    // if there are no nameservers, let's get some defaults
    if config.name_servers().is_empty() {
        warn!("no nameservers found in config");
    }

    (config, options.unwrap_or_else(ResolverOpts::default))
}

#[cfg(test)]
mod tests {
    use std::fs::File;
    use std::io::Read;
    use std::net::*;
    use std::str::FromStr;
    use std::time::Duration;
    use trust_dns::rr::Name;
    use super::*;

    #[test]
    fn test_comment() {
        let mut errors = Vec::new();
        resolv_conf::parse_comment(&mut errors, "#").unwrap();
        resolv_conf::parse_comment(&mut errors, ";").unwrap();
        resolv_conf::parse_comment(&mut errors, "#junk").unwrap();
        resolv_conf::parse_comment(&mut errors, "# junk").unwrap();
        resolv_conf::parse_comment(&mut errors, ";junk").unwrap();
        resolv_conf::parse_comment(&mut errors, "; junk").unwrap();
    }

    #[test]
    fn test_basic_options() {
        let mut errors = Vec::new();
        assert_eq!(
            resolv_conf::parse_basic_option(&mut errors, "nameserver 127.0.0.1").expect("failed"),
            BasicOption::Nameserver(IpAddr::from_str("127.0.0.1").unwrap())
        );
        assert_eq!(
            resolv_conf::parse_basic_option(&mut errors, "search localnet.").expect("failed"),
            BasicOption::Search(vec![Name::from_labels(vec!["localnet"])])
        );
        assert_eq!(
            resolv_conf::parse_basic_option(&mut errors, "domain example.com.").expect("failed"),
            BasicOption::Domain(Name::from_labels(vec!["example", "com"]))
        );
    }

    #[test]
    fn test_ip_addr() {
        let mut errors = Vec::new();
        assert_eq!(
            resolv_conf::parse_ip_addr(&mut errors, "127.0.0.1").expect("failed"),
            Ipv4Addr::new(127, 0, 0, 1)
        );

        assert_eq!(
            resolv_conf::parse_ip_addr(&mut errors, "::1").expect("failed"),
            Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)
        );

        assert_eq!(
            resolv_conf::parse_ip_addr(&mut errors, "2001:db8:85a3:8d3:1319:8a2e:370:7348")
                .expect("failed"),
            Ipv6Addr::new(0x2001, 0xdb8, 0x85a3, 0x8d3, 0x1319, 0x8a2e, 0x370, 0x7348)
        );

        assert_eq!(
            resolv_conf::parse_ip_addr(&mut errors, "::ffff:192.0.2.128").expect("failed"),
            Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc000, 0x0280)
        );
    }

    #[test]
    fn test_name() {
        let mut errors = Vec::new();
        assert_eq!(
            resolv_conf::parse_name(&mut errors, ".").unwrap(),
            Name::from_labels::<String>(vec![])
        );

        assert_eq!(
            resolv_conf::parse_name(&mut errors, "com.").unwrap(),
            Name::from_labels(vec!["com"])
        );

        assert_eq!(
            resolv_conf::parse_name(&mut errors, "example.com.").unwrap(),
            Name::from_labels(vec!["example", "com"])
        );
    }

    #[test]
    fn test_config_line() {
        let mut errors = Vec::new();
        // no comment
        assert_eq!(
            resolv_conf::parse_config_line(&mut errors, "nameserver 127.0.0.1").expect("failed"),
            Some(ConfigOption::Basic(BasicOption::Nameserver(
                IpAddr::from_str("127.0.0.1").unwrap(),
            )))
        );

        // FIXME: add these tests back, need a custom Lexer for comments...
        // assert_eq!(
        //     resolv_conf::parse_config_line(&mut errors, "nameserver 127.0.0.1; a comment")
        //         .expect("failed"),
        //     Some(ConfigOption::Basic(BasicOption::Nameserver(
        //         IpAddr::from_str("127.0.0.1").unwrap(),
        //     )))
        // );

        // assert_eq!(
        //     resolv_conf::parse_config_line(&mut errors, "nameserver 127.0.0.1# a comment")
        //         .expect("failed"),
        //     Some(ConfigOption::Basic(BasicOption::Nameserver(
        //         IpAddr::from_str("127.0.0.1").unwrap(),
        //     )))
        // );

        // assert_eq!(
        //     resolv_conf::parse_config_line(&mut errors, "nameserver 127.0.0.1 #a comment")
        //         .expect("failed"),
        //     Some(ConfigOption::Basic(BasicOption::Nameserver(
        //         IpAddr::from_str("127.0.0.1").unwrap(),
        //     )))
        // );

        // assert_eq!(
        //     resolv_conf::parse_config_line(&mut errors, "nameserver 127.0.0.1 # a comment")
        //         .expect("failed"),
        //     Some(ConfigOption::Basic(BasicOption::Nameserver(
        //         IpAddr::from_str("127.0.0.1").unwrap(),
        //     )))
        // );

        // assert_eq!(
        //     resolv_conf::parse_config_line(&mut errors, "options ndots:8 # a comment")
        //         .expect("failed"),
        //     Some(ConfigOption::Advanced(
        //         vec![AdvancedOption::NumberOfDots(8)],
        //     ))
        // );

        // only comment
        assert_eq!(
            resolv_conf::parse_config_line(&mut errors, "# a comment").expect("failed"),
            None
        );
    }

    #[test]
    fn test_advanced_option() {
        let mut errors = Vec::new();
        assert_eq!(
            resolv_conf::parse_advanced_option(&mut errors, "ndots:8").expect("failed"),
            AdvancedOption::NumberOfDots(8)
        );

        assert_eq!(
            resolv_conf::parse_advanced_option(&mut errors, "timeout:8").expect("failed"),
            AdvancedOption::Timeout(Duration::from_secs(8))
        );

        assert_eq!(
            resolv_conf::parse_advanced_option(&mut errors, "attempts:8").expect("failed"),
            AdvancedOption::Attempts(8)
        );
    }

    #[test]
    fn test_advanced_options() {
        let mut errors = Vec::new();
        assert_eq!(
            resolv_conf::parse_advanced_options(&mut errors, "options ndots:8 timeout:8 attempts:8")
                .expect("failed"),
            vec![
                AdvancedOption::NumberOfDots(8),
                AdvancedOption::Timeout(Duration::from_secs(8)),
                AdvancedOption::Attempts(8),
            ]
        );
    }

    #[test]
    fn test_resolv_conf_macos() {
        let mut data = String::new();
        let mut file = File::open("tests/resolv.conf-macos").unwrap();
        file.read_to_string(&mut data).unwrap();

        let configuration = vec![
            ConfigOption::Advanced(vec![
                AdvancedOption::NumberOfDots(8),
                AdvancedOption::Timeout(Duration::from_secs(8)),
                AdvancedOption::Attempts(8),
            ]),
            ConfigOption::Basic(BasicOption::Domain(
                Name::from_labels(vec!["example", "com"]),
            )),
            ConfigOption::Basic(BasicOption::Search(vec![
                Name::from_labels(vec!["example", "com"]),
                Name::from_labels(vec!["sub", "example", "com"]),
            ])),
            ConfigOption::Basic(BasicOption::Nameserver(
                IpAddr::from_str("2001:4860:4860::8888").unwrap(),
            )),
            ConfigOption::Basic(BasicOption::Nameserver(
                IpAddr::from_str("2001:4860:4860::8844").unwrap(),
            )),
            ConfigOption::Basic(BasicOption::Nameserver(
                IpAddr::from_str("8.8.8.8").unwrap(),
            )),
            ConfigOption::Basic(BasicOption::Nameserver(
                IpAddr::from_str("8.8.4.4").unwrap(),
            )),
        ];

        let mut errors = Vec::new();
        assert_eq!(
            resolv_conf::parse_config(&mut errors, &data).expect("failed"),
            configuration
        );
    }

    #[test]
    fn test_resolv_conf_linux() {
        let mut data = String::new();
        let mut file = File::open("tests/resolv.conf-linux").unwrap();
        file.read_to_string(&mut data).unwrap();

        let configuration =
            vec![
                ConfigOption::Advanced(vec![
                    AdvancedOption::NumberOfDots(8),
                    AdvancedOption::Timeout(Duration::from_secs(8)),
                    AdvancedOption::Attempts(8),
                ]),
                ConfigOption::Basic(BasicOption::Domain(
                    Name::from_labels(vec!["example", "com"]),
                )),
                ConfigOption::Basic(BasicOption::Search(vec![
                    Name::from_labels(vec!["example", "com"]),
                    Name::from_labels(vec!["sub", "example", "com"]),
                ])),
                ConfigOption::Basic(BasicOption::Nameserver(
                    IpAddr::from_str("2001:4860:4860::8888").unwrap(),
                )),
                ConfigOption::Basic(BasicOption::Nameserver(
                    IpAddr::from_str("2001:4860:4860::8844").unwrap(),
                )),
                ConfigOption::Basic(BasicOption::Nameserver(
                    IpAddr::from_str("8.8.8.8").unwrap(),
                )),
                ConfigOption::Basic(BasicOption::Nameserver(
                    IpAddr::from_str("8.8.4.4").unwrap(),
                )),
                ConfigOption::Advanced(vec![AdvancedOption::Unknown("rotate", None)]),
                ConfigOption::Advanced(vec![
                    AdvancedOption::Unknown("inet6", None),
                    AdvancedOption::Unknown("no-tld-query", None),
                ]),
                ConfigOption::Basic(BasicOption::SortList(
                    "sortlist 130.155.160.0/255.255.240.0 130.155.0.0",
                )),
            ];

        let mut errors = Vec::new();
        assert_eq!(
            resolv_conf::parse_config(&mut errors, &data).expect("failed"),
            configuration
        );
    }

    #[test]
    fn test_read_resolv_conf() {
        read_resolv_conf("tests/resolv.conf-simple").expect("simple failed");
        read_resolv_conf("tests/resolv.conf-macos").expect("macos failed");
        read_resolv_conf("tests/resolv.conf-linux").expect("linux failed");
    }
}<commit_msg>fixup path for resolver coverage<commit_after>// Copyright 2015-2017 Benjamin Fry <benjaminfry@me.com>
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

//! System configuration loading
//!
//! This module is resposible for parsing and returning the configuration from
//!  the host system. It will read from the default location on each operating
//!  system, e.g. most Unixes have this written to `/etc/resolv.conf`
#![allow(missing_docs)]

/// resolv.conf parser
// TODO: make crate only...
mod resolv_conf_ast;

use std::fs::File;
use std::io;
use std::io::Read;
use std::path::Path;

use trust_dns::rr::Name;

use config::*;
use self::resolv_conf_ast::*;

pub(crate) mod resolv_conf {
    #![allow(unused)]
    // lalrpop from the build script generates the grammar to this file,
    //  see build.rs for the resolver for details.
    include!(concat!(env!("OUT_DIR"), "/system_conf/resolv_conf.rs"));
}

#[cfg(unix)]
pub(crate) fn read_system_conf() -> io::Result<(ResolverConfig, ResolverOpts)> {
    read_resolv_conf("/etc/resolv.conf")
}

#[cfg(not(unix))]
pub(crate) fn read_system_conf() -> io::Result<(ResolverConfig, ResolverOpts)> {
    Err(io::Error::new(
        io::ErrorKind::NotFound,
        format!(
            "Unsupported OS, pass explicit configuration to Resolver::new(..)"
        ),
    ))
}

pub fn read_resolv_conf<P: AsRef<Path>>(path: P) -> io::Result<(ResolverConfig, ResolverOpts)> {
    let mut data = String::new();
    let mut file = File::open(path)?;
    file.read_to_string(&mut data)?;

    // TODO: what to do with these errors?
    let mut errors = Vec::new();
    let conf = resolv_conf::parse_config(&mut errors, &data).map_err(|e| {
        io::Error::new(
            io::ErrorKind::Other,
            format!("Error parsing resolv.conf: {:?}", e),
        )
    })?;

    Ok(into_resolver_config(conf))
}

pub fn into_resolver_config(config_opts: Vec<ConfigOption>) -> (ResolverConfig, ResolverOpts) {
    let mut domain = Option::None::<Name>;
    let mut search = Option::None::<Vec<Name>>;
    let mut nameservers = Vec::<NameServerConfig>::new();
    let mut options = Option::None::<ResolverOpts>;

    for config_opt in config_opts {
        match config_opt {
            ConfigOption::Basic(BasicOption::Domain(name)) => domain = Some(name),
            ConfigOption::Basic(BasicOption::Search(names)) => search = Some(names),
            ConfigOption::Basic(nameserver) => {
                nameserver.push_nameserver(&mut nameservers).ok();
            }
            ConfigOption::Advanced(advanced_opts) => {
                options = Some(advanced_opts.into_iter().fold(
                    ResolverOpts::default(),
                    |mut ropts, advanced| {
                        match advanced {
                            AdvancedOption::NumberOfDots(ndots) => ropts.ndots = ndots as usize,
                            AdvancedOption::Timeout(dur) => ropts.timeout = dur,
                            AdvancedOption::Attempts(attempts) => {
                                ropts.attempts = attempts as usize
                            }
                            AdvancedOption::Unknown(..) => (),
                        }
                        ropts
                    },
                ))
            }
            //_ => (),
        }
    }

    let config = ResolverConfig::from_parts(
        domain.unwrap_or_else(Name::root),
        search.unwrap_or_else(Vec::new),
        nameservers,
    );

    // if there are no nameservers, let's get some defaults
    if config.name_servers().is_empty() {
        warn!("no nameservers found in config");
    }

    (config, options.unwrap_or_else(ResolverOpts::default))
}

#[cfg(test)]
mod tests {
    use std::env;
    use std::fs::File;
    use std::io::Read;
    use std::net::*;
    use std::str::FromStr;
    use std::time::Duration;
    use trust_dns::rr::Name;
    use super::*;

    fn tests_dir() -> String {
        let server_path = env::var("TDNS_SERVER_SRC_ROOT").unwrap_or(".".to_owned());
        format!{"{}/../resolver/tests", server_path}
    }

    #[test]
    fn test_comment() {
        let mut errors = Vec::new();
        resolv_conf::parse_comment(&mut errors, "#").unwrap();
        resolv_conf::parse_comment(&mut errors, ";").unwrap();
        resolv_conf::parse_comment(&mut errors, "#junk").unwrap();
        resolv_conf::parse_comment(&mut errors, "# junk").unwrap();
        resolv_conf::parse_comment(&mut errors, ";junk").unwrap();
        resolv_conf::parse_comment(&mut errors, "; junk").unwrap();
    }

    #[test]
    fn test_basic_options() {
        let mut errors = Vec::new();
        assert_eq!(
            resolv_conf::parse_basic_option(&mut errors, "nameserver 127.0.0.1").expect("failed"),
            BasicOption::Nameserver(IpAddr::from_str("127.0.0.1").unwrap())
        );
        assert_eq!(
            resolv_conf::parse_basic_option(&mut errors, "search localnet.").expect("failed"),
            BasicOption::Search(vec![Name::from_labels(vec!["localnet"])])
        );
        assert_eq!(
            resolv_conf::parse_basic_option(&mut errors, "domain example.com.").expect("failed"),
            BasicOption::Domain(Name::from_labels(vec!["example", "com"]))
        );
    }

    #[test]
    fn test_ip_addr() {
        let mut errors = Vec::new();
        assert_eq!(
            resolv_conf::parse_ip_addr(&mut errors, "127.0.0.1").expect("failed"),
            Ipv4Addr::new(127, 0, 0, 1)
        );

        assert_eq!(
            resolv_conf::parse_ip_addr(&mut errors, "::1").expect("failed"),
            Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)
        );

        assert_eq!(
            resolv_conf::parse_ip_addr(&mut errors, "2001:db8:85a3:8d3:1319:8a2e:370:7348")
                .expect("failed"),
            Ipv6Addr::new(0x2001, 0xdb8, 0x85a3, 0x8d3, 0x1319, 0x8a2e, 0x370, 0x7348)
        );

        assert_eq!(
            resolv_conf::parse_ip_addr(&mut errors, "::ffff:192.0.2.128").expect("failed"),
            Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc000, 0x0280)
        );
    }

    #[test]
    fn test_name() {
        let mut errors = Vec::new();
        assert_eq!(
            resolv_conf::parse_name(&mut errors, ".").unwrap(),
            Name::from_labels::<String>(vec![])
        );

        assert_eq!(
            resolv_conf::parse_name(&mut errors, "com.").unwrap(),
            Name::from_labels(vec!["com"])
        );

        assert_eq!(
            resolv_conf::parse_name(&mut errors, "example.com.").unwrap(),
            Name::from_labels(vec!["example", "com"])
        );
    }

    #[test]
    fn test_config_line() {
        let mut errors = Vec::new();
        // no comment
        assert_eq!(
            resolv_conf::parse_config_line(&mut errors, "nameserver 127.0.0.1").expect("failed"),
            Some(ConfigOption::Basic(BasicOption::Nameserver(
                IpAddr::from_str("127.0.0.1").unwrap(),
            )))
        );

        // FIXME: add these tests back, need a custom Lexer for comments...
        // assert_eq!(
        //     resolv_conf::parse_config_line(&mut errors, "nameserver 127.0.0.1; a comment")
        //         .expect("failed"),
        //     Some(ConfigOption::Basic(BasicOption::Nameserver(
        //         IpAddr::from_str("127.0.0.1").unwrap(),
        //     )))
        // );

        // assert_eq!(
        //     resolv_conf::parse_config_line(&mut errors, "nameserver 127.0.0.1# a comment")
        //         .expect("failed"),
        //     Some(ConfigOption::Basic(BasicOption::Nameserver(
        //         IpAddr::from_str("127.0.0.1").unwrap(),
        //     )))
        // );

        // assert_eq!(
        //     resolv_conf::parse_config_line(&mut errors, "nameserver 127.0.0.1 #a comment")
        //         .expect("failed"),
        //     Some(ConfigOption::Basic(BasicOption::Nameserver(
        //         IpAddr::from_str("127.0.0.1").unwrap(),
        //     )))
        // );

        // assert_eq!(
        //     resolv_conf::parse_config_line(&mut errors, "nameserver 127.0.0.1 # a comment")
        //         .expect("failed"),
        //     Some(ConfigOption::Basic(BasicOption::Nameserver(
        //         IpAddr::from_str("127.0.0.1").unwrap(),
        //     )))
        // );

        // assert_eq!(
        //     resolv_conf::parse_config_line(&mut errors, "options ndots:8 # a comment")
        //         .expect("failed"),
        //     Some(ConfigOption::Advanced(
        //         vec![AdvancedOption::NumberOfDots(8)],
        //     ))
        // );

        // only comment
        assert_eq!(
            resolv_conf::parse_config_line(&mut errors, "# a comment").expect("failed"),
            None
        );
    }

    #[test]
    fn test_advanced_option() {
        let mut errors = Vec::new();
        assert_eq!(
            resolv_conf::parse_advanced_option(&mut errors, "ndots:8").expect("failed"),
            AdvancedOption::NumberOfDots(8)
        );

        assert_eq!(
            resolv_conf::parse_advanced_option(&mut errors, "timeout:8").expect("failed"),
            AdvancedOption::Timeout(Duration::from_secs(8))
        );

        assert_eq!(
            resolv_conf::parse_advanced_option(&mut errors, "attempts:8").expect("failed"),
            AdvancedOption::Attempts(8)
        );
    }

    #[test]
    fn test_advanced_options() {
        let mut errors = Vec::new();
        assert_eq!(
            resolv_conf::parse_advanced_options(&mut errors, "options ndots:8 timeout:8 attempts:8")
                .expect("failed"),
            vec![
                AdvancedOption::NumberOfDots(8),
                AdvancedOption::Timeout(Duration::from_secs(8)),
                AdvancedOption::Attempts(8),
            ]
        );
    }

    #[test]
    fn test_resolv_conf_macos() {
        let mut data = String::new();
        let mut file = File::open(format!("{}/resolv.conf-macos", tests_dir())).unwrap();
        file.read_to_string(&mut data).unwrap();

        let configuration = vec![
            ConfigOption::Advanced(vec![
                AdvancedOption::NumberOfDots(8),
                AdvancedOption::Timeout(Duration::from_secs(8)),
                AdvancedOption::Attempts(8),
            ]),
            ConfigOption::Basic(BasicOption::Domain(
                Name::from_labels(vec!["example", "com"]),
            )),
            ConfigOption::Basic(BasicOption::Search(vec![
                Name::from_labels(vec!["example", "com"]),
                Name::from_labels(vec!["sub", "example", "com"]),
            ])),
            ConfigOption::Basic(BasicOption::Nameserver(
                IpAddr::from_str("2001:4860:4860::8888").unwrap(),
            )),
            ConfigOption::Basic(BasicOption::Nameserver(
                IpAddr::from_str("2001:4860:4860::8844").unwrap(),
            )),
            ConfigOption::Basic(BasicOption::Nameserver(
                IpAddr::from_str("8.8.8.8").unwrap(),
            )),
            ConfigOption::Basic(BasicOption::Nameserver(
                IpAddr::from_str("8.8.4.4").unwrap(),
            )),
        ];

        let mut errors = Vec::new();
        assert_eq!(
            resolv_conf::parse_config(&mut errors, &data).expect("failed"),
            configuration
        );
    }

    #[test]
    fn test_resolv_conf_linux() {
        let mut data = String::new();
        let mut file = File::open(format!("{}/resolv.conf-linux", tests_dir())).unwrap();
        file.read_to_string(&mut data).unwrap();

        let configuration =
            vec![
                ConfigOption::Advanced(vec![
                    AdvancedOption::NumberOfDots(8),
                    AdvancedOption::Timeout(Duration::from_secs(8)),
                    AdvancedOption::Attempts(8),
                ]),
                ConfigOption::Basic(BasicOption::Domain(
                    Name::from_labels(vec!["example", "com"]),
                )),
                ConfigOption::Basic(BasicOption::Search(vec![
                    Name::from_labels(vec!["example", "com"]),
                    Name::from_labels(vec!["sub", "example", "com"]),
                ])),
                ConfigOption::Basic(BasicOption::Nameserver(
                    IpAddr::from_str("2001:4860:4860::8888").unwrap(),
                )),
                ConfigOption::Basic(BasicOption::Nameserver(
                    IpAddr::from_str("2001:4860:4860::8844").unwrap(),
                )),
                ConfigOption::Basic(BasicOption::Nameserver(
                    IpAddr::from_str("8.8.8.8").unwrap(),
                )),
                ConfigOption::Basic(BasicOption::Nameserver(
                    IpAddr::from_str("8.8.4.4").unwrap(),
                )),
                ConfigOption::Advanced(vec![AdvancedOption::Unknown("rotate", None)]),
                ConfigOption::Advanced(vec![
                    AdvancedOption::Unknown("inet6", None),
                    AdvancedOption::Unknown("no-tld-query", None),
                ]),
                ConfigOption::Basic(BasicOption::SortList(
                    "sortlist 130.155.160.0/255.255.240.0 130.155.0.0",
                )),
            ];

        let mut errors = Vec::new();
        assert_eq!(
            resolv_conf::parse_config(&mut errors, &data).expect("failed"),
            configuration
        );
    }

    #[test]
    fn test_read_resolv_conf() {
        read_resolv_conf(format!("{}/resolv.conf-simple", tests_dir())).expect("simple failed");
        read_resolv_conf(format!("{}/resolv.conf-macos", tests_dir())).expect("macos failed");
        read_resolv_conf(format!("{}/resolv.conf-linux", tests_dir())).expect("linux failed");
    }
}<|endoftext|>