<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Off-screen windows.
//!
//! This is used for off-screen rendering mode only; on-screen windows (the default embedding mode)
//! are managed by a platform toolkit (Glutin).

use eutil::Downcast;
use interfaces::CefBrowser;
use render_handler::CefRenderHandlerExtensions;
use types::{cef_cursor_handle_t, cef_rect_t};
use unicode::str::Utf16Encoder;

use compositing::compositor_task::{self, CompositorProxy, CompositorReceiver};
use compositing::windowing::{WindowEvent, WindowMethods};
use geom::scale_factor::ScaleFactor;
use geom::size::TypedSize2D;
use gleam::gl;
use layers::geometry::DevicePixel;
use layers::platform::surface::NativeGraphicsMetadata;
use libc::{c_char, c_void};
use msg::constellation_msg::{Key, KeyModifiers};
use msg::compositor_msg::{ReadyState, PaintState};
use msg::constellation_msg::LoadData;
use util::cursor::Cursor;
use util::geometry::ScreenPx;
use std::cell::RefCell;
use std::ffi::CString;
use std::rc::Rc;
use std::sync::mpsc::{Sender, channel};

/// The type of an off-screen window.
#[derive(Clone)]
pub struct Window {
    cef_browser: RefCell<Option<CefBrowser>>,
}

#[cfg(target_os="macos")]
fn load_gl() {
    const RTLD_DEFAULT: *mut c_void = (-2) as *mut c_void;

    extern {
        fn dlsym(handle: *mut c_void, symbol: *const c_char) -> *mut c_void;
    }

    gl::load_with(|s| {
        unsafe {
            let c_str = CString::from_slice(s.as_bytes());
            dlsym(RTLD_DEFAULT, c_str.as_ptr()) as *const c_void
        }
    });
}

#[cfg(target_os="linux")]
fn load_gl() {
    extern {
        fn glXGetProcAddress(symbol: *const c_char) -> *mut c_void;
    }

    gl::load_with(|s| {
        unsafe {
            let c_str = CString::from_slice(s.as_bytes());
            glXGetProcAddress(c_str.as_ptr()) as *const c_void
        }
    });
}

impl Window {
    /// Creates a new window.
    pub fn new() -> Rc<Window> {
        load_gl();

        Rc::new(Window {
            cef_browser: RefCell::new(None),
        })
    }

    /// Sets the current browser.
    pub fn set_browser(&self, browser: CefBrowser) {
        *self.cef_browser.borrow_mut() = Some(browser)
    }

    /// Currently unimplemented.
    pub fn wait_events(&self) -> WindowEvent {
        WindowEvent::Idle
    }

    /// Returns the Cocoa cursor for a CSS cursor. These match Firefox, except where Firefox
    /// bundles custom resources (which we don't yet do).
    #[cfg(target_os="macos")]
    fn cursor_handle_for_cursor(&self, cursor: Cursor) -> cef_cursor_handle_t {
        use cocoa::base::{class, msg_send, selector};

        let cocoa_name = match cursor {
            Cursor::NoCursor => return 0 as cef_cursor_handle_t,
            Cursor::ContextMenuCursor => "contextualMenuCursor",
            Cursor::GrabbingCursor => "closedHandCursor",
            Cursor::CrosshairCursor => "crosshairCursor",
            Cursor::CopyCursor => "dragCopyCursor",
            Cursor::AliasCursor => "dragLinkCursor",
            Cursor::TextCursor => "IBeamCursor",
            Cursor::GrabCursor | Cursor::AllScrollCursor => "openHandCursor",
            Cursor::NoDropCursor | Cursor::NotAllowedCursor => "operationNotAllowedCursor",
            Cursor::PointerCursor => "pointingHandCursor",
            Cursor::SResizeCursor => "resizeDownCursor",
            Cursor::WResizeCursor => "resizeLeftCursor",
            Cursor::EwResizeCursor | Cursor::ColResizeCursor => "resizeLeftRightCursor",
            Cursor::EResizeCursor => "resizeRightCursor",
            Cursor::NResizeCursor => "resizeUpCursor",
            Cursor::NsResizeCursor | Cursor::RowResizeCursor => "resizeUpDownCursor",
            Cursor::VerticalTextCursor => "IBeamCursorForVerticalLayout",
            _ => "arrowCursor",
        };
        unsafe {
            msg_send()(class("NSCursor"), selector(cocoa_name))
        }
    }

    #[cfg(not(target_os="macos"))]
    fn cursor_handle_for_cursor(&self, _: Cursor) -> cef_cursor_handle_t {
        0
    }
}

impl WindowMethods for Window {
    fn framebuffer_size(&self) -> TypedSize2D<DevicePixel,u32> {
        let browser = self.cef_browser.borrow();
        match *browser {
            None => TypedSize2D(400, 300),
            Some(ref browser) => {
                let mut rect = cef_rect_t::zero();
                browser.get_host()
                       .get_client()
                       .get_render_handler()
                       .get_backing_rect((*browser).clone(), &mut rect);
                TypedSize2D(rect.width as u32, rect.height as u32)
            }
        }
    }

    fn size(&self) -> TypedSize2D<ScreenPx,f32> {
        let browser = self.cef_browser.borrow();
        match *browser {
            None => TypedSize2D(400.0, 300.0),
            Some(ref browser) => {
                let mut rect = cef_rect_t::zero();
                browser.get_host()
                       .get_client()
                       .get_render_handler()
                       .get_view_rect((*browser).clone(), &mut rect);
                TypedSize2D(rect.width as f32, rect.height as f32)
            }
        }
    }

    fn present(&self) {
        let browser = self.cef_browser.borrow();
        match *browser {
            None => {}
            Some(ref browser) => {
                browser.get_host().get_client().get_render_handler().on_present(browser.clone());
            }
        }
    }

    fn set_ready_state(&self, ready_state: ReadyState) {
        let browser = self.cef_browser.borrow();
        let browser = match *browser {
            None => return,
            Some(ref browser) => browser,
        };
        let is_loading = match ready_state {
            ReadyState::Blank | ReadyState::FinishedLoading => 0,
            ReadyState::Loading | ReadyState::PerformingLayout => 1,
        };
        browser.get_host()
               .get_client()
               .get_load_handler()
               .on_loading_state_change(browser.clone(), is_loading, 1, 1);
    }

    fn set_paint_state(&self, _: PaintState) {
        // TODO(pcwalton)
    }

    fn hidpi_factor(&self) -> ScaleFactor<ScreenPx,DevicePixel,f32> {
        let browser = self.cef_browser.borrow();
        match *browser {
            None => ScaleFactor(1.0),
            Some(ref browser) => {
                let mut view_rect = cef_rect_t::zero();
                browser.get_host()
                       .get_client()
                       .get_render_handler()
                       .get_view_rect((*browser).clone(), &mut view_rect);
                let mut backing_rect = cef_rect_t::zero();
                browser.get_host()
                       .get_client()
                       .get_render_handler()
                       .get_backing_rect((*browser).clone(), &mut backing_rect);
                ScaleFactor(backing_rect.width as f32 / view_rect.width as f32)
            }
        }
    }

    #[cfg(target_os="macos")]
    fn native_metadata(&self) -> NativeGraphicsMetadata {
        use cgl::{CGLGetCurrentContext, CGLGetPixelFormat};

        // FIXME(pcwalton)
        unsafe {
            NativeGraphicsMetadata {
                pixel_format: CGLGetPixelFormat(CGLGetCurrentContext()),
            }
        }
    }

    #[cfg(target_os="linux")]
    fn native_metadata(&self) -> NativeGraphicsMetadata {
        extern {
            fn cef_get_xdisplay() -> *mut c_void;
        }

        unsafe {
            NativeGraphicsMetadata {
                display: cef_get_xdisplay()
            }
        }
    }

    fn create_compositor_channel(_: &Option<Rc<Window>>)
                                 -> (Box<CompositorProxy+Send>, Box<CompositorReceiver>) {
        let (sender, receiver) = channel();
        (box CefCompositorProxy {
             sender: sender,
         } as Box<CompositorProxy+Send>,
         box receiver as Box<CompositorReceiver>)
    }

    fn prepare_for_composite(&self) -> bool {
        let browser = self.cef_browser.borrow();
        match *browser {
            None => {}
            Some(ref browser) => {
                browser.get_host().get_client().get_render_handler().paint(browser.clone());
            }
        }
        true
    }

    fn load_end(&self) {
        // FIXME(pcwalton): The status code 200 is a lie.
        let browser = self.cef_browser.borrow();
        let browser = match *browser {
            None => return,
            Some(ref browser) => browser,
        };
        browser.get_host()
               .get_client()
               .get_load_handler()
               .on_load_end((*browser).clone(), browser.get_main_frame(), 200);
    }

    fn set_page_title(&self, string: Option<String>) {
        let browser = self.cef_browser.borrow();
        let browser = match *browser {
            None => return,
            Some(ref browser) => browser,
        };
        let frame = browser.get_main_frame();
        let frame = frame.downcast();
        let mut title_visitor = frame.title_visitor.borrow_mut();
        match &mut *title_visitor {
            &mut None => {}
            &mut Some(ref mut visitor) => {
                match string {
                    None => visitor.visit(&[]),
                    Some(string) => {
                        let utf16_chars: Vec<u16> = Utf16Encoder::new(string.chars()).collect();
                        visitor.visit(utf16_chars.as_slice())
                    }
                }
            }
        }
    }

    fn set_page_load_data(&self, load_data: LoadData) {
        let browser = self.cef_browser.borrow();
        let browser = match *browser {
            None => return,
            Some(ref browser) => browser,
        };
        let frame = browser.get_main_frame();
        let frame = frame.downcast();
        *frame.url.borrow_mut() = load_data.url.to_string()
    }

    fn handle_key(&self, _: Key, _: KeyModifiers) {
        // TODO(negge)
    }

    fn set_cursor(&self, cursor: Cursor) {
        let browser = self.cef_browser.borrow();
        match *browser {
            None => {}
            Some(ref browser) => {
                let cursor_handle = self.cursor_handle_for_cursor(cursor);
                browser.get_host()
                       .get_client()
                       .get_render_handler()
                       .on_cursor_change(browser.clone(), cursor_handle)
            }
        }
    }
}

struct CefCompositorProxy {
    sender: Sender<compositor_task::Msg>,
}

impl CompositorProxy for CefCompositorProxy {
    #[cfg(target_os="macos")]
    fn send(&mut self, msg: compositor_task::Msg) {
        use cocoa::appkit::{NSApp, NSApplication, NSApplicationDefined, NSAutoreleasePool};
        use cocoa::appkit::{NSEvent, NSEventModifierFlags, NSEventSubtype, NSPoint};
        use cocoa::base::nil;

        // Send a message and kick the OS event loop awake.
        self.sender.send(msg);

        unsafe {
            let pool = NSAutoreleasePool::new(nil);
            let event =
                NSEvent::otherEventWithType_location_modifierFlags_timestamp_windowNumber_context_subtype_data1_data2_(
                nil,
                NSApplicationDefined,
                NSPoint::new(0.0, 0.0),
                NSEventModifierFlags::empty(),
                0.0,
                0,
                0,
                NSEventSubtype::NSWindowExposedEventType,
                0,
                0);
            NSApp().postEvent_atStart_(event, 0);
            pool.drain();
        }
    }

    #[cfg(target_os="linux")]
    fn send(&mut self, msg: compositor_task::Msg) {
        // FIXME(pcwalton): Kick the GTK event loop awake?
        self.sender.send(msg).unwrap();
    }

    fn clone_compositor_proxy(&self) -> Box<CompositorProxy+Send> {
        box CefCompositorProxy {
            sender: self.sender.clone(),
        } as Box<CompositorProxy+Send>
    }
}

<commit_msg>Fix a build warning with CEF on Mac.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Off-screen windows.
//!
//! This is used for off-screen rendering mode only; on-screen windows (the default embedding mode)
//! are managed by a platform toolkit (Glutin).

use eutil::Downcast;
use interfaces::CefBrowser;
use render_handler::CefRenderHandlerExtensions;
use types::{cef_cursor_handle_t, cef_rect_t};
use unicode::str::Utf16Encoder;

use compositing::compositor_task::{self, CompositorProxy, CompositorReceiver};
use compositing::windowing::{WindowEvent, WindowMethods};
use geom::scale_factor::ScaleFactor;
use geom::size::TypedSize2D;
use gleam::gl;
use layers::geometry::DevicePixel;
use layers::platform::surface::NativeGraphicsMetadata;
use libc::{c_char, c_void};
use msg::constellation_msg::{Key, KeyModifiers};
use msg::compositor_msg::{ReadyState, PaintState};
use msg::constellation_msg::LoadData;
use util::cursor::Cursor;
use util::geometry::ScreenPx;
use std::cell::RefCell;
use std::ffi::CString;
use std::rc::Rc;
use std::sync::mpsc::{Sender, channel};

/// The type of an off-screen window.
#[derive(Clone)]
pub struct Window {
    cef_browser: RefCell<Option<CefBrowser>>,
}

#[cfg(target_os="macos")]
fn load_gl() {
    const RTLD_DEFAULT: *mut c_void = (-2) as *mut c_void;

    extern {
        fn dlsym(handle: *mut c_void, symbol: *const c_char) -> *mut c_void;
    }

    gl::load_with(|s| {
        unsafe {
            let c_str = CString::from_slice(s.as_bytes());
            dlsym(RTLD_DEFAULT, c_str.as_ptr()) as *const c_void
        }
    });
}

#[cfg(target_os="linux")]
fn load_gl() {
    extern {
        fn glXGetProcAddress(symbol: *const c_char) -> *mut c_void;
    }

    gl::load_with(|s| {
        unsafe {
            let c_str = CString::from_slice(s.as_bytes());
            glXGetProcAddress(c_str.as_ptr()) as *const c_void
        }
    });
}

impl Window {
    /// Creates a new window.
    pub fn new() -> Rc<Window> {
        load_gl();

        Rc::new(Window {
            cef_browser: RefCell::new(None),
        })
    }

    /// Sets the current browser.
    pub fn set_browser(&self, browser: CefBrowser) {
        *self.cef_browser.borrow_mut() = Some(browser)
    }

    /// Currently unimplemented.
    pub fn wait_events(&self) -> WindowEvent {
        WindowEvent::Idle
    }

    /// Returns the Cocoa cursor for a CSS cursor. These match Firefox, except where Firefox
    /// bundles custom resources (which we don't yet do).
    #[cfg(target_os="macos")]
    fn cursor_handle_for_cursor(&self, cursor: Cursor) -> cef_cursor_handle_t {
        use cocoa::base::{class, msg_send, selector};

        let cocoa_name = match cursor {
            Cursor::NoCursor => return 0 as cef_cursor_handle_t,
            Cursor::ContextMenuCursor => "contextualMenuCursor",
            Cursor::GrabbingCursor => "closedHandCursor",
            Cursor::CrosshairCursor => "crosshairCursor",
            Cursor::CopyCursor => "dragCopyCursor",
            Cursor::AliasCursor => "dragLinkCursor",
            Cursor::TextCursor => "IBeamCursor",
            Cursor::GrabCursor | Cursor::AllScrollCursor => "openHandCursor",
            Cursor::NoDropCursor | Cursor::NotAllowedCursor => "operationNotAllowedCursor",
            Cursor::PointerCursor => "pointingHandCursor",
            Cursor::SResizeCursor => "resizeDownCursor",
            Cursor::WResizeCursor => "resizeLeftCursor",
            Cursor::EwResizeCursor | Cursor::ColResizeCursor => "resizeLeftRightCursor",
            Cursor::EResizeCursor => "resizeRightCursor",
            Cursor::NResizeCursor => "resizeUpCursor",
            Cursor::NsResizeCursor | Cursor::RowResizeCursor => "resizeUpDownCursor",
            Cursor::VerticalTextCursor => "IBeamCursorForVerticalLayout",
            _ => "arrowCursor",
        };
        unsafe {
            msg_send()(class("NSCursor"), selector(cocoa_name))
        }
    }

    #[cfg(not(target_os="macos"))]
    fn cursor_handle_for_cursor(&self, _: Cursor) -> cef_cursor_handle_t {
        0
    }
}

impl WindowMethods for Window {
    fn framebuffer_size(&self) -> TypedSize2D<DevicePixel,u32> {
        let browser = self.cef_browser.borrow();
        match *browser {
            None => TypedSize2D(400, 300),
            Some(ref browser) => {
                let mut rect = cef_rect_t::zero();
                browser.get_host()
                       .get_client()
                       .get_render_handler()
                       .get_backing_rect((*browser).clone(), &mut rect);
                TypedSize2D(rect.width as u32, rect.height as u32)
            }
        }
    }

    fn size(&self) -> TypedSize2D<ScreenPx,f32> {
        let browser = self.cef_browser.borrow();
        match *browser {
            None => TypedSize2D(400.0, 300.0),
            Some(ref browser) => {
                let mut rect = cef_rect_t::zero();
                browser.get_host()
                       .get_client()
                       .get_render_handler()
                       .get_view_rect((*browser).clone(), &mut rect);
                TypedSize2D(rect.width as f32, rect.height as f32)
            }
        }
    }

    fn present(&self) {
        let browser = self.cef_browser.borrow();
        match *browser {
            None => {}
            Some(ref browser) => {
                browser.get_host().get_client().get_render_handler().on_present(browser.clone());
            }
        }
    }

    fn set_ready_state(&self, ready_state: ReadyState) {
        let browser = self.cef_browser.borrow();
        let browser = match *browser {
            None => return,
            Some(ref browser) => browser,
        };
        let is_loading = match ready_state {
            ReadyState::Blank | ReadyState::FinishedLoading => 0,
            ReadyState::Loading | ReadyState::PerformingLayout => 1,
        };
        browser.get_host()
               .get_client()
               .get_load_handler()
               .on_loading_state_change(browser.clone(), is_loading, 1, 1);
    }

    fn set_paint_state(&self, _: PaintState) {
        // TODO(pcwalton)
    }

    fn hidpi_factor(&self) -> ScaleFactor<ScreenPx,DevicePixel,f32> {
        let browser = self.cef_browser.borrow();
        match *browser {
            None => ScaleFactor(1.0),
            Some(ref browser) => {
                let mut view_rect = cef_rect_t::zero();
                browser.get_host()
                       .get_client()
                       .get_render_handler()
                       .get_view_rect((*browser).clone(), &mut view_rect);
                let mut backing_rect = cef_rect_t::zero();
                browser.get_host()
                       .get_client()
                       .get_render_handler()
                       .get_backing_rect((*browser).clone(), &mut backing_rect);
                ScaleFactor(backing_rect.width as f32 / view_rect.width as f32)
            }
        }
    }

    #[cfg(target_os="macos")]
    fn native_metadata(&self) -> NativeGraphicsMetadata {
        use cgl::{CGLGetCurrentContext, CGLGetPixelFormat};

        // FIXME(pcwalton)
        unsafe {
            NativeGraphicsMetadata {
                pixel_format: CGLGetPixelFormat(CGLGetCurrentContext()),
            }
        }
    }

    #[cfg(target_os="linux")]
    fn native_metadata(&self) -> NativeGraphicsMetadata {
        extern {
            fn cef_get_xdisplay() -> *mut c_void;
        }

        unsafe {
            NativeGraphicsMetadata {
                display: cef_get_xdisplay()
            }
        }
    }

    fn create_compositor_channel(_: &Option<Rc<Window>>)
                                 -> (Box<CompositorProxy+Send>, Box<CompositorReceiver>) {
        let (sender, receiver) = channel();
        (box CefCompositorProxy {
             sender: sender,
         } as Box<CompositorProxy+Send>,
         box receiver as Box<CompositorReceiver>)
    }

    fn prepare_for_composite(&self) -> bool {
        let browser = self.cef_browser.borrow();
        match *browser {
            None => {}
            Some(ref browser) => {
                browser.get_host().get_client().get_render_handler().paint(browser.clone());
            }
        }
        true
    }

    fn load_end(&self) {
        // FIXME(pcwalton): The status code 200 is a lie.
        let browser = self.cef_browser.borrow();
        let browser = match *browser {
            None => return,
            Some(ref browser) => browser,
        };
        browser.get_host()
               .get_client()
               .get_load_handler()
               .on_load_end((*browser).clone(), browser.get_main_frame(), 200);
    }

    fn set_page_title(&self, string: Option<String>) {
        let browser = self.cef_browser.borrow();
        let browser = match *browser {
            None => return,
            Some(ref browser) => browser,
        };
        let frame = browser.get_main_frame();
        let frame = frame.downcast();
        let mut title_visitor = frame.title_visitor.borrow_mut();
        match &mut *title_visitor {
            &mut None => {}
            &mut Some(ref mut visitor) => {
                match string {
                    None => visitor.visit(&[]),
                    Some(string) => {
                        let utf16_chars: Vec<u16> = Utf16Encoder::new(string.chars()).collect();
                        visitor.visit(utf16_chars.as_slice())
                    }
                }
            }
        }
    }

    fn set_page_load_data(&self, load_data: LoadData) {
        let browser = self.cef_browser.borrow();
        let browser = match *browser {
            None => return,
            Some(ref browser) => browser,
        };
        let frame = browser.get_main_frame();
        let frame = frame.downcast();
        *frame.url.borrow_mut() = load_data.url.to_string()
    }

    fn handle_key(&self, _: Key, _: KeyModifiers) {
        // TODO(negge)
    }

    fn set_cursor(&self, cursor: Cursor) {
        let browser = self.cef_browser.borrow();
        match *browser {
            None => {}
            Some(ref browser) => {
                let cursor_handle = self.cursor_handle_for_cursor(cursor);
                browser.get_host()
                       .get_client()
                       .get_render_handler()
                       .on_cursor_change(browser.clone(), cursor_handle)
            }
        }
    }
}

struct CefCompositorProxy {
    sender: Sender<compositor_task::Msg>,
}

impl CompositorProxy for CefCompositorProxy {
    #[cfg(target_os="macos")]
    fn send(&mut self, msg: compositor_task::Msg) {
        use cocoa::appkit::{NSApp, NSApplication, NSApplicationDefined, NSAutoreleasePool};
        use cocoa::appkit::{NSEvent, NSEventModifierFlags, NSEventSubtype, NSPoint};
        use cocoa::base::nil;

        // Send a message and kick the OS event loop awake.
        self.sender.send(msg).unwrap();

        unsafe {
            let pool = NSAutoreleasePool::new(nil);
            let event =
                NSEvent::otherEventWithType_location_modifierFlags_timestamp_windowNumber_context_subtype_data1_data2_(
                nil,
                NSApplicationDefined,
                NSPoint::new(0.0, 0.0),
                NSEventModifierFlags::empty(),
                0.0,
                0,
                0,
                NSEventSubtype::NSWindowExposedEventType,
                0,
                0);
            NSApp().postEvent_atStart_(event, 0);
            pool.drain();
        }
    }

    #[cfg(target_os="linux")]
    fn send(&mut self, msg: compositor_task::Msg) {
        // FIXME(pcwalton): Kick the GTK event loop awake?
        self.sender.send(msg).unwrap();
    }

    fn clone_compositor_proxy(&self) -> Box<CompositorProxy+Send> {
        box CefCompositorProxy {
            sender: self.sender.clone(),
        } as Box<CompositorProxy+Send>
    }
}

<|endoftext|>


<commit_before>use inflector::Inflector;
use lazy_static::lazy_static;
use linked_hash_map::LinkedHashMap;
use std::collections::HashSet;

use crate::options::{ImportStyle, Options, StringTransform};
use crate::shape::{self, Shape};
use crate::util::{kebab_case, lower_camel_case, snake_case, type_case};
use crate::OutputMode;

struct Ctxt {
    options: Options,
    type_names: HashSet<String>,
    imports: HashSet<String>,
}

pub type Ident = String;
pub type Code = String;

pub fn kotlin_types(name: &str, shape: &Shape, options: Options) -> (Ident, Option<Code>) {
    let mut ctxt = Ctxt {
        options,
        type_names: HashSet::new(),
        imports: HashSet::new(),
    };

    if !matches!(shape, Shape::Struct { .. }) {
        // reserve the requested name
        ctxt.type_names.insert(name.to_string());
    }

    let (ident, code) = type_from_shape(&mut ctxt, name, shape);
    let mut code = code.unwrap_or(String::new());

    if ident != name {
        code = format!("typealias {} = {};\n\n", name, ident) + &code;
    }

    if !ctxt.imports.is_empty() {
        let mut imports: Vec<_> = ctxt.imports.drain().collect();
        imports.sort();
        let mut import_code = String::new();
        for import in imports {
            import_code += "import ";
            import_code += &import;
            import_code += "\n";
        }
        import_code += "\n";
        code = import_code + &code;
    }

    (name.to_string(), Some(code))
}

fn type_from_shape(ctxt: &mut Ctxt, path: &str, shape: &Shape) -> (Ident, Option<Code>) {
    use crate::shape::Shape::*;
    match *shape {
        Null | Any | Bottom => ("Any".into(), None),
        Bool => ("Boolean".into(), None),
        StringT => ("String".into(), None),
        Integer => ("Long".into(), None),
        Floating => ("Double".into(), None),
        Tuple(ref shapes, _n) => {
            let folded = shape::fold_shapes(shapes.clone());
            if shapes.len() <= 3 && folded == Any && shapes.iter().any(|s| s != &Any) {
                generate_tuple_type(ctxt, path, shapes)
            } else {
                generate_vec_type(ctxt, path, &folded)
            }
        }
        VecT { elem_type: ref e } => generate_vec_type(ctxt, path, e),
        Struct { fields: ref map } => generate_data_class(ctxt, path, map),
        MapT { val_type: ref v } => generate_map_type(ctxt, path, v),
        Opaque(ref t) => (t.clone(), None),
        Optional(ref e) => {
            let (inner, defs) = type_from_shape(ctxt, path, e);
            if ctxt.options.use_default_for_missing_fields {
                (inner, defs)
            } else {
                (format!("{}?", inner), defs)
            }
        }
    }
}

fn generate_vec_type(ctxt: &mut Ctxt, path: &str, shape: &Shape) -> (Ident, Option<Code>) {
    let singular = path.to_singular();
    let (inner, defs) = type_from_shape(ctxt, &singular, shape);
    (format!("List<{}>", inner), defs)
}

fn generate_map_type(ctxt: &mut Ctxt, path: &str, shape: &Shape) -> (Ident, Option<Code>) {
    let singular = path.to_singular();
    let (inner, defs) = type_from_shape(ctxt, &singular, shape);
    (format!("Map<String, {}>", inner), defs)
}

fn generate_tuple_type(ctxt: &mut Ctxt, path: &str, shapes: &[Shape]) -> (Ident, Option<Code>) {
    let mut types = Vec::new();
    let mut defs = Vec::new();

    for shape in shapes {
        let (typ, def) = type_from_shape(ctxt, path, shape);
        types.push(typ);
        if let Some(code) = def {
            defs.push(code)
        }
    }

    (
        format!(
            "{}<{}>",
            match types.len() {
                2 => "Pair",
                3 => "Triple",
                _ => panic!("No n-tuple type exists for n of {}", types.len()),
            },
            types.join(", ")
        ),
        Some(defs.join("\n\n")),
    )
}

fn field_name(name: &str, used_names: &HashSet<String>) -> Ident {
    type_or_field_name(name, used_names, "field", lower_camel_case)
}

fn type_name(name: &str, used_names: &HashSet<String>) -> Ident {
    type_or_field_name(name, used_names, "GeneratedType", type_case)
}

// https://kotlinlang.org/docs/reference/keyword-reference.html
// Only hard keywords are restricted. Others don't cause any problems I know of.
#[rustfmt::skip]
const KOTLIN_KEYWORDS_ARR: &[&str] = &[
    "as", "break", "class", "continue", "do", "else", "false", "for", "fun", "if", "in",
    "interface", "is", "null", "object", "package", "return", "super", "this", "throw", "true",
    "try", "typealias", "val", "var", "when", "while",
];

lazy_static! {
    static ref KOTLIN_KEYWORDS: HashSet<&'static str> =
        KOTLIN_KEYWORDS_ARR.iter().cloned().collect();
}

fn type_or_field_name(
    name: &str,
    used_names: &HashSet<String>,
    default_name: &str,
    case_fn: fn(&str) -> String,
) -> Ident {
    let name = name.trim();
    let mut output_name = case_fn(name);
    if KOTLIN_KEYWORDS.contains::<str>(&output_name) {
        output_name.push_str("_field");
    }
    if output_name == "" {
        output_name.push_str(default_name);
    }
    if let Some(c) = output_name.chars().next() {
        if c.is_ascii() && c.is_numeric() {
            output_name = String::from("n") + &output_name;
        }
    }
    if !used_names.contains(&output_name) {
        return output_name;
    }
    for n in 2.. {
        let temp = format!("{}{}", output_name, n);
        if !used_names.contains(&temp) {
            return temp;
        }
    }
    unreachable!()
}

fn import(ctxt: &mut Ctxt, qualified: &str) -> String {
    match ctxt.options.import_style {
        ImportStyle::AddImports => {
            ctxt.imports.insert(qualified.into());
            qualified.rsplit('.').next().unwrap().into()
        }
        ImportStyle::AssumeExisting => qualified.rsplit('.').next().unwrap().into(),
        ImportStyle::QualifiedPaths => qualified.into(),
    }
}

fn generate_data_class(
    ctxt: &mut Ctxt,
    path: &str,
    map: &LinkedHashMap<String, Shape>,
) -> (Ident, Option<Code>) {
    if map.is_empty() {
        // Kotlin does not allow empty data classes, so use type for general unknown object
        // Once #30 is implemented: && !options.collect_unknown_properties
        return ("Map<String, Any>".into(), None);
    }

    let type_name = type_name(path, &ctxt.type_names);
    ctxt.type_names.insert(type_name.clone());

    let mut field_names = HashSet::new();
    let mut defs = Vec::new();

    let fields: Vec<Code> = map
        .iter()
        .map(|(name, typ)| {
            let field_name = field_name(name, &field_names);
            field_names.insert(field_name.clone());

            let mut field_code = String::new();
            if &apply_transform(ctxt, &field_name) != name {
                if ctxt.options.output_mode == OutputMode::KotlinJackson {
                    field_code += &format!(
                        "    @{}(\"{}\")\n",
                        import(ctxt, "com.fasterxml.jackson.annotation.JsonProperty"),
                        name
                    )
                } else if ctxt.options.output_mode == OutputMode::KotlinKotlinx {
                    field_code += &format!(
                        "    @{}(\"{}\")\n",
                        import(ctxt, "kotlinx.serialization.SerialName"),
                        name
                    )
                }
            }

            let (field_type, child_defs) = type_from_shape(ctxt, name, typ);

            if let Some(code) = child_defs {
                defs.push(code);
            }

            format!("{}    val {}: {}", field_code, field_name, field_type)
        })
        .collect();

    let mut code = String::new();

    code += &transform_annotation(ctxt);

    if ctxt.options.output_mode == OutputMode::KotlinKotlinx {
        code += &format!("@{}\n", import(ctxt, "kotlinx.serialization.Serializable"));
    }
    code += &format!("data class {}(\n", type_name);

    if !fields.is_empty() {
        code += &fields.join(",\n");
        code += "\n";
    }
    code += ")";

    if !defs.is_empty() {
        code += "\n\n";
        code += &defs.join("\n\n");
    }

    (type_name, Some(code))
}

fn apply_transform(ctxt: &Ctxt, field_name: &str) -> String {
    match (
        &ctxt.options.property_name_format,
        &ctxt.options.output_mode,
    ) {
        (Some(StringTransform::LowerCase), OutputMode::KotlinJackson) => {
            field_name.to_ascii_lowercase()
        }
        (Some(StringTransform::PascalCase), OutputMode::KotlinJackson) => type_case(field_name),
        (Some(StringTransform::SnakeCase), OutputMode::KotlinJackson) => snake_case(field_name),
        (Some(StringTransform::KebabCase), OutputMode::KotlinJackson) => kebab_case(field_name),
        _ => field_name.to_string(),
    }
}

fn jackson_naming_annotation(ctxt: &mut Ctxt, strategy: &str) -> String {
    format!(
        "@{}({}.{}::class)\n",
        import(ctxt, "com.fasterxml.jackson.databind.annotation.JsonNaming"),
        import(
            ctxt,
            "com.fasterxml.jackson.databind.PropertyNamingStrategies"
        ),
        strategy
    )
}

fn transform_annotation(ctxt: &mut Ctxt) -> String {
    match (
        &ctxt.options.property_name_format,
        &ctxt.options.output_mode,
    ) {
        (Some(StringTransform::LowerCase), OutputMode::KotlinJackson) => {
            jackson_naming_annotation(ctxt, "LowerCaseStrategy")
        }
        (Some(StringTransform::PascalCase), OutputMode::KotlinJackson) => {
            jackson_naming_annotation(ctxt, "UpperCamelCaseStrategy")
        }
        (Some(StringTransform::SnakeCase), OutputMode::KotlinJackson) => {
            jackson_naming_annotation(ctxt, "SnakeCaseStrategy")
        }
        (Some(StringTransform::KebabCase), OutputMode::KotlinJackson) => {
            jackson_naming_annotation(ctxt, "KebabCaseStrategy::class)")
        }
        _ => "".into(),
    }
}

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

    #[test]
    fn field_names_test() {
        fn field_name_test(from: &str, to: &str) {
            assert_eq!(
                field_name(from, &HashSet::new()),
                to.to_string(),
                r#"From "{}" to "{}""#,
                from,
                to
            );
        }

        field_name_test("valid", "valid");
        field_name_test("1", "n1");
        field_name_test("+1", "n1");
        field_name_test("", "field");
        field_name_test("object", "object_field");
    }
}
<commit_msg>shared: Kotlin Any does not include null, output Any?<commit_after>use inflector::Inflector;
use lazy_static::lazy_static;
use linked_hash_map::LinkedHashMap;
use std::collections::HashSet;

use crate::options::{ImportStyle, Options, StringTransform};
use crate::shape::{self, Shape};
use crate::util::{kebab_case, lower_camel_case, snake_case, type_case};
use crate::OutputMode;

struct Ctxt {
    options: Options,
    type_names: HashSet<String>,
    imports: HashSet<String>,
}

pub type Ident = String;
pub type Code = String;

pub fn kotlin_types(name: &str, shape: &Shape, options: Options) -> (Ident, Option<Code>) {
    let mut ctxt = Ctxt {
        options,
        type_names: HashSet::new(),
        imports: HashSet::new(),
    };

    if !matches!(shape, Shape::Struct { .. }) {
        // reserve the requested name
        ctxt.type_names.insert(name.to_string());
    }

    let (ident, code) = type_from_shape(&mut ctxt, name, shape);
    let mut code = code.unwrap_or(String::new());

    if ident != name {
        code = format!("typealias {} = {};\n\n", name, ident) + &code;
    }

    if !ctxt.imports.is_empty() {
        let mut imports: Vec<_> = ctxt.imports.drain().collect();
        imports.sort();
        let mut import_code = String::new();
        for import in imports {
            import_code += "import ";
            import_code += &import;
            import_code += "\n";
        }
        import_code += "\n";
        code = import_code + &code;
    }

    (name.to_string(), Some(code))
}

fn type_from_shape(ctxt: &mut Ctxt, path: &str, shape: &Shape) -> (Ident, Option<Code>) {
    use crate::shape::Shape::*;
    match *shape {
        Null | Any | Bottom => ("Any?".into(), None),
        Bool => ("Boolean".into(), None),
        StringT => ("String".into(), None),
        Integer => ("Long".into(), None),
        Floating => ("Double".into(), None),
        Tuple(ref shapes, _n) => {
            let folded = shape::fold_shapes(shapes.clone());
            if shapes.len() <= 3 && folded == Any && shapes.iter().any(|s| s != &Any) {
                generate_tuple_type(ctxt, path, shapes)
            } else {
                generate_vec_type(ctxt, path, &folded)
            }
        }
        VecT { elem_type: ref e } => generate_vec_type(ctxt, path, e),
        Struct { fields: ref map } => generate_data_class(ctxt, path, map),
        MapT { val_type: ref v } => generate_map_type(ctxt, path, v),
        Opaque(ref t) => (t.clone(), None),
        Optional(ref e) => {
            let (inner, defs) = type_from_shape(ctxt, path, e);
            if ctxt.options.use_default_for_missing_fields {
                (inner, defs)
            } else {
                (format!("{}?", inner), defs)
            }
        }
    }
}

fn generate_vec_type(ctxt: &mut Ctxt, path: &str, shape: &Shape) -> (Ident, Option<Code>) {
    let singular = path.to_singular();
    let (inner, defs) = type_from_shape(ctxt, &singular, shape);
    (format!("List<{}>", inner), defs)
}

fn generate_map_type(ctxt: &mut Ctxt, path: &str, shape: &Shape) -> (Ident, Option<Code>) {
    let singular = path.to_singular();
    let (inner, defs) = type_from_shape(ctxt, &singular, shape);
    (format!("Map<String, {}>", inner), defs)
}

fn generate_tuple_type(ctxt: &mut Ctxt, path: &str, shapes: &[Shape]) -> (Ident, Option<Code>) {
    let mut types = Vec::new();
    let mut defs = Vec::new();

    for shape in shapes {
        let (typ, def) = type_from_shape(ctxt, path, shape);
        types.push(typ);
        if let Some(code) = def {
            defs.push(code)
        }
    }

    (
        format!(
            "{}<{}>",
            match types.len() {
                2 => "Pair",
                3 => "Triple",
                _ => panic!("No n-tuple type exists for n of {}", types.len()),
            },
            types.join(", ")
        ),
        Some(defs.join("\n\n")),
    )
}

fn field_name(name: &str, used_names: &HashSet<String>) -> Ident {
    type_or_field_name(name, used_names, "field", lower_camel_case)
}

fn type_name(name: &str, used_names: &HashSet<String>) -> Ident {
    type_or_field_name(name, used_names, "GeneratedType", type_case)
}

// https://kotlinlang.org/docs/reference/keyword-reference.html
// Only hard keywords are restricted. Others don't cause any problems I know of.
#[rustfmt::skip]
const KOTLIN_KEYWORDS_ARR: &[&str] = &[
    "as", "break", "class", "continue", "do", "else", "false", "for", "fun", "if", "in",
    "interface", "is", "null", "object", "package", "return", "super", "this", "throw", "true",
    "try", "typealias", "val", "var", "when", "while",
];

lazy_static! {
    static ref KOTLIN_KEYWORDS: HashSet<&'static str> =
        KOTLIN_KEYWORDS_ARR.iter().cloned().collect();
}

fn type_or_field_name(
    name: &str,
    used_names: &HashSet<String>,
    default_name: &str,
    case_fn: fn(&str) -> String,
) -> Ident {
    let name = name.trim();
    let mut output_name = case_fn(name);
    if KOTLIN_KEYWORDS.contains::<str>(&output_name) {
        output_name.push_str("_field");
    }
    if output_name == "" {
        output_name.push_str(default_name);
    }
    if let Some(c) = output_name.chars().next() {
        if c.is_ascii() && c.is_numeric() {
            output_name = String::from("n") + &output_name;
        }
    }
    if !used_names.contains(&output_name) {
        return output_name;
    }
    for n in 2.. {
        let temp = format!("{}{}", output_name, n);
        if !used_names.contains(&temp) {
            return temp;
        }
    }
    unreachable!()
}

fn import(ctxt: &mut Ctxt, qualified: &str) -> String {
    match ctxt.options.import_style {
        ImportStyle::AddImports => {
            ctxt.imports.insert(qualified.into());
            qualified.rsplit('.').next().unwrap().into()
        }
        ImportStyle::AssumeExisting => qualified.rsplit('.').next().unwrap().into(),
        ImportStyle::QualifiedPaths => qualified.into(),
    }
}

fn generate_data_class(
    ctxt: &mut Ctxt,
    path: &str,
    map: &LinkedHashMap<String, Shape>,
) -> (Ident, Option<Code>) {
    if map.is_empty() {
        // Kotlin does not allow empty data classes, so use type for general unknown object
        // Once #30 is implemented: && !options.collect_unknown_properties
        return ("Map<String, Any>".into(), None);
    }

    let type_name = type_name(path, &ctxt.type_names);
    ctxt.type_names.insert(type_name.clone());

    let mut field_names = HashSet::new();
    let mut defs = Vec::new();

    let fields: Vec<Code> = map
        .iter()
        .map(|(name, typ)| {
            let field_name = field_name(name, &field_names);
            field_names.insert(field_name.clone());

            let mut field_code = String::new();
            if &apply_transform(ctxt, &field_name) != name {
                if ctxt.options.output_mode == OutputMode::KotlinJackson {
                    field_code += &format!(
                        "    @{}(\"{}\")\n",
                        import(ctxt, "com.fasterxml.jackson.annotation.JsonProperty"),
                        name
                    )
                } else if ctxt.options.output_mode == OutputMode::KotlinKotlinx {
                    field_code += &format!(
                        "    @{}(\"{}\")\n",
                        import(ctxt, "kotlinx.serialization.SerialName"),
                        name
                    )
                }
            }

            let (field_type, child_defs) = type_from_shape(ctxt, name, typ);

            if let Some(code) = child_defs {
                defs.push(code);
            }

            format!("{}    val {}: {}", field_code, field_name, field_type)
        })
        .collect();

    let mut code = String::new();

    code += &transform_annotation(ctxt);

    if ctxt.options.output_mode == OutputMode::KotlinKotlinx {
        code += &format!("@{}\n", import(ctxt, "kotlinx.serialization.Serializable"));
    }
    code += &format!("data class {}(\n", type_name);

    if !fields.is_empty() {
        code += &fields.join(",\n");
        code += "\n";
    }
    code += ")";

    if !defs.is_empty() {
        code += "\n\n";
        code += &defs.join("\n\n");
    }

    (type_name, Some(code))
}

fn apply_transform(ctxt: &Ctxt, field_name: &str) -> String {
    match (
        &ctxt.options.property_name_format,
        &ctxt.options.output_mode,
    ) {
        (Some(StringTransform::LowerCase), OutputMode::KotlinJackson) => {
            field_name.to_ascii_lowercase()
        }
        (Some(StringTransform::PascalCase), OutputMode::KotlinJackson) => type_case(field_name),
        (Some(StringTransform::SnakeCase), OutputMode::KotlinJackson) => snake_case(field_name),
        (Some(StringTransform::KebabCase), OutputMode::KotlinJackson) => kebab_case(field_name),
        _ => field_name.to_string(),
    }
}

fn jackson_naming_annotation(ctxt: &mut Ctxt, strategy: &str) -> String {
    format!(
        "@{}({}.{}::class)\n",
        import(ctxt, "com.fasterxml.jackson.databind.annotation.JsonNaming"),
        import(
            ctxt,
            "com.fasterxml.jackson.databind.PropertyNamingStrategies"
        ),
        strategy
    )
}

fn transform_annotation(ctxt: &mut Ctxt) -> String {
    match (
        &ctxt.options.property_name_format,
        &ctxt.options.output_mode,
    ) {
        (Some(StringTransform::LowerCase), OutputMode::KotlinJackson) => {
            jackson_naming_annotation(ctxt, "LowerCaseStrategy")
        }
        (Some(StringTransform::PascalCase), OutputMode::KotlinJackson) => {
            jackson_naming_annotation(ctxt, "UpperCamelCaseStrategy")
        }
        (Some(StringTransform::SnakeCase), OutputMode::KotlinJackson) => {
            jackson_naming_annotation(ctxt, "SnakeCaseStrategy")
        }
        (Some(StringTransform::KebabCase), OutputMode::KotlinJackson) => {
            jackson_naming_annotation(ctxt, "KebabCaseStrategy::class)")
        }
        _ => "".into(),
    }
}

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

    #[test]
    fn field_names_test() {
        fn field_name_test(from: &str, to: &str) {
            assert_eq!(
                field_name(from, &HashSet::new()),
                to.to_string(),
                r#"From "{}" to "{}""#,
                from,
                to
            );
        }

        field_name_test("valid", "valid");
        field_name_test("1", "n1");
        field_name_test("+1", "n1");
        field_name_test("", "field");
        field_name_test("object", "object_field");
    }
}
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::panic::catch_unwind;
use std::path::PathBuf;

use mnt::get_submounts;
use nix::mount::{MntFlags, umount2};

use devicemapper::{DevId, DmFlags, DmResult};

use super::super::dm::get_dm;

mod cleanup_errors {
    use mnt;
    use nix;

    error_chain!{
        foreign_links {
            Mnt(mnt::ParseError);
            Nix(nix::Error);
        }
    }
}

use self::cleanup_errors::{Error, Result};

/// Attempt to remove all device mapper devices which match the stratis naming convention.
/// FIXME: Current implementation complicated by https://bugzilla.redhat.com/show_bug.cgi?id=1506287
fn dm_stratis_devices_remove() -> Result<()> {

    /// One iteration of removing devicemapper devices
    fn one_iteration() -> DmResult<(bool, Vec<String>)> {
        let mut progress_made = false;
        let mut remain = Vec::new();

        for d in get_dm()
                .list_devices()?
                .iter()
                .filter(|d| format!("{}", d.0.as_ref()).starts_with("stratis-1")) {

            match get_dm().device_remove(&DevId::Name(&d.0), DmFlags::empty()) {
                Ok(_) => progress_made = true,
                Err(_) => remain.push(format!("{}", d.0.as_ref())),
            }
        }
        Ok((progress_made, remain))
    }

    if catch_unwind(get_dm).is_err() {
        return Err("Unable to initialize DM".into());
    }

    loop {
        let (progress_made, remain) = one_iteration()
            .map_err(|e| {
                Error::with_chain(e,
                                  "Error while attempting to remove stratis device mapper devices")}
                )?;

        if !progress_made {
            if remain.len() != 0 {
                bail!("We were unable to remove all stratis device mapper devices {:?}",
                      remain);
            }
            break;
        }
    }

    Ok(())
}

/// Try and un-mount any filesystems that have the name stratis in the mount point, returning
/// immediately on the first one we are unable to unmount.
fn stratis_filesystems_unmount() -> Result<()> {
    || -> Result<()> {
        let mounts = get_submounts(&PathBuf::from("/"))?;
        for m in mounts
                .iter()
                .filter(|m| m.file.to_str().map_or(false, |s| s.contains("stratis"))) {
            umount2(&m.file, MntFlags::MNT_DETACH)?;
        }
        Ok(())
    }()
            .map_err(|e| Error::with_chain(e, "unable to unmount all stratis filesystems"))
}

/// When a unit test panics we can leave the system in an inconsistent state.  This function
/// tries to clean up by un-mounting any mounted file systems which contain the string
/// "stratis_testing" and then it tries to remove any device mapper tables which are also stratis
/// created.
pub fn clean_up() -> Result<()> {
    stratis_filesystems_unmount()?;
    dm_stratis_devices_remove()?;
    Ok(())
}
<commit_msg>Adjust cleanup code in various ways<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::panic::catch_unwind;
use std::path::PathBuf;

use mnt::get_submounts;
use nix::mount::{MntFlags, umount2};

use devicemapper::{DevId, DmFlags};

use super::super::dm::get_dm;

mod cleanup_errors {
    use mnt;
    use nix;

    error_chain!{
        foreign_links {
            Mnt(mnt::ParseError);
            Nix(nix::Error);
        }
    }
}

use self::cleanup_errors::{Error, Result};

/// Attempt to remove all device mapper devices which match the stratis naming convention.
/// FIXME: Current implementation complicated by https://bugzilla.redhat.com/show_bug.cgi?id=1506287
fn dm_stratis_devices_remove() -> Result<()> {

    /// One iteration of removing devicemapper devices
    fn one_iteration() -> Result<(bool, Vec<String>)> {
        let mut progress_made = false;
        let mut remain = Vec::new();

        for n in get_dm()
                .list_devices()
                .map_err(|e| {
                             let err_msg = "failed while listing DM devices, giving up";
                             Error::with_chain(e, err_msg)
                         })?
                .iter()
                .map(|d| &d.0)
                .filter(|n| n.to_string().starts_with("stratis-1")) {
            match get_dm().device_remove(&DevId::Name(n), DmFlags::empty()) {
                Ok(_) => progress_made = true,
                Err(_) => remain.push(n.to_string()),
            }
        }
        Ok((progress_made, remain))
    }

    /// Do one iteration of removals until progress stops. Return remaining
    /// dm devices.
    fn do_while_progress() -> Result<Vec<String>> {
        let mut result = one_iteration()?;
        while result.0 {
            result = one_iteration()?;
        }
        Ok(result.1)
    }

    || -> Result<()> {
        if catch_unwind(get_dm).is_err() {
            return Err("Unable to initialize DM".into());
        }

        do_while_progress().and_then(|remain| if !remain.is_empty() {
                                         Err(format!("Some Stratis DM devices remaining: {:?}",
                                                     remain)
                                                     .into())
                                     } else {
                                         Ok(())
                                     })
    }()
            .map_err(|e| e.chain_err(|| "Failed to ensure removal of all Stratis DM devices"))
}

/// Try and un-mount any filesystems that have the name stratis in the mount point, returning
/// immediately on the first one we are unable to unmount.
fn stratis_filesystems_unmount() -> Result<()> {
    || -> Result<()> {
        let mounts = get_submounts(&PathBuf::from("/"))?;
        for m in mounts
                .iter()
                .filter(|m| m.file.to_str().map_or(false, |s| s.contains("stratis"))) {
            umount2(&m.file, MntFlags::MNT_DETACH)?;
        }
        Ok(())
    }()
            .map_err(|e| e.chain_err(|| "Failed to ensure all Stratis filesystems were unmounted"))
}

/// When a unit test panics we can leave the system in an inconsistent state.  This function
/// tries to clean up by un-mounting any mounted file systems which contain the string
/// "stratis_testing" and then it tries to remove any device mapper tables which are also stratis
/// created.
pub fn clean_up() -> Result<()> {
    stratis_filesystems_unmount()?;
    dm_stratis_devices_remove()?;
    Ok(())
}
<|endoftext|>


<commit_before>use super::plain_summary_line;
use super::{ErrorCodes, IdMap, Ignore, LangString, Markdown, MarkdownHtml};
use rustc_span::edition::{Edition, DEFAULT_EDITION};
use std::cell::RefCell;

#[test]
fn test_unique_id() {
    let input = [
        "foo",
        "examples",
        "examples",
        "method.into_iter",
        "examples",
        "method.into_iter",
        "foo",
        "main",
        "search",
        "methods",
        "examples",
        "method.into_iter",
        "assoc_type.Item",
        "assoc_type.Item",
    ];
    let expected = [
        "foo",
        "examples",
        "examples-1",
        "method.into_iter",
        "examples-2",
        "method.into_iter-1",
        "foo-1",
        "main",
        "search",
        "methods",
        "examples-3",
        "method.into_iter-2",
        "assoc_type.Item",
        "assoc_type.Item-1",
    ];

    let map = RefCell::new(IdMap::new());
    let test = || {
        let mut map = map.borrow_mut();
        let actual: Vec<String> = input.iter().map(|s| map.derive(s.to_string())).collect();
        assert_eq!(&actual[..], expected);
    };
    test();
    map.borrow_mut().reset();
    test();
}

#[test]
fn test_lang_string_parse() {
    fn t(
        s: &str,
        should_panic: bool,
        no_run: bool,
        ignore: Ignore,
        rust: bool,
        test_harness: bool,
        compile_fail: bool,
        allow_fail: bool,
        error_codes: Vec<String>,
        edition: Option<Edition>,
    ) {
        assert_eq!(
            LangString::parse(s, ErrorCodes::Yes, true),
            LangString {
                should_panic,
                no_run,
                ignore,
                rust,
                test_harness,
                compile_fail,
                error_codes,
                original: s.to_owned(),
                allow_fail,
                edition,
            }
        )
    }
    let ignore_foo = Ignore::Some(vec!["foo".to_string()]);

    fn v() -> Vec<String> {
        Vec::new()
    }

    // marker                | should_panic | no_run | ignore | rust | test_harness
    //                       | compile_fail | allow_fail | error_codes | edition
    t("", false, false, Ignore::None, true, false, false, false, v(), None);
    t("rust", false, false, Ignore::None, true, false, false, false, v(), None);
    t("sh", false, false, Ignore::None, false, false, false, false, v(), None);
    t("ignore", false, false, Ignore::All, true, false, false, false, v(), None);
    t("ignore-foo", false, false, ignore_foo, true, false, false, false, v(), None);
    t("should_panic", true, false, Ignore::None, true, false, false, false, v(), None);
    t("no_run", false, true, Ignore::None, true, false, false, false, v(), None);
    t("test_harness", false, false, Ignore::None, true, true, false, false, v(), None);
    t("compile_fail", false, true, Ignore::None, true, false, true, false, v(), None);
    t("allow_fail", false, false, Ignore::None, true, false, false, true, v(), None);
    t("{.no_run .example}", false, true, Ignore::None, true, false, false, false, v(), None);
    t("{.sh .should_panic}", true, false, Ignore::None, false, false, false, false, v(), None);
    t("{.example .rust}", false, false, Ignore::None, true, false, false, false, v(), None);
    t("{.test_harness .rust}", false, false, Ignore::None, true, true, false, false, v(), None);
    t("text, no_run", false, true, Ignore::None, false, false, false, false, v(), None);
    t("text,no_run", false, true, Ignore::None, false, false, false, false, v(), None);
    t(
        "edition2015",
        false,
        false,
        Ignore::None,
        true,
        false,
        false,
        false,
        v(),
        Some(Edition::Edition2015),
    );
    t(
        "edition2018",
        false,
        false,
        Ignore::None,
        true,
        false,
        false,
        false,
        v(),
        Some(Edition::Edition2018),
    );
}

#[test]
fn test_header() {
    fn t(input: &str, expect: &str) {
        let mut map = IdMap::new();
        let output =
            Markdown(input, &[], &mut map, ErrorCodes::Yes, DEFAULT_EDITION, &None).to_string();
        assert_eq!(output, expect, "original: {}", input);
    }

    t(
        "# Foo bar",
        "
\
      Foo bar
",
    );
    t(
        "## Foo-bar_baz qux",
        "
Foo-bar_baz qux
",
    );
    t(
        "### **Foo** *bar* baz!?!& -_qux_-%",
        "
\
      Foo \
      bar baz!?!& -qux-%
",
    );
    t(
        "#### **Foo?** & \\*bar?!*  _`baz`_ ❤ #qux",
        "
\
      Foo? & *bar?!*  \
      baz ❤ #qux
",
    );
}

#[test]
fn test_header_ids_multiple_blocks() {
    let mut map = IdMap::new();
    fn t(map: &mut IdMap, input: &str, expect: &str) {
        let output = Markdown(input, &[], map, ErrorCodes::Yes, DEFAULT_EDITION, &None).to_string();
        assert_eq!(output, expect, "original: {}", input);
    }

    t(
        &mut map,
        "# Example",
        "
\
        Example
",
    );
    t(
        &mut map,
        "# Panics",
        "
\
        Panics
",
    );
    t(
        &mut map,
        "# Example",
        "
\
        Example
",
    );
    t(
        &mut map,
        "# Main",
        "
\
        Main
",
    );
    t(
        &mut map,
        "# Example",
        "
\
        Example
",
    );
    t(
        &mut map,
        "# Panics",
        "
\
        Panics
",
    );
}

#[test]
fn test_plain_summary_line() {
    fn t(input: &str, expect: &str) {
        let output = plain_summary_line(input);
        assert_eq!(output, expect, "original: {}", input);
    }

    t("hello [Rust](https://www.rust-lang.org) :)", "hello Rust :)");
    t("hello [Rust](https://www.rust-lang.org \"Rust\") :)", "hello Rust :)");
    t("code `let x = i32;` ...", "code `let x = i32;` ...");
    t("type `Type<'static>` ...", "type `Type<'static>` ...");
    t("# top header", "top header");
    t("## header", "header");
}

#[test]
fn test_markdown_html_escape() {
    fn t(input: &str, expect: &str) {
        let mut idmap = IdMap::new();
        let output =
            MarkdownHtml(input, &mut idmap, ErrorCodes::Yes, DEFAULT_EDITION, &None).to_string();
        assert_eq!(output, expect, "original: {}", input);
    }

    t("`Struct<'a, T>`", "
Struct<'a, T>
\n");
    t("Struct<'a, T>", "
Struct<'a, T>
\n");
    t("Struct
", "
Struct<br>
\n");
}
<commit_msg>Update LangString::parse usage in markdown tests<commit_after>use super::plain_summary_line;
use super::{ErrorCodes, IdMap, Ignore, LangString, Markdown, MarkdownHtml};
use rustc_span::edition::{Edition, DEFAULT_EDITION};
use std::cell::RefCell;

#[test]
fn test_unique_id() {
    let input = [
        "foo",
        "examples",
        "examples",
        "method.into_iter",
        "examples",
        "method.into_iter",
        "foo",
        "main",
        "search",
        "methods",
        "examples",
        "method.into_iter",
        "assoc_type.Item",
        "assoc_type.Item",
    ];
    let expected = [
        "foo",
        "examples",
        "examples-1",
        "method.into_iter",
        "examples-2",
        "method.into_iter-1",
        "foo-1",
        "main",
        "search",
        "methods",
        "examples-3",
        "method.into_iter-2",
        "assoc_type.Item",
        "assoc_type.Item-1",
    ];

    let map = RefCell::new(IdMap::new());
    let test = || {
        let mut map = map.borrow_mut();
        let actual: Vec<String> = input.iter().map(|s| map.derive(s.to_string())).collect();
        assert_eq!(&actual[..], expected);
    };
    test();
    map.borrow_mut().reset();
    test();
}

#[test]
fn test_lang_string_parse() {
    fn t(
        s: &str,
        should_panic: bool,
        no_run: bool,
        ignore: Ignore,
        rust: bool,
        test_harness: bool,
        compile_fail: bool,
        allow_fail: bool,
        error_codes: Vec<String>,
        edition: Option<Edition>,
    ) {
        assert_eq!(
            LangString::parse(s, ErrorCodes::Yes, true, None),
            LangString {
                should_panic,
                no_run,
                ignore,
                rust,
                test_harness,
                compile_fail,
                error_codes,
                original: s.to_owned(),
                allow_fail,
                edition,
            }
        )
    }
    let ignore_foo = Ignore::Some(vec!["foo".to_string()]);

    fn v() -> Vec<String> {
        Vec::new()
    }

    // marker                | should_panic | no_run | ignore | rust | test_harness
    //                       | compile_fail | allow_fail | error_codes | edition
    t("", false, false, Ignore::None, true, false, false, false, v(), None);
    t("rust", false, false, Ignore::None, true, false, false, false, v(), None);
    t("sh", false, false, Ignore::None, false, false, false, false, v(), None);
    t("ignore", false, false, Ignore::All, true, false, false, false, v(), None);
    t("ignore-foo", false, false, ignore_foo, true, false, false, false, v(), None);
    t("should_panic", true, false, Ignore::None, true, false, false, false, v(), None);
    t("no_run", false, true, Ignore::None, true, false, false, false, v(), None);
    t("test_harness", false, false, Ignore::None, true, true, false, false, v(), None);
    t("compile_fail", false, true, Ignore::None, true, false, true, false, v(), None);
    t("allow_fail", false, false, Ignore::None, true, false, false, true, v(), None);
    t("{.no_run .example}", false, true, Ignore::None, true, false, false, false, v(), None);
    t("{.sh .should_panic}", true, false, Ignore::None, false, false, false, false, v(), None);
    t("{.example .rust}", false, false, Ignore::None, true, false, false, false, v(), None);
    t("{.test_harness .rust}", false, false, Ignore::None, true, true, false, false, v(), None);
    t("text, no_run", false, true, Ignore::None, false, false, false, false, v(), None);
    t("text,no_run", false, true, Ignore::None, false, false, false, false, v(), None);
    t(
        "edition2015",
        false,
        false,
        Ignore::None,
        true,
        false,
        false,
        false,
        v(),
        Some(Edition::Edition2015),
    );
    t(
        "edition2018",
        false,
        false,
        Ignore::None,
        true,
        false,
        false,
        false,
        v(),
        Some(Edition::Edition2018),
    );
}

#[test]
fn test_header() {
    fn t(input: &str, expect: &str) {
        let mut map = IdMap::new();
        let output =
            Markdown(input, &[], &mut map, ErrorCodes::Yes, DEFAULT_EDITION, &None).to_string();
        assert_eq!(output, expect, "original: {}", input);
    }

    t(
        "# Foo bar",
        "
\
      Foo bar
",
    );
    t(
        "## Foo-bar_baz qux",
        "
Foo-bar_baz qux
",
    );
    t(
        "### **Foo** *bar* baz!?!& -_qux_-%",
        "
\
      Foo \
      bar baz!?!& -qux-%
",
    );
    t(
        "#### **Foo?** & \\*bar?!*  _`baz`_ ❤ #qux",
        "
\
      Foo? & *bar?!*  \
      baz ❤ #qux
",
    );
}

#[test]
fn test_header_ids_multiple_blocks() {
    let mut map = IdMap::new();
    fn t(map: &mut IdMap, input: &str, expect: &str) {
        let output = Markdown(input, &[], map, ErrorCodes::Yes, DEFAULT_EDITION, &None).to_string();
        assert_eq!(output, expect, "original: {}", input);
    }

    t(
        &mut map,
        "# Example",
        "
\
        Example
",
    );
    t(
        &mut map,
        "# Panics",
        "
\
        Panics
",
    );
    t(
        &mut map,
        "# Example",
        "
\
        Example
",
    );
    t(
        &mut map,
        "# Main",
        "
\
        Main
",
    );
    t(
        &mut map,
        "# Example",
        "
\
        Example
",
    );
    t(
        &mut map,
        "# Panics",
        "
\
        Panics
",
    );
}

#[test]
fn test_plain_summary_line() {
    fn t(input: &str, expect: &str) {
        let output = plain_summary_line(input);
        assert_eq!(output, expect, "original: {}", input);
    }

    t("hello [Rust](https://www.rust-lang.org) :)", "hello Rust :)");
    t("hello [Rust](https://www.rust-lang.org \"Rust\") :)", "hello Rust :)");
    t("code `let x = i32;` ...", "code `let x = i32;` ...");
    t("type `Type<'static>` ...", "type `Type<'static>` ...");
    t("# top header", "top header");
    t("## header", "header");
}

#[test]
fn test_markdown_html_escape() {
    fn t(input: &str, expect: &str) {
        let mut idmap = IdMap::new();
        let output =
            MarkdownHtml(input, &mut idmap, ErrorCodes::Yes, DEFAULT_EDITION, &None).to_string();
        assert_eq!(output, expect, "original: {}", input);
    }

    t("`Struct<'a, T>`", "
Struct<'a, T>
\n");
    t("Struct<'a, T>", "
Struct<'a, T>
\n");
    t("Struct
", "
Struct<br>
\n");
}
<|endoftext|>


<commit_before>use std::env;
use std::fs;
use std::io::{self, prelude::*};
use std::process;

enum Op {
    Dec,
    Inc,
    Prev,
    Next,
    Loop(Box<[Op]>),
    Print,
}
use Op::*;

struct Tape {
    pos: usize,
    tape: Vec<i32>,
}

impl Tape {
    fn new() -> Self {
        Default::default()
    }

    fn get(&self) -> i32 {
        // Always safe
        unsafe { *self.tape.get_unchecked(self.pos) }
    }

    fn dec(&mut self) {
        // Always safe
        unsafe {
            *self.tape.get_unchecked_mut(self.pos) -= 1;
        }
    }

    fn inc(&mut self) {
        // Always safe
        unsafe {
            *self.tape.get_unchecked_mut(self.pos) += 1;
        }
    }

    fn prev(&mut self) {
        self.pos -= 1;
    }

    fn next(&mut self) {
        self.pos += 1;
        if self.pos >= self.tape.len() {
            self.tape.resize(self.pos << 1, 0);
        }
    }
}

impl Default for Tape {
    fn default() -> Self {
        Self {
            pos: 0,
            tape: vec![0],
        }
    }
}

#[derive(Default)]
struct Printer {
    sum1: i32,
    sum2: i32,
    quiet: bool,
}

impl Printer {
    fn new(quiet: bool) -> Self {
        Self {
            quiet,
            ..Default::default()
        }
    }

    fn print(&mut self, n: i32) {
        if self.quiet {
            self.sum1 = (self.sum1 + n) % 255;
            self.sum2 = (self.sum2 + self.sum1) % 255;
        } else {
            let mut stdout = io::stdout();
            stdout.lock().write_all(&[n as u8]).ok();
            stdout.flush().ok();
        }
    }

    fn get_checksum(&self) -> i32 {
        (self.sum2 << 8) | self.sum1
    }
}

fn run(program: &[Op], tape: &mut Tape, p: &mut Printer) {
    for op in program {
        match *op {
            Dec => tape.dec(),
            Inc => tape.inc(),
            Prev => tape.prev(),
            Next => tape.next(),
            Loop(ref program) => {
                while tape.get() > 0 {
                    run(program, tape, p);
                }
            }
            Print => {
                p.print(tape.get());
            }
        }
    }
}

fn parse<I: Iterator<Item = char>>(it: &mut I) -> Box<[Op]> {
    let mut buf = vec![];
    while let Some(c) = it.next() {
        buf.push(match c {
            '-' => Dec,
            '+' => Inc,
            '<' => Prev,
            '>' => Next,
            '.' => Print,
            '[' => Loop(parse(it)),
            ']' => break,
            _ => continue,
        });
    }
    buf.into_boxed_slice()
}

struct Program {
    ops: Box<[Op]>,
}

impl Program {
    fn new(code: &str) -> Self {
        Self {
            ops: parse(&mut code.chars()),
        }
    }

    fn run(&self, p: &mut Printer) {
        let mut tape = Tape::new();
        run(&self.ops, &mut tape, p);
    }
}

fn notify(msg: &str) {
    if let Ok(mut stream) = std::net::TcpStream::connect("localhost:9001") {
        stream.write_all(msg.as_bytes()).ok();
    }
}

fn verify() {
    let s = "++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>
             ---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.";
    let mut p_left = Printer::new(true);
    Program::new(s).run(&mut p_left);
    let left = p_left.get_checksum();

    let mut p_right = Printer::new(true);
    for c in "Hello World!\n".chars() {
        p_right.print(c as i32);
    }
    let right = p_right.get_checksum();
    if left != right {
        eprintln!("{} != {}", left, right);
        process::exit(-1);
    }
}

fn main() {
    verify();

    let arg1 = env::args().nth(1).unwrap();
    let s = fs::read_to_string(arg1).unwrap();
    let mut p = Printer::new(env::var("QUIET").is_ok());

    notify(&format!("Rust\t{}", process::id()));
    Program::new(&s).run(&mut p);
    notify("stop");

    if p.quiet {
        println!("Output checksum: {}", p.get_checksum());
    }
}

<commit_msg>Lock the output only once per printer (#328)<commit_after>use std::env;
use std::fs;
use std::io::{self, prelude::*};
use std::process;

enum Op {
    Dec,
    Inc,
    Prev,
    Next,
    Loop(Box<[Op]>),
    Print,
}
use Op::*;

struct Tape {
    pos: usize,
    tape: Vec<i32>,
}

impl Tape {
    fn new() -> Self {
        Default::default()
    }

    fn get(&self) -> i32 {
        // Safe: `self.pos` is already checked in `self.next()`
        unsafe { *self.tape.get_unchecked(self.pos) }
    }

    fn dec(&mut self) {
        // Safe: `self.pos` is already checked in `self.next()`
        unsafe {
            *self.tape.get_unchecked_mut(self.pos) -= 1;
        }
    }

    fn inc(&mut self) {
        // Safe: `self.pos` is already checked in `self.next()`
        unsafe {
            *self.tape.get_unchecked_mut(self.pos) += 1;
        }
    }

    fn prev(&mut self) {
        self.pos -= 1;
    }

    fn next(&mut self) {
        self.pos += 1;
        if self.pos >= self.tape.len() {
            self.tape.resize(self.pos << 1, 0);
        }
    }
}

impl Default for Tape {
    fn default() -> Self {
        Self {
            pos: 0,
            tape: vec![0],
        }
    }
}

struct Printer<'a> {
    output: io::StdoutLock<'a>,
    sum1: i32,
    sum2: i32,
    quiet: bool,
}

impl<'a> Printer<'a> {
    fn new(output: &'a io::Stdout, quiet: bool) -> Self {
        Self {
            output: output.lock(),
            sum1: 0,
            sum2: 0,
            quiet,
        }
    }

    fn print(&mut self, n: i32) {
        if self.quiet {
            self.sum1 = (self.sum1 + n) % 255;
            self.sum2 = (self.sum2 + self.sum1) % 255;
        } else {
            self.output.write_all(&[n as u8]).ok();
            self.output.flush().ok();
        }
    }

    fn get_checksum(&self) -> i32 {
        (self.sum2 << 8) | self.sum1
    }
}

fn run(program: &[Op], tape: &mut Tape, p: &mut Printer) {
    for op in program {
        match op {
            Dec => tape.dec(),
            Inc => tape.inc(),
            Prev => tape.prev(),
            Next => tape.next(),
            Loop(program) => {
                while tape.get() > 0 {
                    run(program, tape, p);
                }
            }
            Print => {
                p.print(tape.get());
            }
        }
    }
}

fn parse<I: Iterator<Item = char>>(it: &mut I) -> Box<[Op]> {
    let mut buf = vec![];
    while let Some(c) = it.next() {
        buf.push(match c {
            '-' => Dec,
            '+' => Inc,
            '<' => Prev,
            '>' => Next,
            '.' => Print,
            '[' => Loop(parse(it)),
            ']' => break,
            _ => continue,
        });
    }
    buf.into_boxed_slice()
}

struct Program {
    ops: Box<[Op]>,
}

impl Program {
    fn new(code: &str) -> Self {
        Self {
            ops: parse(&mut code.chars()),
        }
    }

    fn run(&self, p: &mut Printer) {
        let mut tape = Tape::new();
        run(&self.ops, &mut tape, p);
    }
}

fn notify(msg: &str) {
    if let Ok(mut stream) = std::net::TcpStream::connect("localhost:9001") {
        stream.write_all(msg.as_bytes()).ok();
    }
}

fn verify() {
    let s = "++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>
             ---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.";

    let left = {
        let output = io::stdout();
        let mut p_left = Printer::new(&output, true);
        Program::new(s).run(&mut p_left);
        p_left.get_checksum()
    };

    let right = {
        let output = io::stdout();
        let mut p_right = Printer::new(&output, true);
        for c in "Hello World!\n".chars() {
            p_right.print(c as i32);
        }
        p_right.get_checksum()
    };

    if left != right {
        eprintln!("{} != {}", left, right);
        process::exit(-1);
    }
}

fn main() {
    verify();

    let s = {
        let arg1 = env::args().nth(1).unwrap();
        fs::read_to_string(arg1).unwrap()
    };
    let output = io::stdout();
    let mut p = Printer::new(&output, env::var("QUIET").is_ok());

    notify(&format!("Rust\t{}", process::id()));
    Program::new(&s).run(&mut p);
    notify("stop");

    if p.quiet {
        println!("Output checksum: {}", p.get_checksum());
    }
}
<|endoftext|>


<commit_before>//! Conversions between coordinate systems
//!
//! # Coordinate Systems
//!
//! logical or "world" coordinates (cartesian coordinates)
//!   * origin is in the center of the framebuffer and can be offset by drawing `center`
//!   * x is left to right
//!   * y is bottom to top
//!
//! screen coordinates
//!   * origin is the top-left corner of the framebuffer
//!   * x is left to right
//!   * y is top to bottom

use glutin::dpi::PhysicalPosition;
use pathfinder_geometry::vector::{Vector2F, vec2f};

use crate::Point;

/// A point in the screen corodinate system
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct ScreenPoint {
    pub x: f64,
    pub y: f64,
}

/// A `PhysicalPosition<f64>` is considered to be in screen coordinates in glutin
impl From<PhysicalPosition<f64>> for ScreenPoint {
    fn from(pos: PhysicalPosition<f64>) -> Self {
        Self {x: pos.x, y: pos.y}
    }
}

/// A `PhysicalPosition<f64>` is considered to be in screen coordinates in glutin
impl From<ScreenPoint> for PhysicalPosition<f64> {
    fn from(pt: ScreenPoint) -> Self {
        let ScreenPoint {x, y} = pt;
        PhysicalPosition {x, y}
    }
}

/// A `Vector2F` is considered to be in screen coordinates as this is the type used in the renderer
impl From<Vector2F> for ScreenPoint {
    fn from(vec: Vector2F) -> Self {
        Self {
            x: vec.x() as f64,
            y: vec.y() as f64,
        }
    }
}

/// A `Vector2F` is considered to be in screen coordinates as this is the type used in the renderer
impl From<ScreenPoint> for Vector2F {
    fn from(pt: ScreenPoint) -> Self {
        let ScreenPoint {x, y} = pt;
        vec2f(x as f32, y as f32)
    }
}

impl ScreenPoint {
    /// Converts a `Point` in logical (or "world") coordinates to a `ScreenPoint` in
    /// screen coordinates
    ///
    /// # Parameters
    ///
    /// * `point` is the `Point` to convert to screen coordinates
    /// * `dpi_scale` is the high DPI scale factor (>= 0.0)
    /// * `center` is the `Point` configured in the drawing that all other `Point`s are relative to.
    /// * `fb_center` is the center of the framebuffer in screen coordinates.
    pub fn from_logical(
        point: Point,
        dpi_scale: f64,
        center: Point,
        fb_center: ScreenPoint,
    ) -> Self {
        let Point {x, y} = point;
        let Point {x: center_x, y: center_y} = center;
        let ScreenPoint {x: fb_center_x, y: fb_center_y} = fb_center;

        Self {
            x: ((x - center_x) * dpi_scale) + fb_center_x,
            y: -((y - center_y) * dpi_scale) + fb_center_y,
        }
    }

    /// Converts a `ScreenPoint` in screen coordinates to a `Point` in logical (or "world")
    /// coordinates
    ///
    /// # Parameters
    ///
    /// * `point` is the `Point` to convert to screen coordinates
    /// * `dpi_scale` is the high DPI scale factor (>= 0.0)
    /// * `center` is the `Point` configured in the drawing that all other `Point`s are relative to.
    /// * `fb_center` is the center of the framebuffer in screen coordinates.
    pub fn to_logical(self, dpi_scale: f64, center: Point, fb_center: ScreenPoint) -> Point {
        let Self {x, y} = self;
        let Point {x: center_x, y: center_y} = center;
        let ScreenPoint {x: fb_center_x, y: fb_center_y} = fb_center;

        Point {
            x: (x - fb_center_x) / dpi_scale + center_x,
            y: -(y - fb_center_y) / dpi_scale + center_y,
        }
    }
}

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

    #[test]
    fn screen_coords() {
        // The origin is always at fb_center as long as center is also the origin
        let screen_coord = ScreenPoint::from_logical(Point::origin(), 1.0, Point::origin(), ScreenPoint {x: 200.0, y: 300.0});
        assert_eq!(screen_coord, ScreenPoint {x: 200.0, y: 300.0});
        // The origin is always at fb_center regardless of DPI scale
        let screen_coord = ScreenPoint::from_logical(Point::origin(), 2.0, Point::origin(), ScreenPoint {x: 200.0, y: 300.0});
        assert_eq!(screen_coord, ScreenPoint {x: 200.0, y: 300.0});

        // The x-axis and y-axis treated distinctly and interpreted as cartesian
        let screen_coord = ScreenPoint::from_logical(Point {x: 10.0, y: 20.0}, 1.0, Point::origin(), ScreenPoint {x: 200.0, y: 300.0});
        assert_eq!(screen_coord, ScreenPoint {x: 210.0, y: 280.0});
        // A different fb_center gives a different final coordinate
        let screen_coord = ScreenPoint::from_logical(Point {x: 10.0, y: 20.0}, 1.0, Point::origin(), ScreenPoint {x: 300.0, y: 400.0});
        assert_eq!(screen_coord, ScreenPoint {x: 310.0, y: 380.0});

        // The center is interpreted as cartesian and points are relative to it
        let screen_coord = ScreenPoint::from_logical(Point {x: 10.0, y: 20.0}, 1.0, Point {x: 30.0, y: 5.0}, ScreenPoint {x: 200.0, y: 300.0});
        assert_eq!(screen_coord, ScreenPoint {x: 180.0, y: 285.0});

        // Negative points work too
        let screen_coord = ScreenPoint::from_logical(Point {x: -10.0, y: -20.0}, 1.0, Point {x: 30.0, y: -5.0}, ScreenPoint {x: 200.0, y: 300.0});
        assert_eq!(screen_coord, ScreenPoint {x: 160.0, y: 315.0});

        // DPI scale > 1.0 causes logical coordinates to scale, but NOT screen coordinates
        let screen_coord = ScreenPoint::from_logical(Point {x: 10.0, y: 20.0}, 2.0, Point {x: 30.0, y: 5.0}, ScreenPoint {x: 200.0, y: 300.0});
        assert_eq!(screen_coord, ScreenPoint {x: 160.0, y: 270.0});
    }

    #[test]
    fn logical_coords() {
        // The origin is always at fb_center as long as center is also the origin
        let logical_coord = ScreenPoint {x: 200.0, y: 300.0}.to_logical(1.0, Point::origin(), ScreenPoint {x: 200.0, y: 300.0});
        assert_eq!(logical_coord, Point::origin());
        // The origin is always at fb_center regardless of DPI scale
        let logical_coord = ScreenPoint {x: 200.0, y: 300.0}.to_logical(2.0, Point::origin(), ScreenPoint {x: 200.0, y: 300.0});
        assert_eq!(logical_coord, Point::origin());

        // The x-axis and y-axis treated distinctly and interpreted as cartesian
        let logical_coord = ScreenPoint {x: 210.0, y: 280.0}.to_logical(1.0, Point::origin(), ScreenPoint {x: 200.0, y: 300.0});
        assert_eq!(logical_coord, Point {x: 10.0, y: 20.0});
        // A different fb_center gives a different final coordinate
        let logical_coord = ScreenPoint {x: 310.0, y: 380.0}.to_logical(1.0, Point::origin(), ScreenPoint {x: 300.0, y: 400.0});
        assert_eq!(logical_coord, Point {x: 10.0, y: 20.0});

        // The center is interpreted as cartesian and points are relative to it
        let logical_coord = ScreenPoint {x: 180.0, y: 285.0}.to_logical(1.0, Point {x: 30.0, y: 5.0}, ScreenPoint {x: 200.0, y: 300.0});
        assert_eq!(logical_coord, Point {x: 10.0, y: 20.0});

        // Negative points work too
        let logical_coord = ScreenPoint {x: 160.0, y: 315.0}.to_logical(1.0, Point {x: 30.0, y: -5.0}, ScreenPoint {x: 200.0, y: 300.0});
        assert_eq!(logical_coord, Point {x: -10.0, y: -20.0});

        // DPI scale > 1.0 causes logical coordinates to scale, but NOT screen coordinates
        let logical_coord = ScreenPoint {x: 160.0, y: 270.0}.to_logical(2.0, Point {x: 30.0, y: 5.0}, ScreenPoint {x: 200.0, y: 300.0});
        assert_eq!(logical_coord, Point {x: 10.0, y: 20.0});
    }
}
<commit_msg>Clarifying negative and positive directions in coords<commit_after>//! Conversions between coordinate systems
//!
//! # Coordinate Systems
//!
//! logical or "world" coordinates (cartesian coordinates)
//!   * origin is in the center of the framebuffer and can be offset by drawing `center`
//!   * x is left (-) to right (+)
//!   * y is bottom (-) to top (+)
//!
//! screen coordinates
//!   * origin is the top-left corner of the framebuffer
//!   * x is left (-) to right (+)
//!   * y is top (-) to bottom (+)

use glutin::dpi::PhysicalPosition;
use pathfinder_geometry::vector::{Vector2F, vec2f};

use crate::Point;

/// A point in the screen corodinate system
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct ScreenPoint {
    pub x: f64,
    pub y: f64,
}

/// A `PhysicalPosition<f64>` is considered to be in screen coordinates in glutin
impl From<PhysicalPosition<f64>> for ScreenPoint {
    fn from(pos: PhysicalPosition<f64>) -> Self {
        Self {x: pos.x, y: pos.y}
    }
}

/// A `PhysicalPosition<f64>` is considered to be in screen coordinates in glutin
impl From<ScreenPoint> for PhysicalPosition<f64> {
    fn from(pt: ScreenPoint) -> Self {
        let ScreenPoint {x, y} = pt;
        PhysicalPosition {x, y}
    }
}

/// A `Vector2F` is considered to be in screen coordinates as this is the type used in the renderer
impl From<Vector2F> for ScreenPoint {
    fn from(vec: Vector2F) -> Self {
        Self {
            x: vec.x() as f64,
            y: vec.y() as f64,
        }
    }
}

/// A `Vector2F` is considered to be in screen coordinates as this is the type used in the renderer
impl From<ScreenPoint> for Vector2F {
    fn from(pt: ScreenPoint) -> Self {
        let ScreenPoint {x, y} = pt;
        vec2f(x as f32, y as f32)
    }
}

impl ScreenPoint {
    /// Converts a `Point` in logical (or "world") coordinates to a `ScreenPoint` in
    /// screen coordinates
    ///
    /// # Parameters
    ///
    /// * `point` is the `Point` to convert to screen coordinates
    /// * `dpi_scale` is the high DPI scale factor (>= 0.0)
    /// * `center` is the `Point` configured in the drawing that all other `Point`s are relative to.
    /// * `fb_center` is the center of the framebuffer in screen coordinates.
    pub fn from_logical(
        point: Point,
        dpi_scale: f64,
        center: Point,
        fb_center: ScreenPoint,
    ) -> Self {
        let Point {x, y} = point;
        let Point {x: center_x, y: center_y} = center;
        let ScreenPoint {x: fb_center_x, y: fb_center_y} = fb_center;

        Self {
            x: ((x - center_x) * dpi_scale) + fb_center_x,
            y: -((y - center_y) * dpi_scale) + fb_center_y,
        }
    }

    /// Converts a `ScreenPoint` in screen coordinates to a `Point` in logical (or "world")
    /// coordinates
    ///
    /// # Parameters
    ///
    /// * `point` is the `Point` to convert to screen coordinates
    /// * `dpi_scale` is the high DPI scale factor (>= 0.0)
    /// * `center` is the `Point` configured in the drawing that all other `Point`s are relative to.
    /// * `fb_center` is the center of the framebuffer in screen coordinates.
    pub fn to_logical(self, dpi_scale: f64, center: Point, fb_center: ScreenPoint) -> Point {
        let Self {x, y} = self;
        let Point {x: center_x, y: center_y} = center;
        let ScreenPoint {x: fb_center_x, y: fb_center_y} = fb_center;

        Point {
            x: (x - fb_center_x) / dpi_scale + center_x,
            y: -(y - fb_center_y) / dpi_scale + center_y,
        }
    }
}

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

    #[test]
    fn screen_coords() {
        // The origin is always at fb_center as long as center is also the origin
        let screen_coord = ScreenPoint::from_logical(Point::origin(), 1.0, Point::origin(), ScreenPoint {x: 200.0, y: 300.0});
        assert_eq!(screen_coord, ScreenPoint {x: 200.0, y: 300.0});
        // The origin is always at fb_center regardless of DPI scale
        let screen_coord = ScreenPoint::from_logical(Point::origin(), 2.0, Point::origin(), ScreenPoint {x: 200.0, y: 300.0});
        assert_eq!(screen_coord, ScreenPoint {x: 200.0, y: 300.0});

        // The x-axis and y-axis treated distinctly and interpreted as cartesian
        let screen_coord = ScreenPoint::from_logical(Point {x: 10.0, y: 20.0}, 1.0, Point::origin(), ScreenPoint {x: 200.0, y: 300.0});
        assert_eq!(screen_coord, ScreenPoint {x: 210.0, y: 280.0});
        // A different fb_center gives a different final coordinate
        let screen_coord = ScreenPoint::from_logical(Point {x: 10.0, y: 20.0}, 1.0, Point::origin(), ScreenPoint {x: 300.0, y: 400.0});
        assert_eq!(screen_coord, ScreenPoint {x: 310.0, y: 380.0});

        // The center is interpreted as cartesian and points are relative to it
        let screen_coord = ScreenPoint::from_logical(Point {x: 10.0, y: 20.0}, 1.0, Point {x: 30.0, y: 5.0}, ScreenPoint {x: 200.0, y: 300.0});
        assert_eq!(screen_coord, ScreenPoint {x: 180.0, y: 285.0});

        // Negative points work too
        let screen_coord = ScreenPoint::from_logical(Point {x: -10.0, y: -20.0}, 1.0, Point {x: 30.0, y: -5.0}, ScreenPoint {x: 200.0, y: 300.0});
        assert_eq!(screen_coord, ScreenPoint {x: 160.0, y: 315.0});

        // DPI scale > 1.0 causes logical coordinates to scale, but NOT screen coordinates
        let screen_coord = ScreenPoint::from_logical(Point {x: 10.0, y: 20.0}, 2.0, Point {x: 30.0, y: 5.0}, ScreenPoint {x: 200.0, y: 300.0});
        assert_eq!(screen_coord, ScreenPoint {x: 160.0, y: 270.0});
    }

    #[test]
    fn logical_coords() {
        // The origin is always at fb_center as long as center is also the origin
        let logical_coord = ScreenPoint {x: 200.0, y: 300.0}.to_logical(1.0, Point::origin(), ScreenPoint {x: 200.0, y: 300.0});
        assert_eq!(logical_coord, Point::origin());
        // The origin is always at fb_center regardless of DPI scale
        let logical_coord = ScreenPoint {x: 200.0, y: 300.0}.to_logical(2.0, Point::origin(), ScreenPoint {x: 200.0, y: 300.0});
        assert_eq!(logical_coord, Point::origin());

        // The x-axis and y-axis treated distinctly and interpreted as cartesian
        let logical_coord = ScreenPoint {x: 210.0, y: 280.0}.to_logical(1.0, Point::origin(), ScreenPoint {x: 200.0, y: 300.0});
        assert_eq!(logical_coord, Point {x: 10.0, y: 20.0});
        // A different fb_center gives a different final coordinate
        let logical_coord = ScreenPoint {x: 310.0, y: 380.0}.to_logical(1.0, Point::origin(), ScreenPoint {x: 300.0, y: 400.0});
        assert_eq!(logical_coord, Point {x: 10.0, y: 20.0});

        // The center is interpreted as cartesian and points are relative to it
        let logical_coord = ScreenPoint {x: 180.0, y: 285.0}.to_logical(1.0, Point {x: 30.0, y: 5.0}, ScreenPoint {x: 200.0, y: 300.0});
        assert_eq!(logical_coord, Point {x: 10.0, y: 20.0});

        // Negative points work too
        let logical_coord = ScreenPoint {x: 160.0, y: 315.0}.to_logical(1.0, Point {x: 30.0, y: -5.0}, ScreenPoint {x: 200.0, y: 300.0});
        assert_eq!(logical_coord, Point {x: -10.0, y: -20.0});

        // DPI scale > 1.0 causes logical coordinates to scale, but NOT screen coordinates
        let logical_coord = ScreenPoint {x: 160.0, y: 270.0}.to_logical(2.0, Point {x: 30.0, y: 5.0}, ScreenPoint {x: 200.0, y: 300.0});
        assert_eq!(logical_coord, Point {x: 10.0, y: 20.0});
    }
}
<|endoftext|>


<commit_before>pub type boolean_t = ::c_int;

pub const CLOCK_UPTIME_RAW: ::clockid_t = 8;

s! {
    pub struct malloc_zone_t {
        __private: [::uintptr_t; 18], // FIXME: needs arm64 auth pointers support
    }
}

cfg_if! {
    if #[cfg(libc_align)] {
        mod align;
        pub use self::align::*;
    }
}
<commit_msg>Fix linter issue<commit_after>pub type boolean_t = ::c_int;

pub const CLOCK_UPTIME_RAW: ::clockid_t = 8;

s! {
    pub struct malloc_zone_t {
        __private: [::uintptr_t; 18], // FIXME: needs arm64 auth pointers support
    }
}

cfg_if! {
    if #[cfg(libc_align)] {
        mod align;
        pub use self::align::*;
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate askama;

use askama::Template;

#[derive(Template)]
#[template(path = "let.html")]
struct LetTemplate<'a> {
    s: &'a str,
}

#[test]
fn test_let() {
    let t = LetTemplate { s: "foo" };
    assert_eq!(t.render().unwrap(), "foo");
}

#[derive(Template)]
#[template(path = "let-decl.html")]
struct LetDeclTemplate<'a> {
    cond: bool,
    s: &'a str,
}

#[test]
fn test_let_decl() {
    let t = LetDeclTemplate { cond: false, s: "bar" };
    assert_eq!(t.render().unwrap(), "bar");
}
<commit_msg>Change one test case to use source attribute<commit_after>#[macro_use]
extern crate askama;

use askama::Template;

#[derive(Template)]
#[template(source = "{% let v = s %}{{ v }}")]
struct LetTemplate<'a> {
    s: &'a str,
}

#[test]
fn test_let() {
    let t = LetTemplate { s: "foo" };
    assert_eq!(t.render().unwrap(), "foo");
}

#[derive(Template)]
#[template(path = "let-decl.html")]
struct LetDeclTemplate<'a> {
    cond: bool,
    s: &'a str,
}

#[test]
fn test_let_decl() {
    let t = LetDeclTemplate { cond: false, s: "bar" };
    assert_eq!(t.render().unwrap(), "bar");
}
<|endoftext|>