<commit_before>#[link(name = "sdl_ttf",
       vers = "0.1",
       uuid = "1d776704-431f-4d5b-9361-b27958535321",
       url = "http://github.com/sfackler/rust-sdl_ttf")];

#[comment="SDL_ttf bindings"];
#[license="MIT"];
#[crate_type="lib"];

extern mod sdl;

use std::cast;
use std::libc::{c_int, c_long};
use std::str;

use sdl::video::{Color, Surface};

mod ffi {
    use std::libc::{c_int, c_char, c_void, c_long};
    use sdl::video::ll::{SDL_Color, SDL_Surface};

    pub type TTF_Font = c_void;

    pub type TTF_StyleFlag = c_int;
    pub static TTF_STYLE_NORMAL: TTF_StyleFlag = 0x00;
    pub static TTF_STYLE_BOLD: TTF_StyleFlag = 0x01;
    pub static TTF_STYLE_ITALIC: TTF_StyleFlag = 0x02;
    pub static TTF_STYLE_UNDERLINE: c_int = 0x04;
    pub static TTF_STYLE_STRIKETHROUGH: c_int = 0x08;

    pub type TTF_Hinting = c_int;
    pub static TTF_HINTING_NORMAL: TTF_Hinting = 0;
    pub static TTF_HINTING_LIGHT: TTF_Hinting = 1;
    pub static TTF_HINTING_MONO: TTF_Hinting = 2;
    pub static TTF_HINTING_NONE: TTF_Hinting = 3;

    #[link_args = "-lSDL_ttf"]
    extern "C" {
        fn TTF_Init() -> c_int;
        fn TTF_WasInit() -> c_int;
        fn TTF_Quit();
        fn TTF_OpenFont(file: *c_char, ptsize: c_int) -> *TTF_Font;
        fn TTF_OpenFontIndex(file: *c_char, ptsize: c_int, index: c_long)
            -> *TTF_Font;
        fn TTF_CloseFont(font: *TTF_Font);

        fn TTF_GetFontStyle(font: *TTF_Font) -> TTF_StyleFlag;
        fn TTF_SetFontStyle(font: *TTF_Font, style: TTF_StyleFlag);
        fn TTF_GetFontOutline(font: *TTF_Font) -> c_int;
        fn TTF_SetFontOutline(font: *TTF_Font, outline: c_int);
        fn TTF_GetFontHinting(font: *TTF_Font) -> TTF_Hinting;
        fn TTF_SetFontHinting(font: *TTF_Font, hinting: TTF_Hinting);
        fn TTF_GetFontKerning(font: *TTF_Font) -> c_int;
        fn TTF_SetFontKerning(font: *TTF_Font, kerning: c_int);
        fn TTF_FontHeight(font: *TTF_Font) -> c_int;
        fn TTF_FontAscent(font: *TTF_Font) -> c_int;
        fn TTF_FontDescent(font: *TTF_Font) -> c_int;
        fn TTF_FontLineSkip(font: *TTF_Font) -> c_int;
        fn TTF_FontFaces(font: *TTF_Font) -> c_long;
        fn TTF_FontFaceIsFixedWidth(font: *TTF_Font) -> c_int;
        fn TTF_FontFaceFamilyName(font: *TTF_Font) -> *c_char;
        fn TTF_FontGlyphIsProvided(font: *TTF_Font, glyph: u16) -> c_int;
        fn TTF_GlyphMetrics(font: *TTF_Font, glyph: u16, minx: *mut c_int,
            maxx: *mut c_int, miny: *mut c_int, maxy: *mut c_int,
            advance: *mut c_int) -> c_int;
        fn TTF_SizeUTF8(font: *TTF_Font, text: *c_char, w: *mut c_int,
            h: *mut c_int) -> c_int;

        fn TTF_RenderUTF8_Solid(font: *TTF_Font, text: *c_char,
            fg: SDL_Color) -> *SDL_Surface;
        fn TTF_RenderUTF8_Shaded(font: *TTF_Font, text: *c_char,
            fg: SDL_Color, bg: SDL_Color) -> *SDL_Surface;
        fn TTF_RenderUTF8_Blended(font: *TTF_Font, text: *c_char,
            fg: SDL_Color) -> *SDL_Surface;
    }
}

pub enum FontStyle {
    NormalStyle = ffi::TTF_STYLE_NORMAL as int,
    BoldStyle = ffi::TTF_STYLE_BOLD as int,
    ItalicStyle = ffi::TTF_STYLE_ITALIC as int,
    UnderlineStyle = ffi::TTF_STYLE_UNDERLINE as int,
    StrikethroughStyle = ffi::TTF_STYLE_STRIKETHROUGH as int
}

pub enum FontHinting {
    NormalHinting = ffi::TTF_HINTING_NORMAL as int,
    LightHinting = ffi::TTF_HINTING_LIGHT as int,
    MonoHinting = ffi::TTF_HINTING_MONO as int,
    NoneHinting = ffi::TTF_HINTING_NONE as int
}

pub struct GlyphMetrics {
    minx: int,
    maxx: int,
    miny: int,
    maxy: int,
    advance: int
}

pub struct Font {
    priv raw: *ffi::TTF_Font
}

impl Drop for Font {
    fn drop(&self) {
        unsafe {
            ffi::TTF_CloseFont(self.raw);
        }
    }
}

impl Font {
    pub fn get_style(&self) -> ~[FontStyle] {
        let bitflags = unsafe { ffi::TTF_GetFontStyle(self.raw) };

        let flags = [
            NormalStyle,
            BoldStyle,
            ItalicStyle,
            UnderlineStyle,
            StrikethroughStyle
        ];

        do flags.iter().filter_map |&flag| {
            if bitflags & (flag as ffi::TTF_StyleFlag) != 0 { Some(flag) }
            else {None}
        }.collect()
    }

    pub fn set_style(&mut self, flags: &[FontStyle]) {
        let bitflags = do flags.iter().fold(0) |bitflags, &flag| {
            bitflags | flag as ffi::TTF_StyleFlag
        };

        unsafe {
            ffi::TTF_SetFontStyle(self.raw, bitflags);
        }
    }

    pub fn get_outline(&self) -> int {
        unsafe {
            ffi::TTF_GetFontOutline(self.raw) as int
        }
    }

    pub fn set_outline(&mut self, outline: int) {
        unsafe {
            ffi::TTF_SetFontOutline(self.raw, outline as c_int);
        }
    }

    pub fn get_hinting(&self) -> FontHinting {
        unsafe {
            cast::transmute(ffi::TTF_GetFontHinting(self.raw) as int)
        }
    }

    pub fn set_hinting(&mut self, hinting: FontHinting) {
        unsafe {
            ffi::TTF_SetFontHinting(self.raw, hinting as ffi::TTF_Hinting);
        }
    }

    pub fn get_kerning(&self) -> int {
        unsafe {
            ffi::TTF_GetFontKerning(self.raw) as int
        }
    }

    pub fn set_kerning(&mut self, kerning: int) {
        unsafe {
            ffi::TTF_SetFontKerning(self.raw, kerning as c_int);
        }
    }

    pub fn height(&self) -> int {
        unsafe {
            ffi::TTF_FontHeight(self.raw) as int
        }
    }

    pub fn ascent(&self) -> int {
        unsafe {
            ffi::TTF_FontAscent(self.raw) as int
        }
    }

    pub fn descent(&self) -> int {
        unsafe {
            ffi::TTF_FontDescent(self.raw) as int
        }
    }

    pub fn line_skip(&self) -> int {
        unsafe {
            ffi::TTF_FontLineSkip(self.raw) as int
        }
    }

    pub fn faces(&self) -> int {
        unsafe {
            ffi::TTF_FontFaces(self.raw) as int
        }
    }

    pub fn face_is_fixed_width(&self) -> bool {
        unsafe {
            ffi::TTF_FontFaceIsFixedWidth(self.raw) > 0
        }
    }

    pub fn face_family_name(&self) -> Option<~str> {
        unsafe {
            let ptr = ffi::TTF_FontFaceFamilyName(self.raw);

            if ptr.is_null() {
                None
            } else {
                Some(str::raw::from_c_str(ptr))
            }
        }
    }

    pub fn glyph_is_provided(&self, glyph: char) -> Option<int> {
        let ch = match char_to_utf16(glyph) {
            Some(ch) => ch,
            None => return None
        };

        unsafe {
            match ffi::TTF_FontGlyphIsProvided(self.raw, ch) {
                0 => None,
                ch => Some(ch as int)
            }
        }
    }

    pub fn glyph_metrics(&self, glyph: char) -> Result<~GlyphMetrics, ~str> {
        let ch = match char_to_utf16(glyph) {
            Some(ch) => ch,
            None => return Err(~"Glyph is not a UTF-16 character")
        };

        let mut minx: c_int = 0;
        let mut maxx: c_int = 0;
        let mut miny: c_int = 0;
        let mut maxy: c_int = 0;
        let mut advance: c_int = 0;

        unsafe {
            match ffi::TTF_GlyphMetrics(self.raw, ch, &mut minx, &mut maxx,
                    &mut miny, &mut maxy, &mut advance) {
                0 => Ok(~GlyphMetrics {minx: minx as int, maxx: maxx as int,
                    miny: miny as int, maxy: maxy as int,
                    advance: advance as int}),
                _ => Err(sdl::get_error())
            }
        }
    }

    pub fn text_size(&self, text: &str) -> Result<(int, int), ~str> {
        let mut w: c_int = 0;
        let mut h: c_int = 0;

        do text.as_c_str |c_text| {
            unsafe {
                match ffi::TTF_SizeUTF8(self.raw, c_text, &mut w, &mut h) {
                    0 => Ok((w as int, h as int)),
                    _ => Err(sdl::get_error())
                }
            }
        }
    }
}

fn char_to_utf16(glyph: char) -> Option<u16> {
    // TODO bounds checking
    if !str::is_utf16([glyph as u16]) {
        return None;
    }

    Some(str::from_char(glyph).to_utf16()[0])
}

pub fn init() -> bool {
    unsafe {
        ffi::TTF_Init() == 0
    }
}

pub fn was_init() -> bool {
    unsafe {
        ffi::TTF_WasInit() == 1
    }
}

pub fn quit() {
    unsafe {
        ffi::TTF_Quit();
    }
}

pub fn open_font(file: &str, ptsize: int) -> Result<~Font, ~str> {
    do file.as_c_str |c_str| {
        unsafe {
            let ptr = ffi::TTF_OpenFont(c_str, ptsize as c_int);
            if ptr.is_null() {
                Err(sdl::get_error())
            } else {
                Ok(~Font {raw: ptr})
            }
        }
    }
}

pub fn open_font_index(file: &str, ptsize: int, index: int)
        -> Result<~Font, ~str> {
    do file.as_c_str |c_str| {
        let ptr = unsafe {
            ffi::TTF_OpenFontIndex(c_str, ptsize as c_int, index as c_long)
        };

        if ptr.is_null() {
            Err(sdl::get_error())
        } else {
            Ok(~Font {raw: ptr})
        }
    }
}

pub fn render_solid(font: &Font, text: &str, fg: Color)
        -> Result<~Surface, ~str> {
    do text.as_c_str |c_text| {
        let ptr = unsafe {
            ffi::TTF_RenderUTF8_Solid(font.raw, c_text, fg.to_struct())
        };

        if ptr.is_null() {
            Err(sdl::get_error())
        } else {
            Ok(~Surface {raw: ptr, owned: true})
        }
    }
}

pub fn render_shaded(font: &Font, text: &str, fg: Color, bg: Color)
        -> Result<~Surface, ~str> {
    do text.as_c_str |c_text| {
        let ptr = unsafe {
            ffi::TTF_RenderUTF8_Shaded(font.raw, c_text, fg.to_struct(),
                bg.to_struct())
        };

        if ptr.is_null() {
            Err(sdl::get_error())
        } else {
            Ok(~Surface {raw: ptr, owned: true})
        }
    }
}

pub fn render_blended(font: &Font, text: &str, fg: Color)
        -> Result<~Surface, ~str> {
    do text.as_c_str |c_text| {
        let ptr = unsafe {
            ffi::TTF_RenderUTF8_Blended(font.raw, c_text, fg.to_struct())
        };

        if ptr.is_null() {
            Err(sdl::get_error())
        } else {
            Ok(~Surface {raw: ptr, owned: true})
        }
    }
}
<commit_msg>Update to work with latest Rust<commit_after>#[link(name = "sdl_ttf",
       vers = "0.1",
       uuid = "1d776704-431f-4d5b-9361-b27958535321",
       url = "http://github.com/sfackler/rust-sdl_ttf")];

#[comment="SDL_ttf bindings"];
#[license="MIT"];
#[crate_type="lib"];

extern mod sdl;

use std::cast;
use std::libc::{c_int, c_long};
use std::str;

use sdl::video::{Color, Surface};

mod ffi {
    use std::libc::{c_int, c_char, c_void, c_long};
    use sdl::video::ll::{SDL_Color, SDL_Surface};

    pub type TTF_Font = c_void;

    pub type TTF_StyleFlag = c_int;
    pub static TTF_STYLE_NORMAL: TTF_StyleFlag = 0x00;
    pub static TTF_STYLE_BOLD: TTF_StyleFlag = 0x01;
    pub static TTF_STYLE_ITALIC: TTF_StyleFlag = 0x02;
    pub static TTF_STYLE_UNDERLINE: c_int = 0x04;
    pub static TTF_STYLE_STRIKETHROUGH: c_int = 0x08;

    pub type TTF_Hinting = c_int;
    pub static TTF_HINTING_NORMAL: TTF_Hinting = 0;
    pub static TTF_HINTING_LIGHT: TTF_Hinting = 1;
    pub static TTF_HINTING_MONO: TTF_Hinting = 2;
    pub static TTF_HINTING_NONE: TTF_Hinting = 3;

    #[link_args = "-lSDL_ttf"]
    extern "C" {
        fn TTF_Init() -> c_int;
        fn TTF_WasInit() -> c_int;
        fn TTF_Quit();
        fn TTF_OpenFont(file: *c_char, ptsize: c_int) -> *TTF_Font;
        fn TTF_OpenFontIndex(file: *c_char, ptsize: c_int, index: c_long)
            -> *TTF_Font;
        fn TTF_CloseFont(font: *TTF_Font);

        fn TTF_GetFontStyle(font: *TTF_Font) -> TTF_StyleFlag;
        fn TTF_SetFontStyle(font: *TTF_Font, style: TTF_StyleFlag);
        fn TTF_GetFontOutline(font: *TTF_Font) -> c_int;
        fn TTF_SetFontOutline(font: *TTF_Font, outline: c_int);
        fn TTF_GetFontHinting(font: *TTF_Font) -> TTF_Hinting;
        fn TTF_SetFontHinting(font: *TTF_Font, hinting: TTF_Hinting);
        fn TTF_GetFontKerning(font: *TTF_Font) -> c_int;
        fn TTF_SetFontKerning(font: *TTF_Font, kerning: c_int);
        fn TTF_FontHeight(font: *TTF_Font) -> c_int;
        fn TTF_FontAscent(font: *TTF_Font) -> c_int;
        fn TTF_FontDescent(font: *TTF_Font) -> c_int;
        fn TTF_FontLineSkip(font: *TTF_Font) -> c_int;
        fn TTF_FontFaces(font: *TTF_Font) -> c_long;
        fn TTF_FontFaceIsFixedWidth(font: *TTF_Font) -> c_int;
        fn TTF_FontFaceFamilyName(font: *TTF_Font) -> *c_char;
        fn TTF_FontGlyphIsProvided(font: *TTF_Font, glyph: u16) -> c_int;
        fn TTF_GlyphMetrics(font: *TTF_Font, glyph: u16, minx: *mut c_int,
            maxx: *mut c_int, miny: *mut c_int, maxy: *mut c_int,
            advance: *mut c_int) -> c_int;
        fn TTF_SizeUTF8(font: *TTF_Font, text: *c_char, w: *mut c_int,
            h: *mut c_int) -> c_int;

        fn TTF_RenderUTF8_Solid(font: *TTF_Font, text: *c_char,
            fg: SDL_Color) -> *SDL_Surface;
        fn TTF_RenderUTF8_Shaded(font: *TTF_Font, text: *c_char,
            fg: SDL_Color, bg: SDL_Color) -> *SDL_Surface;
        fn TTF_RenderUTF8_Blended(font: *TTF_Font, text: *c_char,
            fg: SDL_Color) -> *SDL_Surface;
    }
}

pub enum FontStyle {
    NormalStyle = ffi::TTF_STYLE_NORMAL as int,
    BoldStyle = ffi::TTF_STYLE_BOLD as int,
    ItalicStyle = ffi::TTF_STYLE_ITALIC as int,
    UnderlineStyle = ffi::TTF_STYLE_UNDERLINE as int,
    StrikethroughStyle = ffi::TTF_STYLE_STRIKETHROUGH as int
}

pub enum FontHinting {
    NormalHinting = ffi::TTF_HINTING_NORMAL as int,
    LightHinting = ffi::TTF_HINTING_LIGHT as int,
    MonoHinting = ffi::TTF_HINTING_MONO as int,
    NoneHinting = ffi::TTF_HINTING_NONE as int
}

pub struct GlyphMetrics {
    minx: int,
    maxx: int,
    miny: int,
    maxy: int,
    advance: int
}

pub struct Font {
    priv raw: *ffi::TTF_Font
}

impl Drop for Font {
    #[fixed_stack_segment]
    fn drop(&self) {
        unsafe {
            ffi::TTF_CloseFont(self.raw);
        }
    }
}

impl Font {
    #[fixed_stack_segment]
    pub fn get_style(&self) -> ~[FontStyle] {
        let bitflags = unsafe { ffi::TTF_GetFontStyle(self.raw) };

        let flags = [
            NormalStyle,
            BoldStyle,
            ItalicStyle,
            UnderlineStyle,
            StrikethroughStyle
        ];

        do flags.iter().filter_map |&flag| {
            if bitflags & (flag as ffi::TTF_StyleFlag) != 0 { Some(flag) }
            else {None}
        }.collect()
    }

    #[fixed_stack_segment]
    pub fn set_style(&mut self, flags: &[FontStyle]) {
        let bitflags = do flags.iter().fold(0) |bitflags, &flag| {
            bitflags | flag as ffi::TTF_StyleFlag
        };

        unsafe {
            ffi::TTF_SetFontStyle(self.raw, bitflags);
        }
    }

    #[fixed_stack_segment]
    pub fn get_outline(&self) -> int {
        unsafe {
            ffi::TTF_GetFontOutline(self.raw) as int
        }
    }

    #[fixed_stack_segment]
    pub fn set_outline(&mut self, outline: int) {
        unsafe {
            ffi::TTF_SetFontOutline(self.raw, outline as c_int);
        }
    }

    #[fixed_stack_segment]
    pub fn get_hinting(&self) -> FontHinting {
        unsafe {
            cast::transmute(ffi::TTF_GetFontHinting(self.raw) as int)
        }
    }

    #[fixed_stack_segment]
    pub fn set_hinting(&mut self, hinting: FontHinting) {
        unsafe {
            ffi::TTF_SetFontHinting(self.raw, hinting as ffi::TTF_Hinting);
        }
    }

    #[fixed_stack_segment]
    pub fn get_kerning(&self) -> int {
        unsafe {
            ffi::TTF_GetFontKerning(self.raw) as int
        }
    }

    #[fixed_stack_segment]
    pub fn set_kerning(&mut self, kerning: int) {
        unsafe {
            ffi::TTF_SetFontKerning(self.raw, kerning as c_int);
        }
    }

    #[fixed_stack_segment]
    pub fn height(&self) -> int {
        unsafe {
            ffi::TTF_FontHeight(self.raw) as int
        }
    }

    #[fixed_stack_segment]
    pub fn ascent(&self) -> int {
        unsafe {
            ffi::TTF_FontAscent(self.raw) as int
        }
    }

    #[fixed_stack_segment]
    pub fn descent(&self) -> int {
        unsafe {
            ffi::TTF_FontDescent(self.raw) as int
        }
    }

    #[fixed_stack_segment]
    pub fn line_skip(&self) -> int {
        unsafe {
            ffi::TTF_FontLineSkip(self.raw) as int
        }
    }

    #[fixed_stack_segment]
    pub fn faces(&self) -> int {
        unsafe {
            ffi::TTF_FontFaces(self.raw) as int
        }
    }

    #[fixed_stack_segment]
    pub fn face_is_fixed_width(&self) -> bool {
        unsafe {
            ffi::TTF_FontFaceIsFixedWidth(self.raw) > 0
        }
    }

    #[fixed_stack_segment]
    pub fn face_family_name(&self) -> Option<~str> {
        unsafe {
            let ptr = ffi::TTF_FontFaceFamilyName(self.raw);

            if ptr.is_null() {
                None
            } else {
                Some(str::raw::from_c_str(ptr))
            }
        }
    }

    #[fixed_stack_segment]
    pub fn glyph_is_provided(&self, glyph: char) -> Option<int> {
        let ch = match char_to_utf16(glyph) {
            Some(ch) => ch,
            None => return None
        };

        unsafe {
            match ffi::TTF_FontGlyphIsProvided(self.raw, ch) {
                0 => None,
                ch => Some(ch as int)
            }
        }
    }

    #[fixed_stack_segment]
    pub fn glyph_metrics(&self, glyph: char) -> Result<~GlyphMetrics, ~str> {
        let ch = match char_to_utf16(glyph) {
            Some(ch) => ch,
            None => return Err(~"Glyph is not a UTF-16 character")
        };

        let mut minx: c_int = 0;
        let mut maxx: c_int = 0;
        let mut miny: c_int = 0;
        let mut maxy: c_int = 0;
        let mut advance: c_int = 0;

        unsafe {
            match ffi::TTF_GlyphMetrics(self.raw, ch, &mut minx, &mut maxx,
                    &mut miny, &mut maxy, &mut advance) {
                0 => Ok(~GlyphMetrics {minx: minx as int, maxx: maxx as int,
                    miny: miny as int, maxy: maxy as int,
                    advance: advance as int}),
                _ => Err(sdl::get_error())
            }
        }
    }

    #[fixed_stack_segment]
    pub fn text_size(&self, text: &str) -> Result<(int, int), ~str> {
        let mut w: c_int = 0;
        let mut h: c_int = 0;

        do text.with_c_str |c_text| {
            unsafe {
                match ffi::TTF_SizeUTF8(self.raw, c_text, &mut w, &mut h) {
                    0 => Ok((w as int, h as int)),
                    _ => Err(sdl::get_error())
                }
            }
        }
    }
}

fn char_to_utf16(glyph: char) -> Option<u16> {
    // TODO bounds checking
    if !str::is_utf16([glyph as u16]) {
        return None;
    }

    Some(str::from_char(glyph).to_utf16()[0])
}

#[fixed_stack_segment]
pub fn init() -> bool {
    unsafe {
        ffi::TTF_Init() == 0
    }
}

#[fixed_stack_segment]
pub fn was_init() -> bool {
    unsafe {
        ffi::TTF_WasInit() == 1
    }
}

#[fixed_stack_segment]
pub fn quit() {
    unsafe {
        ffi::TTF_Quit();
    }
}

#[fixed_stack_segment]
pub fn open_font(file: &str, ptsize: int) -> Result<~Font, ~str> {
    do file.with_c_str |c_str| {
        unsafe {
            let ptr = ffi::TTF_OpenFont(c_str, ptsize as c_int);
            if ptr.is_null() {
                Err(sdl::get_error())
            } else {
                Ok(~Font {raw: ptr})
            }
        }
    }
}

#[fixed_stack_segment]
pub fn open_font_index(file: &str, ptsize: int, index: int)
        -> Result<~Font, ~str> {
    do file.with_c_str |c_str| {
        let ptr = unsafe {
            ffi::TTF_OpenFontIndex(c_str, ptsize as c_int, index as c_long)
        };

        if ptr.is_null() {
            Err(sdl::get_error())
        } else {
            Ok(~Font {raw: ptr})
        }
    }
}

#[fixed_stack_segment]
pub fn render_solid(font: &Font, text: &str, fg: Color)
        -> Result<~Surface, ~str> {
    do text.with_c_str |c_text| {
        let ptr = unsafe {
            ffi::TTF_RenderUTF8_Solid(font.raw, c_text, fg.to_struct())
        };

        if ptr.is_null() {
            Err(sdl::get_error())
        } else {
            Ok(~Surface {raw: ptr, owned: true})
        }
    }
}

#[fixed_stack_segment]
pub fn render_shaded(font: &Font, text: &str, fg: Color, bg: Color)
        -> Result<~Surface, ~str> {
    do text.with_c_str |c_text| {
        let ptr = unsafe {
            ffi::TTF_RenderUTF8_Shaded(font.raw, c_text, fg.to_struct(),
                bg.to_struct())
        };

        if ptr.is_null() {
            Err(sdl::get_error())
        } else {
            Ok(~Surface {raw: ptr, owned: true})
        }
    }
}

#[fixed_stack_segment]
pub fn render_blended(font: &Font, text: &str, fg: Color)
        -> Result<~Surface, ~str> {
    do text.with_c_str |c_text| {
        let ptr = unsafe {
            ffi::TTF_RenderUTF8_Blended(font.raw, c_text, fg.to_struct())
        };

        if ptr.is_null() {
            Err(sdl::get_error())
        } else {
            Ok(~Surface {raw: ptr, owned: true})
        }
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::path::PathBuf;
use std::path::Path;

use error::*;

pub struct Vfs {
	mount_points: HashMap<String, PathBuf>,
}

impl Vfs {
	pub fn new() -> Vfs {
		let mut instance = Vfs {
			mount_points: HashMap::new(),
		};
		instance.mount( "root", Path::new("samplemusic") );
		instance
	}

	pub fn mount(&mut self, name: &str, real_path: &Path) -> Result<(), CollectionError>
	{
		let name = name.to_string();
		if self.mount_points.contains_key(&name)	{
			return Err(CollectionError::ConflictingMount);
		}
		self.mount_points.insert(name, real_path.to_path_buf());
		Ok(())
	}

	pub fn real_to_virtual(&self, real_path: &Path) -> Result<PathBuf, CollectionError> {
		for (name, target) in &self.mount_points {
			match real_path.strip_prefix(target) {
				Ok(p) => {
					let mount_path = Path::new(&name);
					return Ok(mount_path.join(p));
				},
				Err(_) => (),
			}
		}
		Err(CollectionError::PathNotInVfs)
	}

	pub fn virtual_to_real(&self, virtual_path: &Path) -> Result<PathBuf, CollectionError> {
		for (name, target) in &self.mount_points {
			let mount_path = Path::new(&name);
			match virtual_path.strip_prefix(mount_path) {
				Ok(p) => return Ok(target.join(p)),
				Err(_) => (),
			}
		}
		Err(CollectionError::PathNotInVfs)
	}
}<commit_msg>Added basic unit test<commit_after>use std::collections::HashMap;
use std::path::PathBuf;
use std::path::Path;

use error::*;

pub struct Vfs {
	mount_points: HashMap<String, PathBuf>,
}

impl Vfs {
	pub fn new() -> Vfs {
		let instance = Vfs {
			mount_points: HashMap::new(),
		};
		instance
	}

	pub fn mount(&mut self, name: &str, real_path: &Path) -> Result<(), CollectionError>
	{
		let name = name.to_string();
		if self.mount_points.contains_key(&name)	{
			return Err(CollectionError::ConflictingMount);
		}
		self.mount_points.insert(name, real_path.to_path_buf());
		Ok(())
	}

	pub fn real_to_virtual(&self, real_path: &Path) -> Result<PathBuf, CollectionError> {
		for (name, target) in &self.mount_points {
			match real_path.strip_prefix(target) {
				Ok(p) => {
					let mount_path = Path::new(&name);
					return Ok(mount_path.join(p));
				},
				Err(_) => (),
			}
		}
		Err(CollectionError::PathNotInVfs)
	}

	pub fn virtual_to_real(&self, virtual_path: &Path) -> Result<PathBuf, CollectionError> {
		for (name, target) in &self.mount_points {
			let mount_path = Path::new(&name);
			match virtual_path.strip_prefix(mount_path) {
				Ok(p) => return Ok(target.join(p)),
				Err(_) => (),
			}
		}
		Err(CollectionError::PathNotInVfs)
	}
}

#[test]
fn test_mount() {
	let mut vfs = Vfs::new();
	assert!(vfs.mount("root", Path::new("test_dir")).is_ok());
	assert!(vfs.mount("root", Path::new("another_dir")).is_err());
}

#[test]
fn test_virtual_to_real() {
	let mut vfs = Vfs::new();
	assert!(vfs.mount("root", Path::new("test_dir")).is_ok());
	let correct_path = Path::new("test_dir/somewhere/something.png");
	let found_path = vfs.virtual_to_real(Path::new("root/somewhere/something.png")).unwrap();
	assert!(found_path == correct_path);
}

#[test]
fn test_real_to_virtual() {
	let mut vfs = Vfs::new();
	assert!(vfs.mount("root", Path::new("test_dir")).is_ok());
	let correct_path = Path::new("root/somewhere/something.png");
	let found_path = vfs.real_to_virtual(Path::new("test_dir/somewhere/something.png")).unwrap();
	assert!(found_path == correct_path);
}<|endoftext|>


<commit_before>// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.

// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Parity.  If not, see <http://www.gnu.org/licenses/>.

//! jsonrpc server over win named pipes
//!
//! ```no_run
//! extern crate jsonrpc_core;
//! extern crate json_ipc_server;
//!
//! use std::sync::Arc;
//! use jsonrpc_core::*;
//! use json_ipc_server::Server;
//!
//! struct SayHello;
//! impl MethodCommand for SayHello {
//! 	fn execute(&self, _params: Params) -> Result<Value, Error> {
//! 		Ok(Value::String("hello".to_string()))
//! 	}
//! }
//!
//! fn main() {
//! 	let io = IoHandler::new();
//! 	io.add_method("say_hello", SayHello);
//! 	let server = Server::new("/tmp/json-ipc-test.ipc", &Arc::new(io)).unwrap();
//!     ::std::thread::spawn(move || server.run());
//! }
//! ```

//! Named pipes library

use miow::pipe::{NamedPipe, NamedPipeBuilder};
use miow::Overlapped;
use miow::iocp::CompletionPort;
use std;
use std::io;
use std::io::{Read, Write};
use std::sync::atomic::*;
use std::sync::Arc;
use jsonrpc_core::IoHandler;
use validator;

pub type Result<T> = std::result::Result<T, Error>;

const MAX_REQUEST_LEN: u32 = 65536;
const REQUEST_READ_BATCH: usize = 4096;
const POLL_PARK_TIMEOUT_MS: u64 = 10;
const STARTING_PIPE_TOKEN: u32 = 1;

#[derive(Debug)]
pub enum Error {
    Io(std::io::Error),
    NotStarted,
    AlreadyStopping,
    NotStopped,
    IsStopping,
}

impl std::convert::From<std::io::Error> for Error {
    fn from(io_error: std::io::Error) -> Error {
        Error::Io(io_error)
    }
}

pub struct PipeHandler {
    waiting_pipe: NamedPipe,
    io_handler: Arc<IoHandler>,
    handle_counter: u32,
}

impl PipeHandler {
    /// start ipc rpc server (blocking)
    pub fn start(addr: &str, io_handler: &Arc<IoHandler>) -> Result<PipeHandler> {
        Ok(PipeHandler {
            waiting_pipe: try!(NamedPipe::new(addr)),
            io_handler: io_handler.clone(),
            handle_counter: STARTING_PIPE_TOKEN,
        })
    }

    fn handle_incoming(&mut self, addr: &str, stop: Arc<AtomicBool>) -> io::Result<()> {
        let cp = try!(CompletionPort::new(self.handle_counter));
        let pipe_token = self.handle_counter as usize + 1;
        try!(cp.add_handle(pipe_token, &self.waiting_pipe));

        let mut overlapped = Overlapped::zero();
        unsafe { try!(self.waiting_pipe.connect_overlapped(&mut overlapped)); };
        trace!(target: "ipc", "Waiting for client: [{}, {}] [{}]", self.handle_counter, pipe_token, addr);
        while !stop.load(Ordering::Relaxed) {
            if let Ok(status) = cp.get(None) {
                if status.token() == pipe_token
                {
                    trace!(target: "ipc", "Received connection to address [{}]", addr);
                    break;
                }
            }
            std::thread::park_timeout(std::time::Duration::from_millis(POLL_PARK_TIMEOUT_MS));
        }

        if stop.load(Ordering::Relaxed) {
            trace!(target: "ipc", "Stopped listening sequence [{}]", addr);
            return Ok(())
        }

        let mut connected_pipe = std::mem::replace::<NamedPipe>(&mut self.waiting_pipe,
            try!(NamedPipeBuilder::new(addr)
                .first(false)
                .inbound(true)
                .outbound(true)
                .out_buffer_size(MAX_REQUEST_LEN)
                .in_buffer_size(MAX_REQUEST_LEN)
                .create()));
        self.handle_counter += 2;

        let thread_handler = self.io_handler.clone();
        std::thread::spawn(move || {
            let mut buf = vec![0u8; MAX_REQUEST_LEN as usize];
            let mut fin = REQUEST_READ_BATCH;
            loop {
                let start = fin - REQUEST_READ_BATCH;
                match connected_pipe.read(&mut buf[start..fin]) {
                    Ok(size) => {
                        let effective = &buf[0..start + size];
                        fin = fin + REQUEST_READ_BATCH;
                        if !validator::is_valid(effective) {
                            continue;
                        }
                        trace!(target: "ipc", "Received rpc request: {} bytes", effective.len());

                        if let Err(parse_err) = String::from_utf8(effective.to_vec())
                            .map(|rpc_msg|
                                 {
                                    let response: Option<String> = thread_handler.handle_request(&rpc_msg);
                                    if let Some(response_str) = response {
                                        let response_bytes = response_str.into_bytes();
                                        if let Err(write_err) = connected_pipe.write_all(&response_bytes[..]) {
                                            trace!(target: "ipc", "Response write error: {:?}", write_err);
                                        }
                                        trace!(target: "ipc", "Sent rpc response:  {} bytes", response_bytes.len());
                                        connected_pipe.flush().unwrap();
                                    }
                                }
                            )
                        {
                            trace!(target: "ipc", "Response decode error: {:?}", parse_err);
                        }

                        fin = REQUEST_READ_BATCH;
                    },
                    Err(e) => {
                        // closed connection
                        trace!(target: "ipc", "Dropped connection {:?}", e);
                        break;
                    }
                }
            }
        });

        Ok(())
    }
}

pub struct Server {
    is_stopping: Arc<AtomicBool>,
    is_stopped: Arc<AtomicBool>,
    addr: String,
    io_handler: Arc<IoHandler>,
}

impl Server {
    /// New server
    pub fn new(socket_addr: &str, io_handler: &Arc<IoHandler>) -> Result<Server> {
        Ok(Server {
            io_handler: io_handler.clone(),
            is_stopping: Arc::new(AtomicBool::new(false)),
            is_stopped: Arc::new(AtomicBool::new(true)),
            addr: socket_addr.to_owned(),
        })
    }

    /// Run server (in this thread)
    pub fn run(&self) -> Result<()> {
        let mut pipe_handler = try!(PipeHandler::start(&self.addr, &self.io_handler));
        loop  {
            try!(pipe_handler.handle_incoming(&self.addr, Arc::new(AtomicBool::new(false))));
        }
    }

    /// Run server (in separate thread)
    pub fn run_async(&self) -> Result<()> {
        if self.is_stopping.load(Ordering::Relaxed) { return Err(Error::IsStopping) }
        if !self.is_stopped.load(Ordering::Relaxed) { return Err(Error::NotStopped) }

        trace!(target: "ipc", "Started named pipes server [{}]", self.addr);

        let thread_stopping = self.is_stopping.clone();
        let thread_stopped = self.is_stopped.clone();
        let thread_handler = self.io_handler.clone();
        let addr = self.addr.clone();
        std::thread::spawn(move || {
            let mut pipe_handler = PipeHandler::start(&addr, &thread_handler).unwrap();
            while !thread_stopping.load(Ordering::Relaxed) {
                if let Err(pipe_listener_error) = pipe_handler.handle_incoming(&addr, thread_stopping.clone()) {
                    trace!(target: "ipc", "Pipe listening error: {:?}", pipe_listener_error);
                }
            }
            thread_stopped.store(true, Ordering::Relaxed);
        });

        self.is_stopped.store(false, Ordering::Relaxed);
        Ok(())
    }

    pub fn stop_async(&self) -> Result<()> {
        if self.is_stopped.load(Ordering::Relaxed) { return Err(Error::NotStarted) }
        if self.is_stopping.load(Ordering::Relaxed) { return Err(Error::AlreadyStopping)}
        self.is_stopping.store(true, Ordering::Relaxed);
        Ok(())
    }

    pub fn stop(&self) -> Result<()> {
        if self.is_stopped.load(Ordering::Relaxed) { return Err(Error::NotStarted) }
        if self.is_stopping.load(Ordering::Relaxed) { return Err(Error::AlreadyStopping)}
        self.is_stopping.store(true, Ordering::Relaxed);
        while !self.is_stopped.load(Ordering::Relaxed) { std::thread::park_timeout(std::time::Duration::new(0, 50)); }
        Ok(())
    }
}

impl Drop for Server {
    fn drop(&mut self) {
        self.stop_async().unwrap_or_else(|_| {}); // ignore error - can be stopped already
        // todo : no stable logging for windows?
        trace!(target: "ipc", "IPC Server : shutdown");
    }
}
<commit_msg>accept remote challenge<commit_after>// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.

// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Parity.  If not, see <http://www.gnu.org/licenses/>.

//! jsonrpc server over win named pipes
//!
//! ```no_run
//! extern crate jsonrpc_core;
//! extern crate json_ipc_server;
//!
//! use std::sync::Arc;
//! use jsonrpc_core::*;
//! use json_ipc_server::Server;
//!
//! struct SayHello;
//! impl MethodCommand for SayHello {
//! 	fn execute(&self, _params: Params) -> Result<Value, Error> {
//! 		Ok(Value::String("hello".to_string()))
//! 	}
//! }
//!
//! fn main() {
//! 	let io = IoHandler::new();
//! 	io.add_method("say_hello", SayHello);
//! 	let server = Server::new("/tmp/json-ipc-test.ipc", &Arc::new(io)).unwrap();
//!     ::std::thread::spawn(move || server.run());
//! }
//! ```

//! Named pipes library

use miow::pipe::{NamedPipe, NamedPipeBuilder};
use miow::Overlapped;
use miow::iocp::CompletionPort;
use std;
use std::io;
use std::io::{Read, Write};
use std::sync::atomic::*;
use std::sync::Arc;
use jsonrpc_core::IoHandler;
use validator;

pub type Result<T> = std::result::Result<T, Error>;

const MAX_REQUEST_LEN: u32 = 65536;
const REQUEST_READ_BATCH: usize = 4096;
const POLL_PARK_TIMEOUT_MS: u64 = 10;
const STARTING_PIPE_TOKEN: u32 = 1;

#[derive(Debug)]
pub enum Error {
    Io(std::io::Error),
    NotStarted,
    AlreadyStopping,
    NotStopped,
    IsStopping,
}

impl std::convert::From<std::io::Error> for Error {
    fn from(io_error: std::io::Error) -> Error {
        Error::Io(io_error)
    }
}

pub struct PipeHandler {
    waiting_pipe: NamedPipe,
    io_handler: Arc<IoHandler>,
    handle_counter: u32,
}

impl PipeHandler {
    /// start ipc rpc server (blocking)
    pub fn start(addr: &str, io_handler: &Arc<IoHandler>) -> Result<PipeHandler> {
        Ok(PipeHandler {
            waiting_pipe: try!(
                NamedPipeBuilder::new(addr)
                    .first(true)
                    .accept_remote(true)
                    .max_instances(255)
                    .inbound(true)
                    .outbound(true)
                    .out_buffer_size(MAX_REQUEST_LEN)
                    .in_buffer_size(MAX_REQUEST_LEN)
                    .create()
            ),
            io_handler: io_handler.clone(),
            handle_counter: STARTING_PIPE_TOKEN,
        })
    }

    fn handle_incoming(&mut self, addr: &str, stop: Arc<AtomicBool>) -> io::Result<()> {
        let cp = try!(CompletionPort::new(self.handle_counter));
        let pipe_token = self.handle_counter as usize + 1;
        try!(cp.add_handle(pipe_token, &self.waiting_pipe));

        let mut overlapped = Overlapped::zero();
        unsafe { try!(self.waiting_pipe.connect_overlapped(&mut overlapped)); };
        trace!(target: "ipc", "Waiting for client: [{}, {}] [{}]", self.handle_counter, pipe_token, addr);
        while !stop.load(Ordering::Relaxed) {
            if let Ok(status) = cp.get(None) {
                if status.token() == pipe_token
                {
                    trace!(target: "ipc", "Received connection to address [{}]", addr);
                    break;
                }
            }
            std::thread::park_timeout(std::time::Duration::from_millis(POLL_PARK_TIMEOUT_MS));
        }

        if stop.load(Ordering::Relaxed) {
            trace!(target: "ipc", "Stopped listening sequence [{}]", addr);
            return Ok(())
        }

        let mut connected_pipe = std::mem::replace::<NamedPipe>(&mut self.waiting_pipe,
            try!(NamedPipeBuilder::new(addr)
                .first(false)
                .accept_remote(true)
                .inbound(true)
                .outbound(true)
                .out_buffer_size(MAX_REQUEST_LEN)
                .in_buffer_size(MAX_REQUEST_LEN)
                .create()));
        self.handle_counter += 2;

        let thread_handler = self.io_handler.clone();
        std::thread::spawn(move || {
            let mut buf = vec![0u8; MAX_REQUEST_LEN as usize];
            let mut fin = REQUEST_READ_BATCH;
            loop {
                let start = fin - REQUEST_READ_BATCH;
                match connected_pipe.read(&mut buf[start..fin]) {
                    Ok(size) => {
                        let effective = &buf[0..start + size];
                        fin = fin + REQUEST_READ_BATCH;
                        if !validator::is_valid(effective) {
                            continue;
                        }
                        trace!(target: "ipc", "Received rpc request: {} bytes", effective.len());

                        if let Err(parse_err) = String::from_utf8(effective.to_vec())
                            .map(|rpc_msg|
                                 {
                                    let response: Option<String> = thread_handler.handle_request(&rpc_msg);
                                    if let Some(response_str) = response {
                                        let response_bytes = response_str.into_bytes();
                                        if let Err(write_err) = connected_pipe.write_all(&response_bytes[..]) {
                                            trace!(target: "ipc", "Response write error: {:?}", write_err);
                                        }
                                        trace!(target: "ipc", "Sent rpc response:  {} bytes", response_bytes.len());
                                        connected_pipe.flush().unwrap();
                                    }
                                }
                            )
                        {
                            trace!(target: "ipc", "Response decode error: {:?}", parse_err);
                        }

                        fin = REQUEST_READ_BATCH;
                    },
                    Err(e) => {
                        // closed connection
                        trace!(target: "ipc", "Dropped connection {:?}", e);
                        break;
                    }
                }
            }
        });

        Ok(())
    }
}

pub struct Server {
    is_stopping: Arc<AtomicBool>,
    is_stopped: Arc<AtomicBool>,
    addr: String,
    io_handler: Arc<IoHandler>,
}

impl Server {
    /// New server
    pub fn new(socket_addr: &str, io_handler: &Arc<IoHandler>) -> Result<Server> {
        Ok(Server {
            io_handler: io_handler.clone(),
            is_stopping: Arc::new(AtomicBool::new(false)),
            is_stopped: Arc::new(AtomicBool::new(true)),
            addr: socket_addr.to_owned(),
        })
    }

    /// Run server (in this thread)
    pub fn run(&self) -> Result<()> {
        let mut pipe_handler = try!(PipeHandler::start(&self.addr, &self.io_handler));
        loop  {
            try!(pipe_handler.handle_incoming(&self.addr, Arc::new(AtomicBool::new(false))));
        }
    }

    /// Run server (in separate thread)
    pub fn run_async(&self) -> Result<()> {
        if self.is_stopping.load(Ordering::Relaxed) { return Err(Error::IsStopping) }
        if !self.is_stopped.load(Ordering::Relaxed) { return Err(Error::NotStopped) }

        trace!(target: "ipc", "Started named pipes server [{}]", self.addr);

        let thread_stopping = self.is_stopping.clone();
        let thread_stopped = self.is_stopped.clone();
        let thread_handler = self.io_handler.clone();
        let addr = self.addr.clone();
        std::thread::spawn(move || {
            let mut pipe_handler = PipeHandler::start(&addr, &thread_handler).unwrap();
            while !thread_stopping.load(Ordering::Relaxed) {
                if let Err(pipe_listener_error) = pipe_handler.handle_incoming(&addr, thread_stopping.clone()) {
                    trace!(target: "ipc", "Pipe listening error: {:?}", pipe_listener_error);
                }
            }
            thread_stopped.store(true, Ordering::Relaxed);
        });

        self.is_stopped.store(false, Ordering::Relaxed);
        Ok(())
    }

    pub fn stop_async(&self) -> Result<()> {
        if self.is_stopped.load(Ordering::Relaxed) { return Err(Error::NotStarted) }
        if self.is_stopping.load(Ordering::Relaxed) { return Err(Error::AlreadyStopping)}
        self.is_stopping.store(true, Ordering::Relaxed);
        Ok(())
    }

    pub fn stop(&self) -> Result<()> {
        if self.is_stopped.load(Ordering::Relaxed) { return Err(Error::NotStarted) }
        if self.is_stopping.load(Ordering::Relaxed) { return Err(Error::AlreadyStopping)}
        self.is_stopping.store(true, Ordering::Relaxed);
        while !self.is_stopped.load(Ordering::Relaxed) { std::thread::park_timeout(std::time::Duration::new(0, 50)); }
        Ok(())
    }
}

impl Drop for Server {
    fn drop(&mut self) {
        self.stop_async().unwrap_or_else(|_| {}); // ignore error - can be stopped already
        // todo : no stable logging for windows?
        trace!(target: "ipc", "IPC Server : shutdown");
    }
}
<|endoftext|>


<commit_before>use crate::backend::{Backend, SupportsOnConflictClause};
use crate::expression::SqlLiteral;
use crate::query_builder::*;
use crate::query_source::Column;
use crate::result::QueryResult;

#[cfg(feature = "postgres")]
/// Used to specify the constraint name for an upsert statement in the form `ON
/// CONFLICT ON CONSTRAINT`. Note that `constraint_name` must be the name of a
/// unique constraint, not the name of an index.
///
/// # Example
///
/// ```rust
/// # include!("on_conflict_docs_setup.rs");
/// #
/// # fn main() {
/// #     use self::users::dsl::*;
/// use diesel::upsert::*;
///
/// #     let conn = establish_connection();
/// #     conn.execute("TRUNCATE TABLE users").unwrap();
/// conn.execute("ALTER TABLE users ADD CONSTRAINT users_name UNIQUE (name)").unwrap();
/// let user = User { id: 1, name: "Sean", };
/// let same_name_different_id = User { id: 2, name: "Sean" };
/// let same_id_different_name = User { id: 1, name: "Pascal" };
///
/// assert_eq!(Ok(1), diesel::insert_into(users).values(&user).execute(&conn));
///
/// let inserted_row_count = diesel::insert_into(users)
///     .values(&same_name_different_id)
///     .on_conflict(on_constraint("users_name"))
///     .do_nothing()
///     .execute(&conn);
/// assert_eq!(Ok(0), inserted_row_count);
///
/// let pk_conflict_result = diesel::insert_into(users)
///     .values(&same_id_different_name)
///     .on_conflict(on_constraint("users_name"))
///     .do_nothing()
///     .execute(&conn);
/// assert!(pk_conflict_result.is_err());
/// # }
/// ```
#[cfg(feature = "postgres")]
pub fn on_constraint(constraint_name: &str) -> OnConstraint {
    OnConstraint {
        constraint_name: constraint_name,
    }
}

#[doc(hidden)]
#[derive(Debug, Clone, Copy)]
pub struct OnConstraint<'a> {
    constraint_name: &'a str,
}

pub trait OnConflictTarget<Table> {}

#[doc(hidden)]
#[derive(Debug, Clone, Copy)]
pub struct NoConflictTarget;

impl<DB: Backend + SupportsOnConflictClause> QueryFragment<DB> for NoConflictTarget {
    fn walk_ast(&self, _: AstPass<DB>) -> QueryResult<()> {
        Ok(())
    }
}

impl<Table> OnConflictTarget<Table> for NoConflictTarget {}

#[doc(hidden)]
#[derive(Debug, Clone, Copy)]
pub struct ConflictTarget<T>(pub T);

impl<DB, T> QueryFragment<DB> for ConflictTarget<T>
where
    DB: Backend + SupportsOnConflictClause,
    T: Column,
{
    fn walk_ast(&self, mut out: AstPass<DB>) -> QueryResult<()> {
        out.push_sql(" (");
        out.push_identifier(T::NAME)?;
        out.push_sql(")");
        Ok(())
    }
}

impl<T> OnConflictTarget<T::Table> for ConflictTarget<T>
where
    T: Column,
{}

impl<DB, ST> QueryFragment<DB> for ConflictTarget<SqlLiteral<ST>>
where
    DB: Backend + SupportsOnConflictClause,
    SqlLiteral<ST>: QueryFragment<DB>,
{
    fn walk_ast(&self, mut out: AstPass<DB>) -> QueryResult<()> {
        out.push_sql(" ");
        self.0.walk_ast(out.reborrow())?;
        Ok(())
    }
}

impl<Tab, ST> OnConflictTarget<Tab> for ConflictTarget<SqlLiteral<ST>> {}

impl<'a, DB> QueryFragment<DB> for ConflictTarget<OnConstraint<'a>>
where
    DB: Backend + SupportsOnConflictClause,
{
    fn walk_ast(&self, mut out: AstPass<DB>) -> QueryResult<()> {
        out.push_sql(" ON CONSTRAINT ");
        out.push_identifier(self.0.constraint_name)?;
        Ok(())
    }
}

impl<'a, Table> OnConflictTarget<Table> for ConflictTarget<OnConstraint<'a>> {}

macro_rules! on_conflict_tuples {
    ($($col:ident),+) => {
        impl<DB, T, $($col),+> QueryFragment<DB> for ConflictTarget<(T, $($col),+)> where
            DB: Backend + SupportsOnConflictClause,
            T: Column,
            $($col: Column<Table=T::Table>,)+
        {
            fn walk_ast(&self, mut out: AstPass<DB>) -> QueryResult<()> {
                out.push_sql(" (");
                out.push_identifier(T::NAME)?;
                $(
                    out.push_sql(", ");
                    out.push_identifier($col::NAME)?;
                )+
                out.push_sql(")");
                Ok(())
            }
        }

        impl<T, $($col),+> OnConflictTarget<T::Table> for ConflictTarget<(T, $($col),+)> where
            T: Column,
            $($col: Column<Table=T::Table>,)+
        {
        }
    }
}

on_conflict_tuples!(U);
on_conflict_tuples!(U, V);
on_conflict_tuples!(U, V, W);
on_conflict_tuples!(U, V, W, X);
on_conflict_tuples!(U, V, W, X, Y);
on_conflict_tuples!(U, V, W, X, Y, Z);
<commit_msg>Excute rustfmt<commit_after>use crate::backend::{Backend, SupportsOnConflictClause};
use crate::expression::SqlLiteral;
use crate::query_builder::*;
use crate::query_source::Column;
use crate::result::QueryResult;

#[cfg(feature = "postgres")]
/// Used to specify the constraint name for an upsert statement in the form `ON
/// CONFLICT ON CONSTRAINT`. Note that `constraint_name` must be the name of a
/// unique constraint, not the name of an index.
///
/// # Example
///
/// ```rust
/// # include!("on_conflict_docs_setup.rs");
/// #
/// # fn main() {
/// #     use self::users::dsl::*;
/// use diesel::upsert::*;
///
/// #     let conn = establish_connection();
/// #     conn.execute("TRUNCATE TABLE users").unwrap();
/// conn.execute("ALTER TABLE users ADD CONSTRAINT users_name UNIQUE (name)").unwrap();
/// let user = User { id: 1, name: "Sean", };
/// let same_name_different_id = User { id: 2, name: "Sean" };
/// let same_id_different_name = User { id: 1, name: "Pascal" };
///
/// assert_eq!(Ok(1), diesel::insert_into(users).values(&user).execute(&conn));
///
/// let inserted_row_count = diesel::insert_into(users)
///     .values(&same_name_different_id)
///     .on_conflict(on_constraint("users_name"))
///     .do_nothing()
///     .execute(&conn);
/// assert_eq!(Ok(0), inserted_row_count);
///
/// let pk_conflict_result = diesel::insert_into(users)
///     .values(&same_id_different_name)
///     .on_conflict(on_constraint("users_name"))
///     .do_nothing()
///     .execute(&conn);
/// assert!(pk_conflict_result.is_err());
/// # }
/// ```
#[cfg(feature = "postgres")]
pub fn on_constraint(constraint_name: &str) -> OnConstraint {
    OnConstraint {
        constraint_name: constraint_name,
    }
}

#[doc(hidden)]
#[derive(Debug, Clone, Copy)]
pub struct OnConstraint<'a> {
    constraint_name: &'a str,
}

pub trait OnConflictTarget<Table> {}

#[doc(hidden)]
#[derive(Debug, Clone, Copy)]
pub struct NoConflictTarget;

impl<DB: Backend + SupportsOnConflictClause> QueryFragment<DB> for NoConflictTarget {
    fn walk_ast(&self, _: AstPass<DB>) -> QueryResult<()> {
        Ok(())
    }
}

impl<Table> OnConflictTarget<Table> for NoConflictTarget {}

#[doc(hidden)]
#[derive(Debug, Clone, Copy)]
pub struct ConflictTarget<T>(pub T);

impl<DB, T> QueryFragment<DB> for ConflictTarget<T>
where
    DB: Backend + SupportsOnConflictClause,
    T: Column,
{
    fn walk_ast(&self, mut out: AstPass<DB>) -> QueryResult<()> {
        out.push_sql(" (");
        out.push_identifier(T::NAME)?;
        out.push_sql(")");
        Ok(())
    }
}

impl<T> OnConflictTarget<T::Table> for ConflictTarget<T>
where
    T: Column,
{
}

impl<DB, ST> QueryFragment<DB> for ConflictTarget<SqlLiteral<ST>>
where
    DB: Backend + SupportsOnConflictClause,
    SqlLiteral<ST>: QueryFragment<DB>,
{
    fn walk_ast(&self, mut out: AstPass<DB>) -> QueryResult<()> {
        out.push_sql(" ");
        self.0.walk_ast(out.reborrow())?;
        Ok(())
    }
}

impl<Tab, ST> OnConflictTarget<Tab> for ConflictTarget<SqlLiteral<ST>> {}

impl<'a, DB> QueryFragment<DB> for ConflictTarget<OnConstraint<'a>>
where
    DB: Backend + SupportsOnConflictClause,
{
    fn walk_ast(&self, mut out: AstPass<DB>) -> QueryResult<()> {
        out.push_sql(" ON CONSTRAINT ");
        out.push_identifier(self.0.constraint_name)?;
        Ok(())
    }
}

impl<'a, Table> OnConflictTarget<Table> for ConflictTarget<OnConstraint<'a>> {}

macro_rules! on_conflict_tuples {
    ($($col:ident),+) => {
        impl<DB, T, $($col),+> QueryFragment<DB> for ConflictTarget<(T, $($col),+)> where
            DB: Backend + SupportsOnConflictClause,
            T: Column,
            $($col: Column<Table=T::Table>,)+
        {
            fn walk_ast(&self, mut out: AstPass<DB>) -> QueryResult<()> {
                out.push_sql(" (");
                out.push_identifier(T::NAME)?;
                $(
                    out.push_sql(", ");
                    out.push_identifier($col::NAME)?;
                )+
                out.push_sql(")");
                Ok(())
            }
        }

        impl<T, $($col),+> OnConflictTarget<T::Table> for ConflictTarget<(T, $($col),+)> where
            T: Column,
            $($col: Column<Table=T::Table>,)+
        {
        }
    }
}

on_conflict_tuples!(U);
on_conflict_tuples!(U, V);
on_conflict_tuples!(U, V, W);
on_conflict_tuples!(U, V, W, X);
on_conflict_tuples!(U, V, W, X, Y);
on_conflict_tuples!(U, V, W, X, Y, Z);
<|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/. */

<%namespace name="helpers" file="/helpers.mako.rs" />
<% from data import Method %>

// CSS Basic User Interface Module Level 1
// https://drafts.csswg.org/css-ui-3/
<% data.new_style_struct("UI", inherited=False, gecko_name="UIReset") %>

// TODO spec says that UAs should not support this
// we should probably remove from gecko (https://bugzilla.mozilla.org/show_bug.cgi?id=1328331)
${helpers.single_keyword("ime-mode", "auto normal active disabled inactive",
                         products="gecko", gecko_ffi_name="mIMEMode",
                         animation_value_type="discrete",
                         spec="https://drafts.csswg.org/css-ui/#input-method-editor")}

${helpers.single_keyword("-moz-user-select", "auto text none all element elements" +
                            " toggle tri-state -moz-all -moz-text",
                         products="gecko",
                         alias="-webkit-user-select",
                         gecko_ffi_name="mUserSelect",
                         gecko_enum_prefix="StyleUserSelect",
                         gecko_strip_moz_prefix=False,
                         aliases="-moz-none=none",
                         animation_value_type="discrete",
                         spec="https://drafts.csswg.org/css-ui-4/#propdef-user-select")}

// TODO(emilio): This probably should be hidden from content.
${helpers.single_keyword("-moz-window-dragging", "default drag no-drag", products="gecko",
                         gecko_ffi_name="mWindowDragging",
                         gecko_enum_prefix="StyleWindowDragging",
                         animation_value_type="discrete",
                         spec="None (Nonstandard Firefox-only property)")}

${helpers.single_keyword("-moz-window-shadow", "none default menu tooltip sheet", products="gecko",
                         gecko_ffi_name="mWindowShadow",
                         gecko_constant_prefix="NS_STYLE_WINDOW_SHADOW",
                         animation_value_type="discrete",
                         enabled_in="chrome",
                         spec="None (Nonstandard internal property)")}

// TODO(bug 1419695) This should be hidden from content.
${helpers.predefined_type("-moz-window-opacity", "Opacity", "1.0", products="gecko",
                          gecko_ffi_name="mWindowOpacity",
                          animation_value_type="ComputedValue",
                          spec="None (Nonstandard internal property)")}

// TODO(bug 1419695) This should be hidden from content.
${helpers.predefined_type(
    "-moz-window-transform",
    "Transform",
    "generics::transform::Transform::none()",
    products="gecko",
    gecko_ffi_name="mSpecifiedWindowTransform",
    flags="GETCS_NEEDS_LAYOUT_FLUSH",
    animation_value_type="ComputedValue",
    spec="None (Nonstandard internal property)"
)}

// TODO(bug 1419695) This should be hidden from content.
${helpers.predefined_type(
    "-moz-window-transform-origin",
    "TransformOrigin",
    "computed::TransformOrigin::initial_value()",
    animation_value_type="ComputedValue",
    gecko_ffi_name="mWindowTransformOrigin",
    products="gecko",
    boxed=True,
    flags="GETCS_NEEDS_LAYOUT_FLUSH",
    spec="None (Nonstandard internal property)"
)}

// TODO(emilio): Probably also should be hidden from content.
${helpers.predefined_type("-moz-force-broken-image-icon",
                          "MozForceBrokenImageIcon",
                          "computed::MozForceBrokenImageIcon::false_value()",
                          animation_value_type="discrete",
                          products="gecko",
                          spec="None (Nonstandard Firefox-only property)")}
<commit_msg>style: Hide multiple -moz-window-* properties from content.<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/. */

<%namespace name="helpers" file="/helpers.mako.rs" />
<% from data import Method %>

// CSS Basic User Interface Module Level 1
// https://drafts.csswg.org/css-ui-3/
<% data.new_style_struct("UI", inherited=False, gecko_name="UIReset") %>

// TODO spec says that UAs should not support this
// we should probably remove from gecko (https://bugzilla.mozilla.org/show_bug.cgi?id=1328331)
${helpers.single_keyword("ime-mode", "auto normal active disabled inactive",
                         products="gecko", gecko_ffi_name="mIMEMode",
                         animation_value_type="discrete",
                         spec="https://drafts.csswg.org/css-ui/#input-method-editor")}

${helpers.single_keyword("-moz-user-select", "auto text none all element elements" +
                            " toggle tri-state -moz-all -moz-text",
                         products="gecko",
                         alias="-webkit-user-select",
                         gecko_ffi_name="mUserSelect",
                         gecko_enum_prefix="StyleUserSelect",
                         gecko_strip_moz_prefix=False,
                         aliases="-moz-none=none",
                         animation_value_type="discrete",
                         spec="https://drafts.csswg.org/css-ui-4/#propdef-user-select")}

// TODO(emilio): This probably should be hidden from content.
${helpers.single_keyword("-moz-window-dragging", "default drag no-drag", products="gecko",
                         gecko_ffi_name="mWindowDragging",
                         gecko_enum_prefix="StyleWindowDragging",
                         animation_value_type="discrete",
                         spec="None (Nonstandard Firefox-only property)")}

${helpers.single_keyword("-moz-window-shadow", "none default menu tooltip sheet", products="gecko",
                         gecko_ffi_name="mWindowShadow",
                         gecko_constant_prefix="NS_STYLE_WINDOW_SHADOW",
                         animation_value_type="discrete",
                         enabled_in="chrome",
                         spec="None (Nonstandard internal property)")}

${helpers.predefined_type(
    "-moz-window-opacity",
    "Opacity",
    "1.0",
    products="gecko",
    gecko_ffi_name="mWindowOpacity",
    animation_value_type="ComputedValue",
    spec="None (Nonstandard internal property)",
    enabled_in="chrome",
)}

${helpers.predefined_type(
    "-moz-window-transform",
    "Transform",
    "generics::transform::Transform::none()",
    products="gecko",
    gecko_ffi_name="mSpecifiedWindowTransform",
    flags="GETCS_NEEDS_LAYOUT_FLUSH",
    animation_value_type="ComputedValue",
    spec="None (Nonstandard internal property)",
    enabled_in="chrome",
)}

${helpers.predefined_type(
    "-moz-window-transform-origin",
    "TransformOrigin",
    "computed::TransformOrigin::initial_value()",
    animation_value_type="ComputedValue",
    gecko_ffi_name="mWindowTransformOrigin",
    products="gecko",
    boxed=True,
    flags="GETCS_NEEDS_LAYOUT_FLUSH",
    spec="None (Nonstandard internal property)",
    enabled_in="chrome",
)}

// TODO(emilio): Probably also should be hidden from content.
${helpers.predefined_type("-moz-force-broken-image-icon",
                          "MozForceBrokenImageIcon",
                          "computed::MozForceBrokenImageIcon::false_value()",
                          animation_value_type="discrete",
                          products="gecko",
                          spec="None (Nonstandard Firefox-only property)")}
<|endoftext|>


<commit_before>//! Complete fields in record literals and patterns.
use ra_syntax::{ast, ast::NameOwner, SmolStr};

use crate::completion::{CompletionContext, Completions};

pub(super) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
    let (ty, variant, already_present_fields) =
        match (ctx.record_lit_pat.as_ref(), ctx.record_lit_syntax.as_ref()) {
            (None, None) => return None,
            (Some(_), Some(_)) => panic!("A record cannot be both a literal and a pattern"),
            (Some(record_pat), _) => (
                ctx.sema.type_of_pat(&record_pat.clone().into())?,
                ctx.sema.resolve_record_pattern(record_pat)?,
                pattern_ascribed_fields(record_pat),
            ),
            (_, Some(record_lit)) => (
                ctx.sema.type_of_expr(&record_lit.clone().into())?,
                ctx.sema.resolve_record_literal(record_lit)?,
                literal_ascribed_fields(record_lit),
            ),
        };

    for (field, field_ty) in ty.variant_fields(ctx.db, variant).into_iter().filter(|(field, _)| {
        // FIXME: already_present_names better be `Vec<hir::Name>`
        !already_present_fields.contains(&SmolStr::from(field.name(ctx.db).to_string()))
    }) {
        acc.add_field(ctx, field, &field_ty);
    }
    Some(())
}

fn literal_ascribed_fields(record_lit: &ast::RecordLit) -> Vec<SmolStr> {
    record_lit
        .record_field_list()
        .map(|field_list| field_list.fields())
        .map(|fields| {
            fields
                .into_iter()
                .filter_map(|field| field.name_ref())
                .map(|name_ref| name_ref.text().clone())
                .collect()
        })
        .unwrap_or_default()
}

fn pattern_ascribed_fields(record_pat: &ast::RecordPat) -> Vec<SmolStr> {
    record_pat
        .record_field_pat_list()
        .map(|pat_list| {
            pat_list
                .record_field_pats()
                .filter_map(|fild_pat| fild_pat.name())
                .chain(pat_list.bind_pats().filter_map(|bind_pat| bind_pat.name()))
                .map(|name| name.text().clone())
                .collect()
        })
        .unwrap_or_default()
}

#[cfg(test)]
mod tests {
    mod record_lit_tests {
        use insta::assert_debug_snapshot;

        use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};

        fn complete(code: &str) -> Vec<CompletionItem> {
            do_completion(code, CompletionKind::Reference)
        }

        #[test]
        fn test_record_pattern_field() {
            let completions = complete(
                r"
            struct S { foo: u32 }

            fn process(f: S) {
                match f {
                    S { f<|>: 92 } => (),
                }
            }
            ",
            );
            assert_debug_snapshot!(completions, @r###"
        [
            CompletionItem {
                label: "foo",
                source_range: [117; 118),
                delete: [117; 118),
                insert: "foo",
                kind: Field,
                detail: "u32",
            },
        ]
        "###);
        }

        #[test]
        fn test_record_pattern_enum_variant() {
            let completions = complete(
                r"
            enum E {
                S { foo: u32, bar: () }
            }

            fn process(e: E) {
                match e {
                    E::S { <|> } => (),
                }
            }
            ",
            );
            assert_debug_snapshot!(completions, @r###"
        [
            CompletionItem {
                label: "bar",
                source_range: [161; 161),
                delete: [161; 161),
                insert: "bar",
                kind: Field,
                detail: "()",
            },
            CompletionItem {
                label: "foo",
                source_range: [161; 161),
                delete: [161; 161),
                insert: "foo",
                kind: Field,
                detail: "u32",
            },
        ]
        "###);
        }

        #[test]
        fn test_record_pattern_field_in_simple_macro() {
            let completions = complete(
                r"
            macro_rules! m { ($e:expr) => { $e } }
            struct S { foo: u32 }

            fn process(f: S) {
                m!(match f {
                    S { f<|>: 92 } => (),
                })
            }
            ",
            );
            assert_debug_snapshot!(completions, @r###"
        [
            CompletionItem {
                label: "foo",
                source_range: [171; 172),
                delete: [171; 172),
                insert: "foo",
                kind: Field,
                detail: "u32",
            },
        ]
        "###);
        }

        #[test]
        fn only_missing_fields_are_completed_in_destruct_pats() {
            let completions = complete(
                r"
            struct S {
                foo1: u32,
                foo2: u32,
                bar: u32,
                baz: u32,
            }

            fn main() {
                let s = S {
                    foo1: 1,
                    foo2: 2,
                    bar: 3,
                    baz: 4,
                };
                if let S { foo1, foo2: a, <|> } = s {}
            }
            ",
            );
            assert_debug_snapshot!(completions, @r###"
        [
            CompletionItem {
                label: "bar",
                source_range: [372; 372),
                delete: [372; 372),
                insert: "bar",
                kind: Field,
                detail: "u32",
            },
            CompletionItem {
                label: "baz",
                source_range: [372; 372),
                delete: [372; 372),
                insert: "baz",
                kind: Field,
                detail: "u32",
            },
        ]
        "###);
        }
    }

    mod record_pat_tests {
        use insta::assert_debug_snapshot;

        use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};

        fn complete(code: &str) -> Vec<CompletionItem> {
            do_completion(code, CompletionKind::Reference)
        }

        #[test]
        fn test_record_literal_deprecated_field() {
            let completions = complete(
                r"
            struct A {
                #[deprecated]
                the_field: u32,
            }
            fn foo() {
               A { the<|> }
            }
            ",
            );
            assert_debug_snapshot!(completions, @r###"
        [
            CompletionItem {
                label: "the_field",
                source_range: [142; 145),
                delete: [142; 145),
                insert: "the_field",
                kind: Field,
                detail: "u32",
                deprecated: true,
            },
        ]
        "###);
        }

        #[test]
        fn test_record_literal_field() {
            let completions = complete(
                r"
            struct A { the_field: u32 }
            fn foo() {
               A { the<|> }
            }
            ",
            );
            assert_debug_snapshot!(completions, @r###"
        [
            CompletionItem {
                label: "the_field",
                source_range: [83; 86),
                delete: [83; 86),
                insert: "the_field",
                kind: Field,
                detail: "u32",
            },
        ]
        "###);
        }

        #[test]
        fn test_record_literal_enum_variant() {
            let completions = complete(
                r"
            enum E {
                A { a: u32 }
            }
            fn foo() {
                let _ = E::A { <|> }
            }
            ",
            );
            assert_debug_snapshot!(completions, @r###"
        [
            CompletionItem {
                label: "a",
                source_range: [119; 119),
                delete: [119; 119),
                insert: "a",
                kind: Field,
                detail: "u32",
            },
        ]
        "###);
        }

        #[test]
        fn test_record_literal_two_structs() {
            let completions = complete(
                r"
            struct A { a: u32 }
            struct B { b: u32 }

            fn foo() {
               let _: A = B { <|> }
            }
            ",
            );
            assert_debug_snapshot!(completions, @r###"
        [
            CompletionItem {
                label: "b",
                source_range: [119; 119),
                delete: [119; 119),
                insert: "b",
                kind: Field,
                detail: "u32",
            },
        ]
        "###);
        }

        #[test]
        fn test_record_literal_generic_struct() {
            let completions = complete(
                r"
            struct A<T> { a: T }

            fn foo() {
               let _: A<u32> = A { <|> }
            }
            ",
            );
            assert_debug_snapshot!(completions, @r###"
        [
            CompletionItem {
                label: "a",
                source_range: [93; 93),
                delete: [93; 93),
                insert: "a",
                kind: Field,
                detail: "u32",
            },
        ]
        "###);
        }

        #[test]
        fn test_record_literal_field_in_simple_macro() {
            let completions = complete(
                r"
            macro_rules! m { ($e:expr) => { $e } }
            struct A { the_field: u32 }
            fn foo() {
               m!(A { the<|> })
            }
            ",
            );
            assert_debug_snapshot!(completions, @r###"
        [
            CompletionItem {
                label: "the_field",
                source_range: [137; 140),
                delete: [137; 140),
                insert: "the_field",
                kind: Field,
                detail: "u32",
            },
        ]
        "###);
        }

        #[test]
        fn only_missing_fields_are_completed() {
            let completions = complete(
                r"
            struct S {
                foo1: u32,
                foo2: u32,
                bar: u32,
                baz: u32,
            }

            fn main() {
                let foo1 = 1;
                let s = S {
                    foo1,
                    foo2: 5,
                    <|>
                }
            }
            ",
            );
            assert_debug_snapshot!(completions, @r###"
        [
            CompletionItem {
                label: "bar",
                source_range: [302; 302),
                delete: [302; 302),
                insert: "bar",
                kind: Field,
                detail: "u32",
            },
            CompletionItem {
                label: "baz",
                source_range: [302; 302),
                delete: [302; 302),
                insert: "baz",
                kind: Field,
                detail: "u32",
            },
        ]
        "###);
        }
    }
}
<commit_msg>A more precise panic macro<commit_after>//! Complete fields in record literals and patterns.
use ra_syntax::{ast, ast::NameOwner, SmolStr};

use crate::completion::{CompletionContext, Completions};

pub(super) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
    let (ty, variant, already_present_fields) =
        match (ctx.record_lit_pat.as_ref(), ctx.record_lit_syntax.as_ref()) {
            (None, None) => return None,
            (Some(_), Some(_)) => unreachable!("A record cannot be both a literal and a pattern"),
            (Some(record_pat), _) => (
                ctx.sema.type_of_pat(&record_pat.clone().into())?,
                ctx.sema.resolve_record_pattern(record_pat)?,
                pattern_ascribed_fields(record_pat),
            ),
            (_, Some(record_lit)) => (
                ctx.sema.type_of_expr(&record_lit.clone().into())?,
                ctx.sema.resolve_record_literal(record_lit)?,
                literal_ascribed_fields(record_lit),
            ),
        };

    for (field, field_ty) in ty.variant_fields(ctx.db, variant).into_iter().filter(|(field, _)| {
        // FIXME: already_present_names better be `Vec<hir::Name>`
        !already_present_fields.contains(&SmolStr::from(field.name(ctx.db).to_string()))
    }) {
        acc.add_field(ctx, field, &field_ty);
    }
    Some(())
}

fn literal_ascribed_fields(record_lit: &ast::RecordLit) -> Vec<SmolStr> {
    record_lit
        .record_field_list()
        .map(|field_list| field_list.fields())
        .map(|fields| {
            fields
                .into_iter()
                .filter_map(|field| field.name_ref())
                .map(|name_ref| name_ref.text().clone())
                .collect()
        })
        .unwrap_or_default()
}

fn pattern_ascribed_fields(record_pat: &ast::RecordPat) -> Vec<SmolStr> {
    record_pat
        .record_field_pat_list()
        .map(|pat_list| {
            pat_list
                .record_field_pats()
                .filter_map(|fild_pat| fild_pat.name())
                .chain(pat_list.bind_pats().filter_map(|bind_pat| bind_pat.name()))
                .map(|name| name.text().clone())
                .collect()
        })
        .unwrap_or_default()
}

#[cfg(test)]
mod tests {
    mod record_lit_tests {
        use insta::assert_debug_snapshot;

        use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};

        fn complete(code: &str) -> Vec<CompletionItem> {
            do_completion(code, CompletionKind::Reference)
        }

        #[test]
        fn test_record_pattern_field() {
            let completions = complete(
                r"
            struct S { foo: u32 }

            fn process(f: S) {
                match f {
                    S { f<|>: 92 } => (),
                }
            }
            ",
            );
            assert_debug_snapshot!(completions, @r###"
        [
            CompletionItem {
                label: "foo",
                source_range: [117; 118),
                delete: [117; 118),
                insert: "foo",
                kind: Field,
                detail: "u32",
            },
        ]
        "###);
        }

        #[test]
        fn test_record_pattern_enum_variant() {
            let completions = complete(
                r"
            enum E {
                S { foo: u32, bar: () }
            }

            fn process(e: E) {
                match e {
                    E::S { <|> } => (),
                }
            }
            ",
            );
            assert_debug_snapshot!(completions, @r###"
        [
            CompletionItem {
                label: "bar",
                source_range: [161; 161),
                delete: [161; 161),
                insert: "bar",
                kind: Field,
                detail: "()",
            },
            CompletionItem {
                label: "foo",
                source_range: [161; 161),
                delete: [161; 161),
                insert: "foo",
                kind: Field,
                detail: "u32",
            },
        ]
        "###);
        }

        #[test]
        fn test_record_pattern_field_in_simple_macro() {
            let completions = complete(
                r"
            macro_rules! m { ($e:expr) => { $e } }
            struct S { foo: u32 }

            fn process(f: S) {
                m!(match f {
                    S { f<|>: 92 } => (),
                })
            }
            ",
            );
            assert_debug_snapshot!(completions, @r###"
        [
            CompletionItem {
                label: "foo",
                source_range: [171; 172),
                delete: [171; 172),
                insert: "foo",
                kind: Field,
                detail: "u32",
            },
        ]
        "###);
        }

        #[test]
        fn only_missing_fields_are_completed_in_destruct_pats() {
            let completions = complete(
                r"
            struct S {
                foo1: u32,
                foo2: u32,
                bar: u32,
                baz: u32,
            }

            fn main() {
                let s = S {
                    foo1: 1,
                    foo2: 2,
                    bar: 3,
                    baz: 4,
                };
                if let S { foo1, foo2: a, <|> } = s {}
            }
            ",
            );
            assert_debug_snapshot!(completions, @r###"
        [
            CompletionItem {
                label: "bar",
                source_range: [372; 372),
                delete: [372; 372),
                insert: "bar",
                kind: Field,
                detail: "u32",
            },
            CompletionItem {
                label: "baz",
                source_range: [372; 372),
                delete: [372; 372),
                insert: "baz",
                kind: Field,
                detail: "u32",
            },
        ]
        "###);
        }
    }

    mod record_pat_tests {
        use insta::assert_debug_snapshot;

        use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};

        fn complete(code: &str) -> Vec<CompletionItem> {
            do_completion(code, CompletionKind::Reference)
        }

        #[test]
        fn test_record_literal_deprecated_field() {
            let completions = complete(
                r"
            struct A {
                #[deprecated]
                the_field: u32,
            }
            fn foo() {
               A { the<|> }
            }
            ",
            );
            assert_debug_snapshot!(completions, @r###"
        [
            CompletionItem {
                label: "the_field",
                source_range: [142; 145),
                delete: [142; 145),
                insert: "the_field",
                kind: Field,
                detail: "u32",
                deprecated: true,
            },
        ]
        "###);
        }

        #[test]
        fn test_record_literal_field() {
            let completions = complete(
                r"
            struct A { the_field: u32 }
            fn foo() {
               A { the<|> }
            }
            ",
            );
            assert_debug_snapshot!(completions, @r###"
        [
            CompletionItem {
                label: "the_field",
                source_range: [83; 86),
                delete: [83; 86),
                insert: "the_field",
                kind: Field,
                detail: "u32",
            },
        ]
        "###);
        }

        #[test]
        fn test_record_literal_enum_variant() {
            let completions = complete(
                r"
            enum E {
                A { a: u32 }
            }
            fn foo() {
                let _ = E::A { <|> }
            }
            ",
            );
            assert_debug_snapshot!(completions, @r###"
        [
            CompletionItem {
                label: "a",
                source_range: [119; 119),
                delete: [119; 119),
                insert: "a",
                kind: Field,
                detail: "u32",
            },
        ]
        "###);
        }

        #[test]
        fn test_record_literal_two_structs() {
            let completions = complete(
                r"
            struct A { a: u32 }
            struct B { b: u32 }

            fn foo() {
               let _: A = B { <|> }
            }
            ",
            );
            assert_debug_snapshot!(completions, @r###"
        [
            CompletionItem {
                label: "b",
                source_range: [119; 119),
                delete: [119; 119),
                insert: "b",
                kind: Field,
                detail: "u32",
            },
        ]
        "###);
        }

        #[test]
        fn test_record_literal_generic_struct() {
            let completions = complete(
                r"
            struct A<T> { a: T }

            fn foo() {
               let _: A<u32> = A { <|> }
            }
            ",
            );
            assert_debug_snapshot!(completions, @r###"
        [
            CompletionItem {
                label: "a",
                source_range: [93; 93),
                delete: [93; 93),
                insert: "a",
                kind: Field,
                detail: "u32",
            },
        ]
        "###);
        }

        #[test]
        fn test_record_literal_field_in_simple_macro() {
            let completions = complete(
                r"
            macro_rules! m { ($e:expr) => { $e } }
            struct A { the_field: u32 }
            fn foo() {
               m!(A { the<|> })
            }
            ",
            );
            assert_debug_snapshot!(completions, @r###"
        [
            CompletionItem {
                label: "the_field",
                source_range: [137; 140),
                delete: [137; 140),
                insert: "the_field",
                kind: Field,
                detail: "u32",
            },
        ]
        "###);
        }

        #[test]
        fn only_missing_fields_are_completed() {
            let completions = complete(
                r"
            struct S {
                foo1: u32,
                foo2: u32,
                bar: u32,
                baz: u32,
            }

            fn main() {
                let foo1 = 1;
                let s = S {
                    foo1,
                    foo2: 5,
                    <|>
                }
            }
            ",
            );
            assert_debug_snapshot!(completions, @r###"
        [
            CompletionItem {
                label: "bar",
                source_range: [302; 302),
                delete: [302; 302),
                insert: "bar",
                kind: Field,
                detail: "u32",
            },
            CompletionItem {
                label: "baz",
                source_range: [302; 302),
                delete: [302; 302),
                insert: "baz",
                kind: Field,
                detail: "u32",
            },
        ]
        "###);
        }
    }
}
<|endoftext|>