<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 {
    #[fixed_stack_segment]
    fn drop(&mut 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})
        }
    }
}
<commit_msg>Updated for most recent rust: made functions explicitly public, and switched from transmute() to FromPrimitive::from_int() for the cast to enum<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::num::FromPrimitive;
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" {
        pub fn TTF_Init() -> c_int;
        pub fn TTF_WasInit() -> c_int;
        pub fn TTF_Quit();
        pub fn TTF_OpenFont(file: *c_char, ptsize: c_int) -> *TTF_Font;
        pub fn TTF_OpenFontIndex(file: *c_char, ptsize: c_int, index: c_long)
            -> *TTF_Font;
        pub fn TTF_CloseFont(font: *TTF_Font);

        pub fn TTF_GetFontStyle(font: *TTF_Font) -> TTF_StyleFlag;
        pub fn TTF_SetFontStyle(font: *TTF_Font, style: TTF_StyleFlag);
        pub fn TTF_GetFontOutline(font: *TTF_Font) -> c_int;
        pub fn TTF_SetFontOutline(font: *TTF_Font, outline: c_int);
        pub fn TTF_GetFontHinting(font: *TTF_Font) -> TTF_Hinting;
        pub fn TTF_SetFontHinting(font: *TTF_Font, hinting: TTF_Hinting);
        pub fn TTF_GetFontKerning(font: *TTF_Font) -> c_int;
        pub fn TTF_SetFontKerning(font: *TTF_Font, kerning: c_int);
        pub fn TTF_FontHeight(font: *TTF_Font) -> c_int;
        pub fn TTF_FontAscent(font: *TTF_Font) -> c_int;
        pub fn TTF_FontDescent(font: *TTF_Font) -> c_int;
        pub fn TTF_FontLineSkip(font: *TTF_Font) -> c_int;
        pub fn TTF_FontFaces(font: *TTF_Font) -> c_long;
        pub fn TTF_FontFaceIsFixedWidth(font: *TTF_Font) -> c_int;
        pub fn TTF_FontFaceFamilyName(font: *TTF_Font) -> *c_char;
        pub fn TTF_FontGlyphIsProvided(font: *TTF_Font, glyph: u16) -> c_int;
        pub 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;
        pub fn TTF_SizeUTF8(font: *TTF_Font, text: *c_char, w: *mut c_int,
            h: *mut c_int) -> c_int;

        pub fn TTF_RenderUTF8_Solid(font: *TTF_Font, text: *c_char,
            fg: SDL_Color) -> *SDL_Surface;
        pub fn TTF_RenderUTF8_Shaded(font: *TTF_Font, text: *c_char,
            fg: SDL_Color, bg: SDL_Color) -> *SDL_Surface;
        pub 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
}

#[deriving(FromPrimitive)]
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(&mut 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 {
            FromPrimitive::from_int(ffi::TTF_GetFontHinting(self.raw) as int).unwrap()
        }
    }

    #[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::*;
use std::iter::*;
use std::sync::atomic::*;
use rand::{Rng, XorShiftRng};
use types::*;
use config::*;
use player::*;
use field::*;

#[unsafe_no_drop_flag]
struct UctNode {
  wins: AtomicUsize,
  draws: AtomicUsize,
  visits: AtomicUsize,
  pos: Pos,
  child: AtomicPtr<UctNode>,
  sibling: Option<Box<UctNode>>
}

unsafe impl Send for UctNode { }

impl Drop for UctNode {
  fn drop(&mut self) {
    self.clear_child();
  }
}

impl UctNode {
  pub fn new(pos: Pos) -> UctNode {
    UctNode {
      wins: AtomicUsize::new(0),
      draws: AtomicUsize::new(0),
      visits: AtomicUsize::new(0),
      pos: pos,
      child: AtomicPtr::new(ptr::null_mut()),
      sibling: None
    }
  }

  pub fn get_pos(&self) -> Pos {
    self.pos
  }

  pub fn set_pos(&mut self, pos: Pos) {
    self.pos = pos;
  }

  pub fn get_sibling(&mut self) -> Option<Box<UctNode>> {
    mem::replace(&mut self.sibling, None)
  }

  pub fn get_sibling_ref<'a>(&'a self) -> Option<&'a UctNode> {
    self.sibling.as_ref().map(|b| &**b)
  }

  pub fn get_sibling_mut<'a>(&'a mut self) -> Option<&'a mut UctNode> {
    self.sibling.as_mut().map(|b| &mut **b)
  }

  pub fn clear_sibling(&mut self) {
    self.sibling = None;
  }

  pub fn set_sibling(&mut self, sibling: Box<UctNode>) {
    self.sibling = Some(sibling);
  }

  pub fn get_child(&self) -> Option<Box<UctNode>> {
    let ptr = self.child.swap(ptr::null_mut(), Ordering::Relaxed);
    if !ptr.is_null() {
      Some(unsafe { mem::transmute(ptr) })
    } else {
      None
    }
  }

  pub fn get_child_ref<'a>(&'a self) -> Option<&'a UctNode> {
    let ptr = self.child.load(Ordering::Relaxed);
    if !ptr.is_null() {
      Some(unsafe { &*ptr })
    } else {
      None
    }
  }

  pub fn get_child_mut<'a>(&'a mut self) -> Option<&'a mut UctNode> {
    let ptr = self.child.load(Ordering::Relaxed);
    if !ptr.is_null() {
      Some(unsafe { &mut *ptr })
    } else {
      None
    }
  }

  pub fn clear_child(&self) {
    let ptr = self.child.swap(ptr::null_mut(), Ordering::Relaxed);
    if !ptr.is_null() {
      drop::<Box<UctNode>>(unsafe { mem::transmute(ptr) });
    }
  }

  pub fn set_child(&self, child: Box<UctNode>) {
    let ptr = self.child.swap(unsafe { mem::transmute(child) }, Ordering::Relaxed);
    if !ptr.is_null() {
      drop::<Box<UctNode>>(unsafe { mem::transmute(ptr) });
    }
  }

  pub fn set_child_if_empty(&self, child: Box<UctNode>) {
    let child_ptr = unsafe { mem::transmute(child) };
    let ptr = self.child.compare_and_swap(ptr::null_mut(), child_ptr, Ordering::Relaxed);
    if !ptr.is_null() {
      drop::<Box<UctNode>>(unsafe { mem::transmute(child_ptr) });
    }
  }

  pub fn get_visits(&self) -> usize {
    self.visits.load(Ordering::Relaxed)
  }

  pub fn get_wins(&self) -> usize {
    self.wins.load(Ordering::Relaxed)
  }

  pub fn get_draws(&self) -> usize {
    self.draws.load(Ordering::Relaxed)
  }

  pub fn add_win(&self) {
    self.visits.fetch_add(1, Ordering::Relaxed);
    self.wins.fetch_add(1, Ordering::Relaxed);
  }

  pub fn add_draw(&self) {
    self.visits.fetch_add(1, Ordering::Relaxed);
    self.draws.fetch_add(1, Ordering::Relaxed);
  }

  pub fn add_loose(&self) {
    self.visits.fetch_add(1, Ordering::Relaxed);
  }

  pub fn clear_stats(&self) {
    self.wins.store(0, Ordering::Relaxed);
    self.draws.store(0, Ordering::Relaxed);
    self.visits.store(0, Ordering::Relaxed);
  }
}

pub struct UctRoot {
  node: Option<Box<UctNode>>,
  moves: Vec<Pos>,
  moves_field: Vec<Pos>,
  player: Player,
  moves_count: usize,
  hash: u64
}

impl UctRoot {
  fn clear(&mut self) {
    self.node = None;
    self.moves.clear();
    for i in self.moves_field.iter_mut() {
      *i = 0;
    }
    self.player = Player::Red;
    self.moves_count = 0;
    self.hash = 0;
  }

  fn init(&mut self, field: &Field, player: Player) {
    self.node = Some(Box::new(UctNode::new(0)));
    self.player = player;
    let width = field.width();
    for &start_pos in field.points_seq() {
      wave(width, start_pos, |pos| {
        if self.moves_field[pos] != start_pos && field.is_putting_allowed(pos) && manhattan(width, start_pos, pos) <= UCT_RADIUS {
          if self.moves_field[pos] == 0 {
            self.moves.push(pos);
          }
          self.moves_field[pos] = start_pos;
          true
        } else {
          false
        }
      });
    }
  }

  fn expand_node(node: &mut UctNode, moves: &Vec<Pos>) {
    if node.get_child_ref().is_none() {
      if node.get_visits() == usize::max_value() {
        node.clear_stats();
      }
    } else {
      let mut next = node.get_child_mut();
      while next.as_ref().unwrap().get_sibling_ref().is_some() {
        UctRoot::expand_node(*next.as_mut().unwrap(), moves);
        next = next.unwrap().get_sibling_mut();
      }
      UctRoot::expand_node(*next.as_mut().unwrap(), moves);
      for &pos in moves {
        next.as_mut().unwrap().set_sibling(Box::new(UctNode::new(pos)));
        next = next.unwrap().get_sibling_mut();
      }
    }
  }

  fn update(&mut self, field: &Field, player: Player) {
    if self.node.is_some() && field.hash_at(self.moves_count) != Some(self.hash) {
      self.clear();
    }
    if self.node.is_none() {
      self.init(field, player);
    } else {
      let points_seq = field.points_seq();
      let moves_count = field.moves_count();
      loop {
        if self.moves_count == moves_count {
          break;
        }
        let next_pos = points_seq[self.moves_count];
        if !field.is_players_point(next_pos, self.player) {
          self.clear();
          self.init(field, player);
          break;
        }
        let mut next = self.node.as_ref().unwrap().get_child();
        while next.is_some() && next.as_ref().unwrap().pos != next_pos {
          next = next.unwrap().get_sibling();
        }
        match next.as_mut() {
          Some(node) => {
            node.clear_sibling();
          },
          None => {
            self.clear();
            self.init(field, player);
            break;
          }
        }
        self.node = next;
        let moves_field = &mut self.moves_field;
        self.moves.retain(|&pos| {
          if field.is_putting_allowed(pos) {
            true
          } else {
            moves_field[pos] = 0;
            false
          }
        });
        let mut added_moves = Vec::new();
        let width = field.width();
        wave(width, next_pos, |pos| {
          if moves_field[pos] != next_pos && field.is_putting_allowed(pos) && manhattan(width, next_pos, pos) <= UCT_RADIUS {
            if moves_field[pos] == 0 {
              added_moves.push(pos);
            }
            moves_field[pos] = next_pos;
            true
          } else {
            false
          }
        });
        UctRoot::expand_node(self.node.as_mut().unwrap(), &added_moves);
        self.moves_count += 1;
        self.player = self.player.next();
      }
    }
  }

  pub fn new(length: Pos) -> UctRoot {
    UctRoot {
      node: None,
      moves: Vec::with_capacity(length),
      moves_field: repeat(0).take(length).collect(),
      player: Player::Red,
      moves_count: 0,
      hash: 0
    }
  }

  fn play_simulation<T: Rng>(field: &mut Field, rng: &mut T) {
  }

  fn best_move_generic<T: Rng>(&mut self, field: &Field, player: Player, rng: &mut T, should_stop: &AtomicBool) -> Pos {
    self.update(field, player);
    let mut guards = Vec::with_capacity(4);
    for i in 0 .. 4 {
      let xor_shift_rng = rng.gen::<XorShiftRng>();
      guards.push(thread::scoped(|| {
        let mut local_field = field.clone();
        let mut local_rng = xor_shift_rng;
        while !should_stop.load(Ordering::Relaxed) {
          UctRoot::play_simulation(&mut local_field, &mut local_rng);
        }
      }));
    }
    0
  }

  pub fn best_move<T: Rng>(&mut self, field: &Field, player: Player, rng: &mut T, time: Time) -> Pos {
    let should_stop = AtomicBool::new(false);
    let guard = thread::scoped(|| {
      thread::sleep_ms(time);
      should_stop.store(true, Ordering::Relaxed);
    });
    let result = self.best_move_generic(field, player, rng, &should_stop);
    drop(guard);
    result
  }

  //pub fn estimates
}
<commit_msg>best_move_generic implemented.<commit_after>use std::*;
use std::iter::*;
use std::sync::atomic::*;
use rand::{Rng, XorShiftRng};
use types::*;
use config::*;
use player::*;
use field::*;

#[unsafe_no_drop_flag]
struct UctNode {
  wins: AtomicUsize,
  draws: AtomicUsize,
  visits: AtomicUsize,
  pos: Pos,
  child: AtomicPtr<UctNode>,
  sibling: Option<Box<UctNode>>
}

unsafe impl Send for UctNode { }

impl Drop for UctNode {
  fn drop(&mut self) {
    self.clear_child();
  }
}

impl UctNode {
  pub fn new(pos: Pos) -> UctNode {
    UctNode {
      wins: AtomicUsize::new(0),
      draws: AtomicUsize::new(0),
      visits: AtomicUsize::new(0),
      pos: pos,
      child: AtomicPtr::new(ptr::null_mut()),
      sibling: None
    }
  }

  pub fn get_pos(&self) -> Pos {
    self.pos
  }

  pub fn set_pos(&mut self, pos: Pos) {
    self.pos = pos;
  }

  pub fn get_sibling(&mut self) -> Option<Box<UctNode>> {
    mem::replace(&mut self.sibling, None)
  }

  pub fn get_sibling_ref<'a>(&'a self) -> Option<&'a UctNode> {
    self.sibling.as_ref().map(|b| &**b)
  }

  pub fn get_sibling_mut<'a>(&'a mut self) -> Option<&'a mut UctNode> {
    self.sibling.as_mut().map(|b| &mut **b)
  }

  pub fn clear_sibling(&mut self) {
    self.sibling = None;
  }

  pub fn set_sibling(&mut self, sibling: Box<UctNode>) {
    self.sibling = Some(sibling);
  }

  pub fn get_child(&self) -> Option<Box<UctNode>> {
    let ptr = self.child.swap(ptr::null_mut(), Ordering::Relaxed);
    if !ptr.is_null() {
      Some(unsafe { mem::transmute(ptr) })
    } else {
      None
    }
  }

  pub fn get_child_ref<'a>(&'a self) -> Option<&'a UctNode> {
    let ptr = self.child.load(Ordering::Relaxed);
    if !ptr.is_null() {
      Some(unsafe { &*ptr })
    } else {
      None
    }
  }

  pub fn get_child_mut<'a>(&'a mut self) -> Option<&'a mut UctNode> {
    let ptr = self.child.load(Ordering::Relaxed);
    if !ptr.is_null() {
      Some(unsafe { &mut *ptr })
    } else {
      None
    }
  }

  pub fn clear_child(&self) {
    let ptr = self.child.swap(ptr::null_mut(), Ordering::Relaxed);
    if !ptr.is_null() {
      drop::<Box<UctNode>>(unsafe { mem::transmute(ptr) });
    }
  }

  pub fn set_child(&self, child: Box<UctNode>) {
    let ptr = self.child.swap(unsafe { mem::transmute(child) }, Ordering::Relaxed);
    if !ptr.is_null() {
      drop::<Box<UctNode>>(unsafe { mem::transmute(ptr) });
    }
  }

  pub fn set_child_if_empty(&self, child: Box<UctNode>) {
    let child_ptr = unsafe { mem::transmute(child) };
    let ptr = self.child.compare_and_swap(ptr::null_mut(), child_ptr, Ordering::Relaxed);
    if !ptr.is_null() {
      drop::<Box<UctNode>>(unsafe { mem::transmute(child_ptr) });
    }
  }

  pub fn get_visits(&self) -> usize {
    self.visits.load(Ordering::Relaxed)
  }

  pub fn get_wins(&self) -> usize {
    self.wins.load(Ordering::Relaxed)
  }

  pub fn get_draws(&self) -> usize {
    self.draws.load(Ordering::Relaxed)
  }

  pub fn add_win(&self) {
    self.visits.fetch_add(1, Ordering::Relaxed);
    self.wins.fetch_add(1, Ordering::Relaxed);
  }

  pub fn add_draw(&self) {
    self.visits.fetch_add(1, Ordering::Relaxed);
    self.draws.fetch_add(1, Ordering::Relaxed);
  }

  pub fn add_loose(&self) {
    self.visits.fetch_add(1, Ordering::Relaxed);
  }

  pub fn clear_stats(&self) {
    self.wins.store(0, Ordering::Relaxed);
    self.draws.store(0, Ordering::Relaxed);
    self.visits.store(0, Ordering::Relaxed);
  }
}

pub struct UctRoot {
  node: Option<Box<UctNode>>,
  moves: Vec<Pos>,
  moves_field: Vec<Pos>,
  player: Player,
  moves_count: usize,
  hash: u64
}

impl UctRoot {
  fn clear(&mut self) {
    self.node = None;
    self.moves.clear();
    for i in self.moves_field.iter_mut() {
      *i = 0;
    }
    self.player = Player::Red;
    self.moves_count = 0;
    self.hash = 0;
  }

  fn init(&mut self, field: &Field, player: Player) {
    self.node = Some(Box::new(UctNode::new(0)));
    self.player = player;
    let width = field.width();
    for &start_pos in field.points_seq() {
      wave(width, start_pos, |pos| {
        if self.moves_field[pos] != start_pos && field.is_putting_allowed(pos) && manhattan(width, start_pos, pos) <= UCT_RADIUS {
          if self.moves_field[pos] == 0 {
            self.moves.push(pos);
          }
          self.moves_field[pos] = start_pos;
          true
        } else {
          false
        }
      });
    }
  }

  fn expand_node(node: &mut UctNode, moves: &Vec<Pos>) {
    if node.get_child_ref().is_none() {
      if node.get_visits() == usize::max_value() {
        node.clear_stats();
      }
    } else {
      let mut next = node.get_child_mut();
      while next.as_ref().unwrap().get_sibling_ref().is_some() {
        UctRoot::expand_node(*next.as_mut().unwrap(), moves);
        next = next.unwrap().get_sibling_mut();
      }
      UctRoot::expand_node(*next.as_mut().unwrap(), moves);
      for &pos in moves {
        next.as_mut().unwrap().set_sibling(Box::new(UctNode::new(pos)));
        next = next.unwrap().get_sibling_mut();
      }
    }
  }

  fn update(&mut self, field: &Field, player: Player) {
    if self.node.is_some() && field.hash_at(self.moves_count) != Some(self.hash) {
      self.clear();
    }
    if self.node.is_none() {
      self.init(field, player);
    } else {
      let points_seq = field.points_seq();
      let moves_count = field.moves_count();
      loop {
        if self.moves_count == moves_count {
          break;
        }
        let next_pos = points_seq[self.moves_count];
        if !field.is_players_point(next_pos, self.player) {
          self.clear();
          self.init(field, player);
          break;
        }
        let mut next = self.node.as_ref().unwrap().get_child();
        while next.is_some() && next.as_ref().unwrap().pos != next_pos {
          next = next.unwrap().get_sibling();
        }
        match next.as_mut() {
          Some(node) => {
            node.clear_sibling();
          },
          None => {
            self.clear();
            self.init(field, player);
            break;
          }
        }
        self.node = next;
        let moves_field = &mut self.moves_field;
        self.moves.retain(|&pos| {
          if field.is_putting_allowed(pos) {
            true
          } else {
            moves_field[pos] = 0;
            false
          }
        });
        let mut added_moves = Vec::new();
        let width = field.width();
        wave(width, next_pos, |pos| {
          if moves_field[pos] != next_pos && field.is_putting_allowed(pos) && manhattan(width, next_pos, pos) <= UCT_RADIUS {
            if moves_field[pos] == 0 {
              added_moves.push(pos);
            }
            moves_field[pos] = next_pos;
            true
          } else {
            false
          }
        });
        UctRoot::expand_node(self.node.as_mut().unwrap(), &added_moves);
        self.moves_count += 1;
        self.player = self.player.next();
      }
    }
  }

  pub fn new(length: Pos) -> UctRoot {
    UctRoot {
      node: None,
      moves: Vec::with_capacity(length),
      moves_field: repeat(0).take(length).collect(),
      player: Player::Red,
      moves_count: 0,
      hash: 0
    }
  }

  fn ucb(parent: &UctNode, node: &UctNode) -> f32 {
    0.0
  }

  fn play_simulation<T: Rng>(field: &mut Field, rng: &mut T) {
  }

  fn best_move_generic<T: Rng>(&mut self, field: &Field, player: Player, rng: &mut T, should_stop: &AtomicBool) -> Option<Pos> {
    self.update(field, player);
    let mut guards = Vec::with_capacity(4);
    for i in 0 .. 4 {
      let xor_shift_rng = rng.gen::<XorShiftRng>();
      guards.push(thread::scoped(|| {
        let mut local_field = field.clone();
        let mut local_rng = xor_shift_rng;
        while !should_stop.load(Ordering::Relaxed) {
          UctRoot::play_simulation(&mut local_field, &mut local_rng);
        }
      }));
    }
    drop(guards);
    let mut best_uct = 0f32;
    let mut result = None;
    let mut next = self.node.as_ref().unwrap().get_child_ref();
    while next.is_some() {
      if next.unwrap().get_visits() != 0 {
        let uct_value = UctRoot::ucb(self.node.as_ref().unwrap(), next.unwrap());
        if uct_value > best_uct {
          best_uct = uct_value;
          result = Some(next.unwrap().get_pos());
        }
      }
      next = next.unwrap().get_sibling_ref();
    }
    result
  }

  pub fn best_move<T: Rng>(&mut self, field: &Field, player: Player, rng: &mut T, time: Time) -> Option<Pos> {
    let should_stop = AtomicBool::new(false);
    let guard = thread::scoped(|| {
      thread::sleep_ms(time);
      should_stop.store(true, Ordering::Relaxed);
    });
    let result = self.best_move_generic(field, player, rng, &should_stop);
    drop(guard);
    result
  }

  //pub fn estimates
}
<|endoftext|>


<commit_before>#![allow(non_snake_case)]
use std::fmt;
use std::slice;
use std::mem;
use num;
use approx;
use channel::{FreeChannel, FreeChannelScalar, PosNormalChannelScalar, PosNormalBoundedChannel,
              ColorChannel, ChannelFormatCast, ChannelCast};
use color::{Color, Bounded, Lerp, Flatten};
use convert::FromColor;
use xyz::Xyz;

pub struct XyYTag;

#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
pub struct XyY<T> {
    pub x: PosNormalBoundedChannel<T>,
    pub y: PosNormalBoundedChannel<T>,
    pub Y: FreeChannel<T>,
}

impl<T> XyY<T>
    where T: FreeChannelScalar + num::Float + PosNormalChannelScalar
{
    pub fn from_channels(x: T, y: T, Y: T) -> Self {
        let zero = num::cast(0.0).unwrap();
        if x + y > num::cast(1.0).unwrap() || x + y < zero {
            panic!("xyY `x` and `y` channels are ratios and must sum to be between 0 and 1");
        }
        assert!(x >= zero);
        assert!(y >= zero);

        XyY {
            x: PosNormalBoundedChannel::new(x),
            y: PosNormalBoundedChannel::new(y),
            Y: FreeChannel::new(Y),
        }
    }

    impl_color_color_cast_square!(XyY {x, y, Y}, chan_traits={FreeChannelScalar,
        PosNormalChannelScalar});

    pub fn x(&self) -> T {
        self.x.0.clone()
    }
    pub fn y(&self) -> T {
        self.y.0.clone()
    }
    pub fn z(&self) -> T {
        num::cast::<_, T>(1.0).unwrap() - self.x() - self.y()
    }
    pub fn Y(&self) -> T {
        self.Y.0.clone()
    }
    pub fn Y_mut(&mut self) -> &mut T {
        &mut self.Y.0
    }
    pub fn set_x(&mut self, val: T) {
        let (x, y, _) = Self::rescale_channels(val, self.y(), self.z());
        self.x.0 = x;
        self.y.0 = y;
    }
    pub fn set_y(&mut self, val: T) {
        let (y, x, _) = Self::rescale_channels(val, self.x(), self.z());
        self.x.0 = x;
        self.y.0 = y;
    }
    pub fn set_Y(&mut self, val: T) {
        let (_, x, y) = Self::rescale_channels(val, self.x(), self.y());
        self.x.0 = x;
        self.y.0 = y;
    }

    fn rescale_channels(primary: T, c2: T, c3: T) -> (T, T, T) {
        if primary > PosNormalBoundedChannel::max_bound() ||
           primary < PosNormalBoundedChannel::min_bound() {
            panic!("xyY chromaticity channels must be between 0.0 and 1.0")
        }

        let zero = num::cast(0.0).unwrap();
        let rem_scale = c2 + c3;
        let rem = num::cast::<_, T>(1.0).unwrap() - primary;
        if rem_scale > zero {
            (primary, (c2 / rem_scale) * rem, (c3 / rem_scale) * rem)
        } else {
            let one_half = num::cast(0.5).unwrap();
            (primary, rem * one_half, rem * one_half)
        }
    }
}

impl<T> Color for XyY<T>
    where T: FreeChannelScalar + PosNormalChannelScalar + num::Float
{
    type Tag = XyYTag;
    type ChannelsTuple = (T, T, T);

    #[inline]
    fn num_channels() -> u32 {
        3
    }
    fn from_tuple(values: (T, T, T)) -> Self {
        let (x, y, Y) = values;
        XyY {
            x: PosNormalBoundedChannel::new(x),
            y: PosNormalBoundedChannel::new(y),
            Y: FreeChannel::new(Y),
        }
    }
    fn to_tuple(self) -> Self::ChannelsTuple {
        (self.x.0, self.y.0, self.Y.0)
    }
}

impl<T> Bounded for XyY<T>
    where T: FreeChannelScalar + PosNormalChannelScalar + num::Float
{
    fn normalize(self) -> Self {
        self
    }
    fn is_normalized(&self) -> bool {
        true
    }
}

impl<T> Lerp for XyY<T>
    where T: FreeChannelScalar + PosNormalChannelScalar + num::Float,
          FreeChannel<T>: Lerp,
          PosNormalBoundedChannel<T>: Lerp<Position=<FreeChannel<T> as Lerp>::Position>,
{
    type Position = <FreeChannel<T> as Lerp>::Position;
    impl_color_lerp_square!(XyY {x, y, Y});
}

impl<T> Flatten for XyY<T>
    where T: FreeChannelScalar + PosNormalChannelScalar + num::Float
{
    type ScalarFormat = T;

    impl_color_as_slice!(T);
    impl_color_from_slice_square!(XyY<T> {x:PosNormalBoundedChannel - 0,
        y:PosNormalBoundedChannel - 1, Y:FreeChannel - 2});
}

impl<T> approx::ApproxEq for XyY<T>
    where T: FreeChannelScalar + PosNormalChannelScalar + approx::ApproxEq,
          T::Epsilon: Clone
{
    impl_approx_eq!({x, y, Y});
}

impl<T> Default for XyY<T>
    where T: FreeChannelScalar + PosNormalChannelScalar + num::Float
{
    impl_color_default!(XyY {x:PosNormalBoundedChannel, y:PosNormalBoundedChannel,
        Y:FreeChannel});
}

impl<T> fmt::Display for XyY<T>
    where T: FreeChannelScalar + PosNormalChannelScalar + num::Float + fmt::Display
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "xyY({}, {}, {})", self.x, self.y, self.Y)
    }
}

impl<T> FromColor<Xyz<T>> for XyY<T>
    where T: FreeChannelScalar + PosNormalChannelScalar + num::Float
{
    fn from_color(from: &Xyz<T>) -> Self {
        let zero = num::cast(0.0).unwrap();
        if from.x() < zero || from.y() < zero || from.z() < zero {
            panic!("Cannot convert an XYZ color with negative channels to xyY");
        }
        let sum = from.x() + from.y() + from.z();

        if sum != zero {
            let x = from.x() / sum;
            let y = from.y() / sum;
            let Y = from.y();

            XyY::from_channels(x, y, Y)
        } else {
            XyY::from_channels(zero, zero, zero)
        }
    }
}

impl<T> FromColor<XyY<T>> for Xyz<T>
    where T: FreeChannelScalar + PosNormalChannelScalar + num::Float
{
    fn from_color(from: &XyY<T>) -> Self {
        let zero = num::cast(0.0).unwrap();
        if from.y() == zero {
            Xyz::from_channels(zero, zero, zero)
        } else {
            let x = (from.Y() / from.y()) * from.x();
            let y = from.Y();
            let z = (from.Y() / from.y()) * from.z();
            Xyz::from_channels(x, y, z)
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use xyz::Xyz;
    use convert::*;

    #[test]
    fn test_from_xyz() {
        let c1 = Xyz::from_channels(0.3, 0.2, 0.5);
        let t1 = XyY::from_color(&c1);
        assert_relative_eq!(t1, XyY::from_channels(0.3, 0.2, 0.2), epsilon=1e-6);
        assert_relative_eq!(Xyz::from_color(&t1), c1, epsilon=1e-6);

        let c2 = Xyz::from_channels(0.8, 0.1, 0.5);
        let t2 = XyY::from_color(&c2);
        assert_relative_eq!(t2, XyY::from_channels(0.571429, 0.071429, 0.1), epsilon=1e-6);
        assert_relative_eq!(Xyz::from_color(&t2), c2, epsilon=1e-6);

        let c3 = Xyz::from_channels(0.0, 0.0, 0.0);
        let t3 = XyY::from_color(&c3);
        assert_relative_eq!(t3, XyY::from_channels(0.0, 0.0, 0.0), epsilon=1e-6);
        assert_relative_eq!(Xyz::from_color(&t3), c3, epsilon=1e-6);

        let c4 = Xyz::from_channels(0.5, 0.5, 0.5);
        let t4 = XyY::from_color(&c4);
        assert_relative_eq!(t4, XyY::from_channels(1.0/3.0, 1.0/3.0, 0.5), epsilon=1e-6);
        assert_relative_eq!(Xyz::from_color(&t4), c4, epsilon=1e-6);

        let c5 = Xyz::from_channels(1.2, 0.3, 0.8);
        let t5 = XyY::from_color(&c5);
        assert_relative_eq!(t5, XyY::from_channels(0.521739, 0.130435, 0.3000), epsilon=1e-6);
        assert_relative_eq!(Xyz::from_color(&t5), c5, epsilon=1e-6);
    }

    #[test]
    fn test_to_xyz() {
        let c1 = XyY::from_channels(0.5, 0.2, 0.5);
        let t1 = Xyz::from_color(&c1);
        assert_relative_eq!(t1, Xyz::from_channels(1.25, 0.5, 0.75), epsilon=1e-6);
        assert_relative_eq!(XyY::from_color(&t1), c1, epsilon=1e-6);

        let c2 = XyY::from_channels(1.0 / 3.0, 1.0 / 3.0, 1.0);
        let t2 = Xyz::from_color(&c2);
        assert_relative_eq!(t2, Xyz::from_channels(1.0, 1.0, 1.0), epsilon=1e-6);
        assert_relative_eq!(XyY::from_color(&t2), c2, epsilon=1e-6);

        let c3 = XyY::from_channels(0.3, 0.5, 0.3);
        let t3 = Xyz::from_color(&c3);
        assert_relative_eq!(t3, Xyz::from_channels(0.18, 0.3, 0.12), epsilon=1e-6);
        assert_relative_eq!(XyY::from_color(&t3), c3, epsilon=1e-6);

        let c4 = XyY::from_channels(0.285, 0.4194, 0.583);
        let t4 = Xyz::from_color(&c4);
        assert_relative_eq!(t4, Xyz::from_channels(0.396173, 0.5830, 0.410908), epsilon=1e-6);
        assert_relative_eq!(XyY::from_color(&t4), c4, epsilon=1e-6);
    }
}
<commit_msg>Added general functionality tests for XyY.<commit_after>#![allow(non_snake_case)]
use std::fmt;
use std::slice;
use std::mem;
use num;
use approx;
use channel::{FreeChannel, FreeChannelScalar, PosNormalChannelScalar, PosNormalBoundedChannel,
              ColorChannel, ChannelFormatCast, ChannelCast};
use color::{Color, Bounded, Lerp, Flatten};
use convert::FromColor;
use xyz::Xyz;

pub struct XyYTag;

#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
pub struct XyY<T> {
    pub x: PosNormalBoundedChannel<T>,
    pub y: PosNormalBoundedChannel<T>,
    pub Y: FreeChannel<T>,
}

impl<T> XyY<T>
    where T: FreeChannelScalar + num::Float + PosNormalChannelScalar
{
    pub fn from_channels(x: T, y: T, Y: T) -> Self {
        let zero = num::cast(0.0).unwrap();
        if x + y > num::cast(1.0).unwrap() || x + y < zero {
            panic!("xyY `x` and `y` channels are ratios and must sum to be between 0 and 1");
        }
        assert!(x >= zero);
        assert!(y >= zero);

        XyY {
            x: PosNormalBoundedChannel::new(x),
            y: PosNormalBoundedChannel::new(y),
            Y: FreeChannel::new(Y),
        }
    }

    impl_color_color_cast_square!(XyY {x, y, Y}, chan_traits={FreeChannelScalar,
        PosNormalChannelScalar});

    pub fn x(&self) -> T {
        self.x.0.clone()
    }
    pub fn y(&self) -> T {
        self.y.0.clone()
    }
    pub fn z(&self) -> T {
        num::cast::<_, T>(1.0).unwrap() - self.x() - self.y()
    }
    pub fn Y(&self) -> T {
        self.Y.0.clone()
    }
    pub fn Y_mut(&mut self) -> &mut T {
        &mut self.Y.0
    }
    pub fn set_x(&mut self, val: T) {
        let (x, y, _) = Self::rescale_channels(val, self.y(), self.z());
        self.x.0 = x;
        self.y.0 = y;
    }
    pub fn set_y(&mut self, val: T) {
        let (y, x, _) = Self::rescale_channels(val, self.x(), self.z());
        self.x.0 = x;
        self.y.0 = y;
    }
    pub fn set_z(&mut self, val: T) {
        let (_, x, y) = Self::rescale_channels(val, self.x(), self.y());
        self.x.0 = x;
        self.y.0 = y;
    }

    fn rescale_channels(primary: T, c2: T, c3: T) -> (T, T, T) {
        if primary > PosNormalBoundedChannel::max_bound() ||
           primary < PosNormalBoundedChannel::min_bound() {
            panic!("xyY chromaticity channels must be between 0.0 and 1.0")
        }

        let zero = num::cast(0.0).unwrap();
        let rem_scale = c2 + c3;
        let rem = num::cast::<_, T>(1.0).unwrap() - primary;
        if rem_scale > zero {
            (primary, (c2 / rem_scale) * rem, (c3 / rem_scale) * rem)
        } else {
            let one_half = num::cast(0.5).unwrap();
            (primary, rem * one_half, rem * one_half)
        }
    }
}

impl<T> Color for XyY<T>
    where T: FreeChannelScalar + PosNormalChannelScalar + num::Float
{
    type Tag = XyYTag;
    type ChannelsTuple = (T, T, T);

    #[inline]
    fn num_channels() -> u32 {
        3
    }
    fn from_tuple(values: (T, T, T)) -> Self {
        let (x, y, Y) = values;
        XyY::from_channels(x, y, Y)
    }
    fn to_tuple(self) -> Self::ChannelsTuple {
        (self.x.0, self.y.0, self.Y.0)
    }
}

impl<T> Bounded for XyY<T>
    where T: FreeChannelScalar + PosNormalChannelScalar + num::Float
{
    fn normalize(self) -> Self {
        self
    }
    fn is_normalized(&self) -> bool {
        true
    }
}

impl<T> Lerp for XyY<T>
    where T: FreeChannelScalar + PosNormalChannelScalar + num::Float,
          FreeChannel<T>: Lerp,
          PosNormalBoundedChannel<T>: Lerp<Position=<FreeChannel<T> as Lerp>::Position>,
{
    type Position = <FreeChannel<T> as Lerp>::Position;
    impl_color_lerp_square!(XyY {x, y, Y});
}

impl<T> Flatten for XyY<T>
    where T: FreeChannelScalar + PosNormalChannelScalar + num::Float
{
    type ScalarFormat = T;

    impl_color_as_slice!(T);
    impl_color_from_slice_square!(XyY<T> {x:PosNormalBoundedChannel - 0,
        y:PosNormalBoundedChannel - 1, Y:FreeChannel - 2});
}

impl<T> approx::ApproxEq for XyY<T>
    where T: FreeChannelScalar + PosNormalChannelScalar + approx::ApproxEq,
          T::Epsilon: Clone
{
    impl_approx_eq!({x, y, Y});
}

impl<T> Default for XyY<T>
    where T: FreeChannelScalar + PosNormalChannelScalar + num::Float
{
    impl_color_default!(XyY {x:PosNormalBoundedChannel, y:PosNormalBoundedChannel,
        Y:FreeChannel});
}

impl<T> fmt::Display for XyY<T>
    where T: FreeChannelScalar + PosNormalChannelScalar + num::Float + fmt::Display
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "xyY({}, {}, {})", self.x, self.y, self.Y)
    }
}

impl<T> FromColor<Xyz<T>> for XyY<T>
    where T: FreeChannelScalar + PosNormalChannelScalar + num::Float
{
    fn from_color(from: &Xyz<T>) -> Self {
        let zero = num::cast(0.0).unwrap();
        if from.x() < zero || from.y() < zero || from.z() < zero {
            panic!("Cannot convert an XYZ color with negative channels to xyY");
        }
        let sum = from.x() + from.y() + from.z();

        if sum != zero {
            let x = from.x() / sum;
            let y = from.y() / sum;
            let Y = from.y();

            XyY::from_channels(x, y, Y)
        } else {
            XyY::from_channels(zero, zero, zero)
        }
    }
}

impl<T> FromColor<XyY<T>> for Xyz<T>
    where T: FreeChannelScalar + PosNormalChannelScalar + num::Float
{
    fn from_color(from: &XyY<T>) -> Self {
        let zero = num::cast(0.0).unwrap();
        if from.y() == zero {
            Xyz::from_channels(zero, zero, zero)
        } else {
            let x = (from.Y() / from.y()) * from.x();
            let y = from.Y();
            let z = (from.Y() / from.y()) * from.z();
            Xyz::from_channels(x, y, z)
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use xyz::Xyz;
    use convert::*;
    use color::*;

    #[test]
    fn test_construct() {
        let c1 = XyY::from_channels(0.5, 0.3, 0.8);
        assert_eq!(c1.x(), 0.5);
        assert_eq!(c1.y(), 0.3);
        assert_eq!(c1.z(), 0.2);
        assert_eq!(c1.Y(), 0.8);
        assert_eq!(c1.to_tuple(), (0.5, 0.3, 0.8));
        assert_eq!(XyY::from_tuple(c1.clone().to_tuple()), c1);

        let c2 = XyY::from_channels(0.0, 0.0, 1.1);
        assert_eq!(c2.x(), 0.0);
        assert_eq!(c2.y(), 0.0);
        assert_eq!(c2.z(), 1.0);
        assert_eq!(c2.Y(), 1.1);
        assert_eq!(c2.to_tuple(), (0.0, 0.0, 1.1));
        assert_eq!(XyY::from_tuple(c2.clone().to_tuple()), c2);

        let c3 = XyY::from_tuple((0.4, 0.1, 0.0));
        assert_eq!(c3.x(), 0.4);
        assert_eq!(c3.y(), 0.1);
        assert_eq!(c3.z(), 0.5);
        assert_eq!(c3.Y(), 0.0);
        assert_eq!(c3.to_tuple(), (0.4, 0.1, 0.0));
        assert_eq!(XyY::from_tuple(c3.clone().to_tuple()), c3);
    }

    #[test]
    #[should_panic]
    fn test_tuple_oob_panic() {
        let _ = XyY::from_tuple((1.2, 0.5, 0.8));
    }

    #[test]
    #[should_panic]
    fn test_neg_channel_panic() {
        let _ = XyY::from_channels(-0.2, 0.0, 0.5);
    }

    #[test]
    #[should_panic]
    fn test_too_large_channel_panic() {
        let _ = XyY::from_channels(1.3, 0.2, 0.7);
    }

    #[test]
    fn test_set_channels() {
        let mut c1 = XyY::from_channels(0.4, 0.3, 0.4);
        c1.set_x(0.6);
        assert_relative_eq!(c1.x(), 0.6);
        assert_relative_eq!(c1.y(), 0.20);
        assert_relative_eq!(c1.z(), 0.20);
        assert_relative_eq!(c1.Y(), 0.4);
        c1.set_z(0.5);
        assert_relative_eq!(c1, XyY::from_channels(0.375, 0.125, 0.4), epsilon=1e-6);

        let mut c2 = XyY::from_channels(0.3, 0.3, 1.0);
        c2.set_y(0.8);
        assert_relative_eq!(c2.x(), 0.2 * (3.0 / 7.0));
        assert_relative_eq!(c2.y(), 0.8);
        assert_relative_eq!(c2.z(), 0.2 * (4.0 / 7.0));
        assert_relative_eq!(c2.Y(), 1.0);
        c2.set_z(1.0);
        assert_relative_eq!(c2.x(), 0.0);
        assert_relative_eq!(c2.y(), 0.0);
        assert_relative_eq!(c2.z(), 1.0);
        c2.set_x(1.0);
        assert_relative_eq!(c2.x(), 1.0);
        assert_relative_eq!(c2.y(), 0.0);
        assert_relative_eq!(c2.z(), 0.0);
        assert_relative_eq!(c2.Y(), 1.0);
    }

    #[test]
    #[should_panic]
    fn test_neg_set_panic() {
        let mut c1 = XyY::from_channels(0.2, 0.3, 0.9);
        c1.set_y(-0.3);
    }

    #[test]
    #[should_panic]
    fn test_too_high_set_panic() {
        let mut c1 = XyY::from_channels(0.2, 0.3, 0.9);
        c1.set_y(33333.0);
    }

    #[test]
    fn test_flatten() {
        let c1 = XyY::from_channels(0.5, 0.3, 0.8);
        assert_eq!(c1.as_slice(), &[0.5, 0.3, 0.8]);
        assert_relative_eq!(XyY::from_slice(c1.as_slice()), c1);
    }

    #[test]
    fn test_normalize() {
        let c1 = XyY::from_channels(0.3, 0.5, 1.5);
        assert!(c1.is_normalized());
        assert_eq!(c1.normalize(), c1);

        let c2 = XyY::from_channels(0.0, 0.0, 0.0);
        assert!(c2.is_normalized());
        assert_eq!(c2.normalize(), c2);
    }

    #[test]
    fn test_lerp() {
        let c1 = XyY::from_channels(0.3, 0.1, 0.6);
        let c2 = XyY::from_channels(0.8, 0.2, 0.6);
        assert_relative_eq!(c1.lerp(&c2, 0.00), c1);
        assert_relative_eq!(c1.lerp(&c2, 1.00), c2);
        assert_relative_eq!(c1.lerp(&c2, 0.50), XyY::from_channels(0.55, 0.15, 0.6));
    }

    #[test]
    fn test_from_xyz() {
        let c1 = Xyz::from_channels(0.3, 0.2, 0.5);
        let t1 = XyY::from_color(&c1);
        assert_relative_eq!(t1, XyY::from_channels(0.3, 0.2, 0.2), epsilon=1e-6);
        assert_relative_eq!(Xyz::from_color(&t1), c1, epsilon=1e-6);

        let c2 = Xyz::from_channels(0.8, 0.1, 0.5);
        let t2 = XyY::from_color(&c2);
        assert_relative_eq!(t2, XyY::from_channels(0.571429, 0.071429, 0.1), epsilon=1e-6);
        assert_relative_eq!(Xyz::from_color(&t2), c2, epsilon=1e-6);

        let c3 = Xyz::from_channels(0.0, 0.0, 0.0);
        let t3 = XyY::from_color(&c3);
        assert_relative_eq!(t3, XyY::from_channels(0.0, 0.0, 0.0), epsilon=1e-6);
        assert_relative_eq!(Xyz::from_color(&t3), c3, epsilon=1e-6);

        let c4 = Xyz::from_channels(0.5, 0.5, 0.5);
        let t4 = XyY::from_color(&c4);
        assert_relative_eq!(t4, XyY::from_channels(1.0/3.0, 1.0/3.0, 0.5), epsilon=1e-6);
        assert_relative_eq!(Xyz::from_color(&t4), c4, epsilon=1e-6);

        let c5 = Xyz::from_channels(1.2, 0.3, 0.8);
        let t5 = XyY::from_color(&c5);
        assert_relative_eq!(t5, XyY::from_channels(0.521739, 0.130435, 0.3000), epsilon=1e-6);
        assert_relative_eq!(Xyz::from_color(&t5), c5, epsilon=1e-6);
    }

    #[test]
    fn test_to_xyz() {
        let c1 = XyY::from_channels(0.5, 0.2, 0.5);
        let t1 = Xyz::from_color(&c1);
        assert_relative_eq!(t1, Xyz::from_channels(1.25, 0.5, 0.75), epsilon=1e-6);
        assert_relative_eq!(XyY::from_color(&t1), c1, epsilon=1e-6);

        let c2 = XyY::from_channels(1.0 / 3.0, 1.0 / 3.0, 1.0);
        let t2 = Xyz::from_color(&c2);
        assert_relative_eq!(t2, Xyz::from_channels(1.0, 1.0, 1.0), epsilon=1e-6);
        assert_relative_eq!(XyY::from_color(&t2), c2, epsilon=1e-6);

        let c3 = XyY::from_channels(0.3, 0.5, 0.3);
        let t3 = Xyz::from_color(&c3);
        assert_relative_eq!(t3, Xyz::from_channels(0.18, 0.3, 0.12), epsilon=1e-6);
        assert_relative_eq!(XyY::from_color(&t3), c3, epsilon=1e-6);

        let c4 = XyY::from_channels(0.285, 0.4194, 0.583);
        let t4 = Xyz::from_color(&c4);
        assert_relative_eq!(t4, Xyz::from_channels(0.396173, 0.5830, 0.410908), epsilon=1e-6);
        assert_relative_eq!(XyY::from_color(&t4), c4, epsilon=1e-6);
    }

    #[test]
    fn test_color_cast() {
        let c1 = XyY::from_channels(0.5, 0.2, 1.0);
        assert_relative_eq!(c1.color_cast(), c1);
        assert_relative_eq!(c1.color_cast::<f32>().color_cast(), c1, epsilon=1e-6);
        assert_relative_eq!(c1.color_cast(), XyY::from_channels(0.5f32, 0.2, 1.0), epsilon=1e-6);
    }
}
<|endoftext|>


<commit_before>extern crate fall_test;
extern crate fall_tree;

use fall_test::{rust, match_ast};
use fall_tree::dump_file_ws;

fn ast(code: &str) -> String {
    dump_file_ws(&rust::parse(code.to_owned()))
}

#[test]
fn empty_nodes() {
    match_ast(&ast("\
struct Foo {}
fn bar() {}
pub struct Baz {}
pub fn quux() {}
"), r#"
FILE
  STRUCT_DEF
    STRUCT "struct"
    WHITESPACE " "
    IDENT "Foo"
    WHITESPACE " "
    LBRACE "{"
    RBRACE "}"
  WHITESPACE "\n"
  FN_DEF
    FN "fn"
    WHITESPACE " "
    IDENT "bar"
    LPAREN "("
    RPAREN ")"
    WHITESPACE " "
    LBRACE "{"
    RBRACE "}"
  WHITESPACE "\n"
  STRUCT_DEF
    PUB "pub"
    WHITESPACE " "
    STRUCT "struct"
    WHITESPACE " "
    IDENT "Baz"
    WHITESPACE " "
    LBRACE "{"
    RBRACE "}"
  WHITESPACE "\n"
  FN_DEF
    PUB "pub"
    WHITESPACE " "
    FN "fn"
    WHITESPACE " "
    IDENT "quux"
    LPAREN "("
    RPAREN ")"
    WHITESPACE " "
    LBRACE "{"
    RBRACE "}"
  WHITESPACE "\n"
"#)
}
<commit_msg>Rename<commit_after>extern crate fall_test;
extern crate fall_tree;

use fall_test::{rust, match_ast};
use fall_tree::dump_file_ws;

fn ast(code: &str) -> String {
    dump_file_ws(&rust::parse(code.to_owned()))
}

#[test]
fn opt_pub() {
    match_ast(&ast("\
struct Foo {}
fn bar() {}
pub struct Baz {}
pub fn quux() {}
"), r#"
FILE
  STRUCT_DEF
    STRUCT "struct"
    WHITESPACE " "
    IDENT "Foo"
    WHITESPACE " "
    LBRACE "{"
    RBRACE "}"
  WHITESPACE "\n"
  FN_DEF
    FN "fn"
    WHITESPACE " "
    IDENT "bar"
    LPAREN "("
    RPAREN ")"
    WHITESPACE " "
    LBRACE "{"
    RBRACE "}"
  WHITESPACE "\n"
  STRUCT_DEF
    PUB "pub"
    WHITESPACE " "
    STRUCT "struct"
    WHITESPACE " "
    IDENT "Baz"
    WHITESPACE " "
    LBRACE "{"
    RBRACE "}"
  WHITESPACE "\n"
  FN_DEF
    PUB "pub"
    WHITESPACE " "
    FN "fn"
    WHITESPACE " "
    IDENT "quux"
    LPAREN "("
    RPAREN ")"
    WHITESPACE " "
    LBRACE "{"
    RBRACE "}"
  WHITESPACE "\n"
"#)
}
<|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 dom::bindings::codegen::Bindings::DocumentBinding::DocumentBinding::DocumentMethods;
use dom::bindings::codegen::Bindings::ElementBinding::ElementBinding::ElementMethods;
use dom::bindings::inheritance::Castable;
use dom::bindings::root::DomRoot;
use dom::document::Document;
use dom::element::Element;
use dom::htmlelement::HTMLElement;
use dom::node::Node;
use script_traits::Microdata;
use std::borrow::Cow;
use std::collections::HashMap;

pub fn parse(doc: &Document, node: &Node) -> Option<Microdata> {
    let serialized_vcard = parse_vcard(doc);
    let serialized_json = parse_json(node);
    if !serialized_vcard.is_empty() {
        return Some(Microdata::VCardData(serialized_vcard.to_owned()));
    } else if !serialized_vcard.is_empty(){
        return Some(Microdata::JSONData(serialized_json.to_owned()));
    } else {
        return None
    }
}

pub fn parse_vcard(doc: &Document) -> String {
    let ele = doc.upcast::<Node>();
    let mut start_vcard = false;
    let mut result: String = String::new();
    let mut master_map: HashMap<String, HashMap<String, String>> = HashMap::new();
    let mut master_key: String = String::new();

    result += "BEGIN:VCARD\nPROFILE:VCARD\nVERSION:4.0\nSOURCE:";
    result += doc.url().as_str();

    let title = doc.Title();
    if !title.is_empty() && !title.trim().is_empty() {
        result += "\nNAME:";
        result += title.trim();
    }

    result += "\n";

    for element in ele.traverse_preorder().filter_map(DomRoot::downcast::<Element>) {
        if element.is::<HTMLElement>() {
            if element.has_attribute(&local_name!("itemtype")) {
                let mut atoms = element.get_tokenlist_attribute(&local_name!("itemtype"), );
                if !atoms.is_empty() {
                    let val = atoms.remove(0);
                    if val.trim() == "http://microformats.org/profile/hcard" {
                        if !start_vcard {
                            start_vcard = true;
                        } else {
                            break;
                        }
                    }
                }
            }
            if start_vcard {
                let mut atoms = element.get_tokenlist_attribute(&local_name!("itemprop"), );
                if !atoms.is_empty() {
                    let temp_key = atoms.remove(0);
                    if element.has_attribute(&local_name!("itemscope")) {
                        master_key = String::from(temp_key.trim()).to_owned();
                        let dup_master_key = Cow::Borrowed(&master_key);
                        master_map.entry(dup_master_key.to_string()).or_insert(HashMap::new());
                    } else {
                        let temp = String::from(temp_key.trim()).to_owned();
                        let dup_key = Cow::Borrowed(&temp);
                        let data = String::from(element.GetInnerHTML().unwrap());
                        let dup_master_key = Cow::Borrowed(&master_key);
                        let temp_map = master_map.entry(dup_master_key.to_string()).or_insert(HashMap::new());
                        temp_map.insert(dup_key.to_string(), String::from(data));
                    }
                }
            }
        }
    }
    let vcard_parts = ["n", "org", "tel", "adr"];
    for info_type in vcard_parts.iter() {
        let detail_map_val = master_map.get(*info_type);
        if detail_map_val.is_none() {
            continue;
        }
        let detail_map = detail_map_val.unwrap();
        match *info_type {
            "n" => {
                let mut n_value: String = String::new();

                let name_parts = ["family-name", "given-name",
                "additional-name", "honorific-prefix", "honorific-suffix"];
                for part in name_parts.iter() {
                    if detail_map.contains_key(*part) {
                        n_value += format!("{};", detail_map.get(*part).unwrap()).as_str();
                    }
                }
                n_value.pop();

                result += format!("{}:{}\n", info_type.to_ascii_uppercase(), n_value).as_str();
            },
            "org" => {
                let mut org_value: String = String::new();

                let org_parts = ["organization-name", "organization-unit"];
                for part in org_parts.iter() {
                    if detail_map.contains_key(*part) {
                        org_value += format!("{};", detail_map.get(*part).unwrap()).as_str();
                    }
                }
                org_value.pop();

                result += format!("{}:{}\n", info_type.to_ascii_uppercase(), org_value).as_str();
            },
            "tel" => {
                let mut tel_value: String = String::new();

                let tel_parts = ["value"];
                for part in tel_parts.iter() {
                    if detail_map.contains_key(*part) {
                        tel_value += format!("{};", detail_map.get(*part).unwrap()).as_str();
                    }
                }
                tel_value.pop();

                result += format!("{}:{}\n", info_type.to_ascii_uppercase(), tel_value).as_str();
            },
            "adr" => {
                let mut adr_value: String = String::new();

                let adr_parts = ["street-address", "locality", "region", "postal-code",
                "country-name", "post-office-box", "extended-address"];
                for part in adr_parts.iter() {
                    if detail_map.contains_key(*part) {
                        adr_value += format!("{};", detail_map.get(*part).unwrap()).as_str();
                    }
                }
                adr_value.pop();

                result += format!("{}:{}\n", info_type.to_ascii_uppercase(), adr_value).as_str();
            },
            _ => {},
        }
    }
    result += "END:VCARD";
    if start_vcard {
        return result;
    } else {
        return "".to_string();
    }
}

pub fn parse_json(node: &Node) -> String {
    // TODO Write the logic for JSON Parsing
    return "".to_string();
}
<commit_msg>Fixed lint issues<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 dom::bindings::codegen::Bindings::DocumentBinding::DocumentBinding::DocumentMethods;
use dom::bindings::codegen::Bindings::ElementBinding::ElementBinding::ElementMethods;
use dom::bindings::inheritance::Castable;
use dom::bindings::root::DomRoot;
use dom::document::Document;
use dom::element::Element;
use dom::htmlelement::HTMLElement;
use dom::node::Node;
use script_traits::Microdata;
use std::borrow::Cow;
use std::collections::HashMap;

pub fn parse(doc: &Document, node: &Node) -> Option<Microdata> {
    let serialized_vcard = parse_vcard(doc);
    let serialized_json = parse_json(node);
    if !serialized_vcard.is_empty() {
        return Some(Microdata::VCardData(serialized_vcard.to_owned()));
    } else if !serialized_vcard.is_empty() {
        return Some(Microdata::JSONData(serialized_json.to_owned()));
    } else {
        return None
    }
}

pub fn parse_vcard(doc: &Document) -> String {
    let ele = doc.upcast::<Node>();
    let mut start_vcard = false;
    let mut result: String = String::new();
    let mut master_map: HashMap<String, HashMap<String, String>> = HashMap::new();
    let mut master_key: String = String::new();

    result += "BEGIN:VCARD\nPROFILE:VCARD\nVERSION:4.0\nSOURCE:";
    result += doc.url().as_str();

    let title = doc.Title();
    if !title.is_empty() && !title.trim().is_empty() {
        result += "\nNAME:";
        result += title.trim();
    }

    result += "\n";

    for element in ele.traverse_preorder().filter_map(DomRoot::downcast::<Element>) {
        if element.is::<HTMLElement>() {
            if element.has_attribute(&local_name!("itemtype")) {
                let mut atoms = element.get_tokenlist_attribute(&local_name!("itemtype"), );
                if !atoms.is_empty() {
                    let val = atoms.remove(0);
                    if val.trim() == "http://microformats.org/profile/hcard" {
                        if !start_vcard {
                            start_vcard = true;
                        } else {
                            break;
                        }
                    }
                }
            }
            if start_vcard {
                let mut atoms = element.get_tokenlist_attribute(&local_name!("itemprop"), );
                if !atoms.is_empty() {
                    let temp_key = atoms.remove(0);
                    if element.has_attribute(&local_name!("itemscope")) {
                        master_key = String::from(temp_key.trim()).to_owned();
                        let dup_master_key = Cow::Borrowed(&master_key);
                        master_map.entry(dup_master_key.to_string()).or_insert(HashMap::new());
                    } else {
                        let temp = String::from(temp_key.trim()).to_owned();
                        let dup_key = Cow::Borrowed(&temp);
                        let data = String::from(element.GetInnerHTML().unwrap());
                        let dup_master_key = Cow::Borrowed(&master_key);
                        let temp_map = master_map.entry(dup_master_key.to_string()).or_insert(HashMap::new());
                        temp_map.insert(dup_key.to_string(), String::from(data));
                    }
                }
            }
        }
    }
    let vcard_parts = ["n", "org", "tel", "adr"];
    for info_type in vcard_parts.iter() {
        let detail_map_val = master_map.get(*info_type);
        if detail_map_val.is_none() {
            continue;
        }
        let detail_map = detail_map_val.unwrap();
        match *info_type {
            "n" => {
                let mut n_value: String = String::new();

                let name_parts = ["family-name", "given-name",
                "additional-name", "honorific-prefix", "honorific-suffix"];
                for part in name_parts.iter() {
                    if detail_map.contains_key(*part) {
                        n_value += format!("{};", detail_map.get(*part).unwrap()).as_str();
                    }
                }
                n_value.pop();

                result += format!("{}:{}\n", info_type.to_ascii_uppercase(), n_value).as_str();
            },
            "org" => {
                let mut org_value: String = String::new();

                let org_parts = ["organization-name", "organization-unit"];
                for part in org_parts.iter() {
                    if detail_map.contains_key(*part) {
                        org_value += format!("{};", detail_map.get(*part).unwrap()).as_str();
                    }
                }
                org_value.pop();

                result += format!("{}:{}\n", info_type.to_ascii_uppercase(), org_value).as_str();
            },
            "tel" => {
                let mut tel_value: String = String::new();

                let tel_parts = ["value"];
                for part in tel_parts.iter() {
                    if detail_map.contains_key(*part) {
                        tel_value += format!("{};", detail_map.get(*part).unwrap()).as_str();
                    }
                }
                tel_value.pop();

                result += format!("{}:{}\n", info_type.to_ascii_uppercase(), tel_value).as_str();
            },
            "adr" => {
                let mut adr_value: String = String::new();

                let adr_parts = ["street-address", "locality", "region", "postal-code",
                "country-name", "post-office-box", "extended-address"];
                for part in adr_parts.iter() {
                    if detail_map.contains_key(*part) {
                        adr_value += format!("{};", detail_map.get(*part).unwrap()).as_str();
                    }
                }
                adr_value.pop();

                result += format!("{}:{}\n", info_type.to_ascii_uppercase(), adr_value).as_str();
            },
            _ => {},
        }
    }
    result += "END:VCARD";
    if start_vcard {
        return result;
    } else {
        return "".to_string();
    }
}

pub fn parse_json(node: &Node) -> String {
    // TODO Write the logic for JSON Parsing
    return "".to_string();
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

pub mod convert;
pub mod handlebars_helpers;
pub mod path;
pub mod sys;
pub mod users;

use std::ffi::OsStr;
use std::net::{Ipv4Addr, SocketAddrV4};
use std::str::FromStr;
use std::process::{Command, Stdio};

use hcore::os;
use time;

use error::{Error, Result};

static LOGKEY: &'static str = "UT";

/// Gives us a time to stop for in seconds.
pub fn stop_time(duration: i64) -> time::Timespec {
    let current_time = time::now_utc().to_timespec();
    let wait_duration = time::Duration::seconds(duration as i64);
    let stop_time = current_time + wait_duration;
    stop_time
}

/// This function takes a string as an option.
/// - If you haven't sent a string, then we'll return (default_ip, default_port).
/// - If you sent Some(some_ip_without_port), we'll return (your IP, default_port).
/// - If you sent Some(some_ip_with_port), we'll just hand that back (your IP, your port).
pub fn parse_ip_port_with_defaults(s: Option<&str>,
                                   default_ip: &str,
                                   default_port: u16)
                                   -> Result<(String, u16)> {
    if let None = s {
        // return the default IP:Port combo
        return Ok((default_ip.to_string(), default_port));
    }

    let s = s.unwrap();

    // is it an IP + Port combo?
    match SocketAddrV4::from_str(s) {
        Ok(sa) => return Ok((sa.ip().to_string(), sa.port())),
        Err(_) => (),
    }

    // is it just an IP? if so, then append the default gossip port
    if Ipv4Addr::from_str(s).is_ok() {
        return Ok((s.to_string(), default_port));
    }

    return Err(sup_error!(Error::IPFailed));
}

#[cfg(any(target_os="linux", target_os="macos"))]
pub fn create_command<S: AsRef<OsStr>>(path: S, user: &str, group: &str) -> Command {
    let mut cmd = Command::new(path);
    use std::os::unix::process::CommandExt;
    let uid = os::users::get_uid_by_name(user).expect("Can't determine uid");
    let gid = os::users::get_gid_by_name(group).expect("Can't determine gid");

    cmd.stdin(Stdio::null())
        .stdout(Stdio::piped())
        .stderr(Stdio::piped())
        .uid(uid)
        .gid(gid);
    cmd
}

#[cfg(target_os = "windows")]
pub fn create_command<S: AsRef<OsStr>>(path: S, user: &str, group: &str) -> Command {
    let mut cmd = Command::new("powershell.exe");
    let ps_command = format!("iex $(gc {} | out-string)", path.as_ref());
    cmd.arg("-command")
        .arg(ps_command)
        .stdin(Stdio::null())
        .stdout(Stdio::piped())
        .stderr(Stdio::piped());
    cmd
}

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

    #[test]
    fn parse_ip_port_with_defaults_test() {
        let default_ip = "192.168.1.11";
        let default_port = 1234;
        // no ip/port passed in, use default ip:port
        assert!(("192.168.1.11".to_string(), 1234) ==
                parse_ip_port_with_defaults(None, default_ip, default_port).unwrap());
        // only IP passed in
        assert!(("192.168.1.99".to_string(), 1234) ==
                parse_ip_port_with_defaults(Some("192.168.1.99"), default_ip, default_port)
            .unwrap());
        // pass in IP and port
        assert!(("192.168.1.99".to_string(), 5678) ==
                parse_ip_port_with_defaults(Some("192.168.1.99:5678"), default_ip, default_port)
            .unwrap());
        // pass in something unparseable
        assert!(parse_ip_port_with_defaults(Some("foo"), default_ip, default_port).is_err());
    }
}
<commit_msg>use a to_string_lossy for writing the powershell hook invocation<commit_after>// Copyright (c) 2016 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

pub mod convert;
pub mod handlebars_helpers;
pub mod path;
pub mod sys;
pub mod users;

use std::ffi::OsStr;
use std::net::{Ipv4Addr, SocketAddrV4};
use std::str::FromStr;
use std::process::{Command, Stdio};

use hcore::os;
use time;

use error::{Error, Result};

static LOGKEY: &'static str = "UT";

/// Gives us a time to stop for in seconds.
pub fn stop_time(duration: i64) -> time::Timespec {
    let current_time = time::now_utc().to_timespec();
    let wait_duration = time::Duration::seconds(duration as i64);
    let stop_time = current_time + wait_duration;
    stop_time
}

/// This function takes a string as an option.
/// - If you haven't sent a string, then we'll return (default_ip, default_port).
/// - If you sent Some(some_ip_without_port), we'll return (your IP, default_port).
/// - If you sent Some(some_ip_with_port), we'll just hand that back (your IP, your port).
pub fn parse_ip_port_with_defaults(s: Option<&str>,
                                   default_ip: &str,
                                   default_port: u16)
                                   -> Result<(String, u16)> {
    if let None = s {
        // return the default IP:Port combo
        return Ok((default_ip.to_string(), default_port));
    }

    let s = s.unwrap();

    // is it an IP + Port combo?
    match SocketAddrV4::from_str(s) {
        Ok(sa) => return Ok((sa.ip().to_string(), sa.port())),
        Err(_) => (),
    }

    // is it just an IP? if so, then append the default gossip port
    if Ipv4Addr::from_str(s).is_ok() {
        return Ok((s.to_string(), default_port));
    }

    return Err(sup_error!(Error::IPFailed));
}

#[cfg(any(target_os="linux", target_os="macos"))]
pub fn create_command<S: AsRef<OsStr>>(path: S, user: &str, group: &str) -> Command {
    let mut cmd = Command::new(path);
    use std::os::unix::process::CommandExt;
    let uid = os::users::get_uid_by_name(user).expect("Can't determine uid");
    let gid = os::users::get_gid_by_name(group).expect("Can't determine gid");

    cmd.stdin(Stdio::null())
        .stdout(Stdio::piped())
        .stderr(Stdio::piped())
        .uid(uid)
        .gid(gid);
    cmd
}

#[cfg(target_os = "windows")]
pub fn create_command<S: AsRef<OsStr>>(path: S, user: &str, group: &str) -> Command {
    let mut cmd = Command::new("powershell.exe");
    let ps_command = format!("iex $(gc {} | out-string)", path.as_ref().to_string_lossy());
    cmd.arg("-command")
        .arg(ps_command)
        .stdin(Stdio::null())
        .stdout(Stdio::piped())
        .stderr(Stdio::piped());
    cmd
}

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

    #[test]
    fn parse_ip_port_with_defaults_test() {
        let default_ip = "192.168.1.11";
        let default_port = 1234;
        // no ip/port passed in, use default ip:port
        assert!(("192.168.1.11".to_string(), 1234) ==
                parse_ip_port_with_defaults(None, default_ip, default_port).unwrap());
        // only IP passed in
        assert!(("192.168.1.99".to_string(), 1234) ==
                parse_ip_port_with_defaults(Some("192.168.1.99"), default_ip, default_port)
            .unwrap());
        // pass in IP and port
        assert!(("192.168.1.99".to_string(), 5678) ==
                parse_ip_port_with_defaults(Some("192.168.1.99:5678"), default_ip, default_port)
            .unwrap());
        // pass in something unparseable
        assert!(parse_ip_port_with_defaults(Some("foo"), default_ip, default_port).is_err());
    }
}
<|endoftext|>