<commit_before>use super::*;

// test expr_literals
// fn foo() {
//     let _ = true;
//     let _ = false;
//     let _ = 1;
//     let _ = 2.0;
//     let _ = b'a';
//     let _ = 'b';
//     let _ = "c";
//     let _ = r"d";
//     let _ = b"e";
//     let _ = br"f";
// }
pub(crate) const LITERAL_FIRST: TokenSet = token_set![
    TRUE_KW,
    FALSE_KW,
    INT_NUMBER,
    FLOAT_NUMBER,
    BYTE,
    CHAR,
    STRING,
    RAW_STRING,
    BYTE_STRING,
    RAW_BYTE_STRING
];

pub(crate) fn literal(p: &mut Parser) -> Option<CompletedMarker> {
    if !p.at_ts(LITERAL_FIRST) {
        return None;
    }
    let m = p.start();
    p.bump();
    Some(m.complete(p, LITERAL))
}

// E.g. for after the break in `if break {}`, this should not match
pub(super) const ATOM_EXPR_FIRST: TokenSet = LITERAL_FIRST.union(paths::PATH_FIRST).union(token_set![
    L_PAREN,
    L_CURLY,
    L_BRACK,
    PIPE,
    MOVE_KW,
    IF_KW,
    WHILE_KW,
    MATCH_KW,
    UNSAFE_KW,
    RETURN_KW,
    BREAK_KW,
    CONTINUE_KW,
    LIFETIME,
]);

const EXPR_RECOVERY_SET: TokenSet = token_set![LET_KW];

pub(super) fn atom_expr(p: &mut Parser, r: Restrictions) -> Option<(CompletedMarker, BlockLike)> {
    if let Some(m) = literal(p) {
        return Some((m, BlockLike::NotBlock));
    }
    if paths::is_path_start(p) || p.at(L_ANGLE) {
        return Some(path_expr(p, r));
    }
    let la = p.nth(1);
    let done = match p.current() {
        L_PAREN => tuple_expr(p),
        L_BRACK => array_expr(p),
        PIPE => lambda_expr(p),
        MOVE_KW if la == PIPE => lambda_expr(p),
        IF_KW => if_expr(p),

        LOOP_KW => loop_expr(p, None),
        FOR_KW => for_expr(p, None),
        WHILE_KW => while_expr(p, None),
        LIFETIME if la == COLON => {
            let m = p.start();
            label(p);
            match p.current() {
                LOOP_KW => loop_expr(p, Some(m)),
                FOR_KW => for_expr(p, Some(m)),
                WHILE_KW => while_expr(p, Some(m)),
                L_CURLY => block_expr(p, Some(m)),
                _ => {
                    // test_err misplaced_label_err
                    // fn main() {
                    //     'loop: impl
                    // }
                    p.error("expected a loop");
                    m.complete(p, ERROR);
                    return None;
                }
            }
        }

        MATCH_KW => match_expr(p),
        UNSAFE_KW if la == L_CURLY => {
            let m = p.start();
            p.bump();
            block_expr(p, Some(m))
        }
        L_CURLY => block_expr(p, None),
        RETURN_KW => return_expr(p),
        CONTINUE_KW => continue_expr(p),
        BREAK_KW => break_expr(p, r),
        _ => {
            p.err_recover("expected expression", EXPR_RECOVERY_SET);
            return None;
        }
    };
    let blocklike = match done.kind() {
        IF_EXPR | WHILE_EXPR | FOR_EXPR | LOOP_EXPR | MATCH_EXPR | BLOCK_EXPR => BlockLike::Block,
        _ => BlockLike::NotBlock,
    };
    Some((done, blocklike))
}

// test tuple_expr
// fn foo() {
//     ();
//     (1);
//     (1,);
// }
fn tuple_expr(p: &mut Parser) -> CompletedMarker {
    assert!(p.at(L_PAREN));
    let m = p.start();
    p.expect(L_PAREN);

    let mut saw_comma = false;
    let mut saw_expr = false;
    while !p.at(EOF) && !p.at(R_PAREN) {
        saw_expr = true;
        if !p.at_ts(EXPR_FIRST) {
            p.error("expected expression");
            break;
        }
        expr(p);
        if !p.at(R_PAREN) {
            saw_comma = true;
            p.expect(COMMA);
        }
    }
    p.expect(R_PAREN);
    m.complete(
        p,
        if saw_expr && !saw_comma {
            PAREN_EXPR
        } else {
            TUPLE_EXPR
        },
    )
}

// test array_expr
// fn foo() {
//     [];
//     [1];
//     [1, 2,];
//     [1; 2];
// }
fn array_expr(p: &mut Parser) -> CompletedMarker {
    assert!(p.at(L_BRACK));
    let m = p.start();
    p.bump();
    if p.eat(R_BRACK) {
        return m.complete(p, ARRAY_EXPR);
    }
    expr(p);
    if p.eat(SEMI) {
        expr(p);
        p.expect(R_BRACK);
        return m.complete(p, ARRAY_EXPR);
    }
    while !p.at(EOF) && !p.at(R_BRACK) {
        p.expect(COMMA);
        if p.at(R_BRACK) {
            break;
        }
        if !p.at_ts(EXPR_FIRST) {
            p.error("expected expression");
            break;
        }
        expr(p);
    }
    p.expect(R_BRACK);
    m.complete(p, ARRAY_EXPR)
}

// test lambda_expr
// fn foo() {
//     || ();
//     || -> i32 { 92 };
//     |x| x;
//     move |x: i32,| x;
// }
fn lambda_expr(p: &mut Parser) -> CompletedMarker {
    assert!(p.at(PIPE) || (p.at(MOVE_KW) && p.nth(1) == PIPE));
    let m = p.start();
    p.eat(MOVE_KW);
    params::param_list_opt_types(p);
    if opt_fn_ret_type(p) {
        if !p.at(L_CURLY) {
            p.error("expected `{`");
        }
    }
    expr(p);
    m.complete(p, LAMBDA_EXPR)
}

// test if_expr
// fn foo() {
//     if true {};
//     if true {} else {};
//     if true {} else if false {} else {};
//     if S {};
// }
fn if_expr(p: &mut Parser) -> CompletedMarker {
    assert!(p.at(IF_KW));
    let m = p.start();
    p.bump();
    cond(p);
    block(p);
    if p.at(ELSE_KW) {
        p.bump();
        if p.at(IF_KW) {
            if_expr(p);
        } else {
            block(p);
        }
    }
    m.complete(p, IF_EXPR)
}

// test label
// fn foo() {
//     'a: loop {}
//     'b: while true {}
//     'c: for x in () {}
// }
fn label(p: &mut Parser) {
    assert!(p.at(LIFETIME) && p.nth(1) == COLON);
    let m = p.start();
    p.bump();
    p.bump();
    m.complete(p, LABEL);
}

// test loop_expr
// fn foo() {
//     loop {};
// }
fn loop_expr(p: &mut Parser, m: Option<Marker>) -> CompletedMarker {
    assert!(p.at(LOOP_KW));
    let m = m.unwrap_or_else(|| p.start());
    p.bump();
    block(p);
    m.complete(p, LOOP_EXPR)
}

// test while_expr
// fn foo() {
//     while true {};
//     while let Some(x) = it.next() {};
// }
fn while_expr(p: &mut Parser, m: Option<Marker>) -> CompletedMarker {
    assert!(p.at(WHILE_KW));
    let m = m.unwrap_or_else(|| p.start());
    p.bump();
    cond(p);
    block(p);
    m.complete(p, WHILE_EXPR)
}

// test for_expr
// fn foo() {
//     for x in [] {};
// }
fn for_expr(p: &mut Parser, m: Option<Marker>) -> CompletedMarker {
    assert!(p.at(FOR_KW));
    let m = m.unwrap_or_else(|| p.start());
    p.bump();
    patterns::pattern(p);
    p.expect(IN_KW);
    expr_no_struct(p);
    block(p);
    m.complete(p, FOR_EXPR)
}

// test cond
// fn foo() { if let Some(_) = None {} }
fn cond(p: &mut Parser) {
    let m = p.start();
    if p.eat(LET_KW) {
        patterns::pattern(p);
        p.expect(EQ);
    }
    expr_no_struct(p);
    m.complete(p, CONDITION);
}

// test match_expr
// fn foo() {
//     match () { };
//     match S {};
// }
fn match_expr(p: &mut Parser) -> CompletedMarker {
    assert!(p.at(MATCH_KW));
    let m = p.start();
    p.bump();
    expr_no_struct(p);
    if p.at(L_CURLY) {
        match_arm_list(p);
    } else {
        p.error("expected `{`")
    }
    m.complete(p, MATCH_EXPR)
}

pub(crate) fn match_arm_list(p: &mut Parser) {
    assert!(p.at(L_CURLY));
    let m = p.start();
    p.eat(L_CURLY);
    while !p.at(EOF) && !p.at(R_CURLY) {
        if p.at(L_CURLY) {
            error_block(p, "expected match arm");
            continue;
        }
        // test match_arms_commas
        // fn foo() {
        //     match () {
        //         _ => (),
        //         _ => {}
        //         _ => ()
        //     }
        // }
        if match_arm(p).is_block() {
            p.eat(COMMA);
        } else if !p.at(R_CURLY) {
            p.expect(COMMA);
        }
    }
    p.expect(R_CURLY);
    m.complete(p, MATCH_ARM_LIST);
}

// test match_arm
// fn foo() {
//     match () {
//         _ => (),
//         _ if Test > Test{field: 0} => (),
//         X | Y if Z => (),
//         | X | Y if Z => (),
//         | X => (),
//     };
// }
fn match_arm(p: &mut Parser) -> BlockLike {
    let m = p.start();
    p.eat(PIPE);
    patterns::pattern_r(p, TokenSet::empty());
    while p.eat(PIPE) {
        patterns::pattern(p);
    }
    if p.eat(IF_KW) {
        expr(p);
    }
    p.expect(FAT_ARROW);
    let ret = expr_stmt(p);
    m.complete(p, MATCH_ARM);
    ret
}

// test block_expr
// fn foo() {
//     {};
//     unsafe {};
//     'label: {};
// }
fn block_expr(p: &mut Parser, m: Option<Marker>) -> CompletedMarker {
    assert!(p.at(L_CURLY));
    let m = m.unwrap_or_else(|| p.start());
    block(p);
    m.complete(p, BLOCK_EXPR)
}

// test return_expr
// fn foo() {
//     return;
//     return 92;
// }
fn return_expr(p: &mut Parser) -> CompletedMarker {
    assert!(p.at(RETURN_KW));
    let m = p.start();
    p.bump();
    if p.at_ts(EXPR_FIRST) {
        expr(p);
    }
    m.complete(p, RETURN_EXPR)
}

// test continue_expr
// fn foo() {
//     loop {
//         continue;
//         continue 'l;
//     }
// }
fn continue_expr(p: &mut Parser) -> CompletedMarker {
    assert!(p.at(CONTINUE_KW));
    let m = p.start();
    p.bump();
    p.eat(LIFETIME);
    m.complete(p, CONTINUE_EXPR)
}

// test break_expr
// fn foo() {
//     loop {
//         break;
//         break 'l;
//         break 92;
//         break 'l 92;
//     }
// }
fn break_expr(p: &mut Parser, r: Restrictions) -> CompletedMarker {
    assert!(p.at(BREAK_KW));
    let m = p.start();
    p.bump();
    p.eat(LIFETIME);
    // test break_ambiguity
    // fn foo(){
    //     if break {}
    //     while break {}
    //     for i in break {}
    //     match break {}
    // }
    if p.at_ts(EXPR_FIRST) && !(r.forbid_structs && p.at(L_CURLY)) {
        expr(p);
    }
    m.complete(p, BREAK_EXPR)
}
<commit_msg>Reformat<commit_after>use super::*;

// test expr_literals
// fn foo() {
//     let _ = true;
//     let _ = false;
//     let _ = 1;
//     let _ = 2.0;
//     let _ = b'a';
//     let _ = 'b';
//     let _ = "c";
//     let _ = r"d";
//     let _ = b"e";
//     let _ = br"f";
// }
pub(crate) const LITERAL_FIRST: TokenSet = token_set![
    TRUE_KW,
    FALSE_KW,
    INT_NUMBER,
    FLOAT_NUMBER,
    BYTE,
    CHAR,
    STRING,
    RAW_STRING,
    BYTE_STRING,
    RAW_BYTE_STRING
];

pub(crate) fn literal(p: &mut Parser) -> Option<CompletedMarker> {
    if !p.at_ts(LITERAL_FIRST) {
        return None;
    }
    let m = p.start();
    p.bump();
    Some(m.complete(p, LITERAL))
}

// E.g. for after the break in `if break {}`, this should not match
pub(super) const ATOM_EXPR_FIRST: TokenSet =
    LITERAL_FIRST.union(paths::PATH_FIRST).union(token_set![
        L_PAREN,
        L_CURLY,
        L_BRACK,
        PIPE,
        MOVE_KW,
        IF_KW,
        WHILE_KW,
        MATCH_KW,
        UNSAFE_KW,
        RETURN_KW,
        BREAK_KW,
        CONTINUE_KW,
        LIFETIME,
    ]);

const EXPR_RECOVERY_SET: TokenSet = token_set![LET_KW];

pub(super) fn atom_expr(p: &mut Parser, r: Restrictions) -> Option<(CompletedMarker, BlockLike)> {
    if let Some(m) = literal(p) {
        return Some((m, BlockLike::NotBlock));
    }
    if paths::is_path_start(p) || p.at(L_ANGLE) {
        return Some(path_expr(p, r));
    }
    let la = p.nth(1);
    let done = match p.current() {
        L_PAREN => tuple_expr(p),
        L_BRACK => array_expr(p),
        PIPE => lambda_expr(p),
        MOVE_KW if la == PIPE => lambda_expr(p),
        IF_KW => if_expr(p),

        LOOP_KW => loop_expr(p, None),
        FOR_KW => for_expr(p, None),
        WHILE_KW => while_expr(p, None),
        LIFETIME if la == COLON => {
            let m = p.start();
            label(p);
            match p.current() {
                LOOP_KW => loop_expr(p, Some(m)),
                FOR_KW => for_expr(p, Some(m)),
                WHILE_KW => while_expr(p, Some(m)),
                L_CURLY => block_expr(p, Some(m)),
                _ => {
                    // test_err misplaced_label_err
                    // fn main() {
                    //     'loop: impl
                    // }
                    p.error("expected a loop");
                    m.complete(p, ERROR);
                    return None;
                }
            }
        }

        MATCH_KW => match_expr(p),
        UNSAFE_KW if la == L_CURLY => {
            let m = p.start();
            p.bump();
            block_expr(p, Some(m))
        }
        L_CURLY => block_expr(p, None),
        RETURN_KW => return_expr(p),
        CONTINUE_KW => continue_expr(p),
        BREAK_KW => break_expr(p, r),
        _ => {
            p.err_recover("expected expression", EXPR_RECOVERY_SET);
            return None;
        }
    };
    let blocklike = match done.kind() {
        IF_EXPR | WHILE_EXPR | FOR_EXPR | LOOP_EXPR | MATCH_EXPR | BLOCK_EXPR => BlockLike::Block,
        _ => BlockLike::NotBlock,
    };
    Some((done, blocklike))
}

// test tuple_expr
// fn foo() {
//     ();
//     (1);
//     (1,);
// }
fn tuple_expr(p: &mut Parser) -> CompletedMarker {
    assert!(p.at(L_PAREN));
    let m = p.start();
    p.expect(L_PAREN);

    let mut saw_comma = false;
    let mut saw_expr = false;
    while !p.at(EOF) && !p.at(R_PAREN) {
        saw_expr = true;
        if !p.at_ts(EXPR_FIRST) {
            p.error("expected expression");
            break;
        }
        expr(p);
        if !p.at(R_PAREN) {
            saw_comma = true;
            p.expect(COMMA);
        }
    }
    p.expect(R_PAREN);
    m.complete(
        p,
        if saw_expr && !saw_comma {
            PAREN_EXPR
        } else {
            TUPLE_EXPR
        },
    )
}

// test array_expr
// fn foo() {
//     [];
//     [1];
//     [1, 2,];
//     [1; 2];
// }
fn array_expr(p: &mut Parser) -> CompletedMarker {
    assert!(p.at(L_BRACK));
    let m = p.start();
    p.bump();
    if p.eat(R_BRACK) {
        return m.complete(p, ARRAY_EXPR);
    }
    expr(p);
    if p.eat(SEMI) {
        expr(p);
        p.expect(R_BRACK);
        return m.complete(p, ARRAY_EXPR);
    }
    while !p.at(EOF) && !p.at(R_BRACK) {
        p.expect(COMMA);
        if p.at(R_BRACK) {
            break;
        }
        if !p.at_ts(EXPR_FIRST) {
            p.error("expected expression");
            break;
        }
        expr(p);
    }
    p.expect(R_BRACK);
    m.complete(p, ARRAY_EXPR)
}

// test lambda_expr
// fn foo() {
//     || ();
//     || -> i32 { 92 };
//     |x| x;
//     move |x: i32,| x;
// }
fn lambda_expr(p: &mut Parser) -> CompletedMarker {
    assert!(p.at(PIPE) || (p.at(MOVE_KW) && p.nth(1) == PIPE));
    let m = p.start();
    p.eat(MOVE_KW);
    params::param_list_opt_types(p);
    if opt_fn_ret_type(p) {
        if !p.at(L_CURLY) {
            p.error("expected `{`");
        }
    }
    expr(p);
    m.complete(p, LAMBDA_EXPR)
}

// test if_expr
// fn foo() {
//     if true {};
//     if true {} else {};
//     if true {} else if false {} else {};
//     if S {};
// }
fn if_expr(p: &mut Parser) -> CompletedMarker {
    assert!(p.at(IF_KW));
    let m = p.start();
    p.bump();
    cond(p);
    block(p);
    if p.at(ELSE_KW) {
        p.bump();
        if p.at(IF_KW) {
            if_expr(p);
        } else {
            block(p);
        }
    }
    m.complete(p, IF_EXPR)
}

// test label
// fn foo() {
//     'a: loop {}
//     'b: while true {}
//     'c: for x in () {}
// }
fn label(p: &mut Parser) {
    assert!(p.at(LIFETIME) && p.nth(1) == COLON);
    let m = p.start();
    p.bump();
    p.bump();
    m.complete(p, LABEL);
}

// test loop_expr
// fn foo() {
//     loop {};
// }
fn loop_expr(p: &mut Parser, m: Option<Marker>) -> CompletedMarker {
    assert!(p.at(LOOP_KW));
    let m = m.unwrap_or_else(|| p.start());
    p.bump();
    block(p);
    m.complete(p, LOOP_EXPR)
}

// test while_expr
// fn foo() {
//     while true {};
//     while let Some(x) = it.next() {};
// }
fn while_expr(p: &mut Parser, m: Option<Marker>) -> CompletedMarker {
    assert!(p.at(WHILE_KW));
    let m = m.unwrap_or_else(|| p.start());
    p.bump();
    cond(p);
    block(p);
    m.complete(p, WHILE_EXPR)
}

// test for_expr
// fn foo() {
//     for x in [] {};
// }
fn for_expr(p: &mut Parser, m: Option<Marker>) -> CompletedMarker {
    assert!(p.at(FOR_KW));
    let m = m.unwrap_or_else(|| p.start());
    p.bump();
    patterns::pattern(p);
    p.expect(IN_KW);
    expr_no_struct(p);
    block(p);
    m.complete(p, FOR_EXPR)
}

// test cond
// fn foo() { if let Some(_) = None {} }
fn cond(p: &mut Parser) {
    let m = p.start();
    if p.eat(LET_KW) {
        patterns::pattern(p);
        p.expect(EQ);
    }
    expr_no_struct(p);
    m.complete(p, CONDITION);
}

// test match_expr
// fn foo() {
//     match () { };
//     match S {};
// }
fn match_expr(p: &mut Parser) -> CompletedMarker {
    assert!(p.at(MATCH_KW));
    let m = p.start();
    p.bump();
    expr_no_struct(p);
    if p.at(L_CURLY) {
        match_arm_list(p);
    } else {
        p.error("expected `{`")
    }
    m.complete(p, MATCH_EXPR)
}

pub(crate) fn match_arm_list(p: &mut Parser) {
    assert!(p.at(L_CURLY));
    let m = p.start();
    p.eat(L_CURLY);
    while !p.at(EOF) && !p.at(R_CURLY) {
        if p.at(L_CURLY) {
            error_block(p, "expected match arm");
            continue;
        }
        // test match_arms_commas
        // fn foo() {
        //     match () {
        //         _ => (),
        //         _ => {}
        //         _ => ()
        //     }
        // }
        if match_arm(p).is_block() {
            p.eat(COMMA);
        } else if !p.at(R_CURLY) {
            p.expect(COMMA);
        }
    }
    p.expect(R_CURLY);
    m.complete(p, MATCH_ARM_LIST);
}

// test match_arm
// fn foo() {
//     match () {
//         _ => (),
//         _ if Test > Test{field: 0} => (),
//         X | Y if Z => (),
//         | X | Y if Z => (),
//         | X => (),
//     };
// }
fn match_arm(p: &mut Parser) -> BlockLike {
    let m = p.start();
    p.eat(PIPE);
    patterns::pattern_r(p, TokenSet::empty());
    while p.eat(PIPE) {
        patterns::pattern(p);
    }
    if p.eat(IF_KW) {
        expr(p);
    }
    p.expect(FAT_ARROW);
    let ret = expr_stmt(p);
    m.complete(p, MATCH_ARM);
    ret
}

// test block_expr
// fn foo() {
//     {};
//     unsafe {};
//     'label: {};
// }
fn block_expr(p: &mut Parser, m: Option<Marker>) -> CompletedMarker {
    assert!(p.at(L_CURLY));
    let m = m.unwrap_or_else(|| p.start());
    block(p);
    m.complete(p, BLOCK_EXPR)
}

// test return_expr
// fn foo() {
//     return;
//     return 92;
// }
fn return_expr(p: &mut Parser) -> CompletedMarker {
    assert!(p.at(RETURN_KW));
    let m = p.start();
    p.bump();
    if p.at_ts(EXPR_FIRST) {
        expr(p);
    }
    m.complete(p, RETURN_EXPR)
}

// test continue_expr
// fn foo() {
//     loop {
//         continue;
//         continue 'l;
//     }
// }
fn continue_expr(p: &mut Parser) -> CompletedMarker {
    assert!(p.at(CONTINUE_KW));
    let m = p.start();
    p.bump();
    p.eat(LIFETIME);
    m.complete(p, CONTINUE_EXPR)
}

// test break_expr
// fn foo() {
//     loop {
//         break;
//         break 'l;
//         break 92;
//         break 'l 92;
//     }
// }
fn break_expr(p: &mut Parser, r: Restrictions) -> CompletedMarker {
    assert!(p.at(BREAK_KW));
    let m = p.start();
    p.bump();
    p.eat(LIFETIME);
    // test break_ambiguity
    // fn foo(){
    //     if break {}
    //     while break {}
    //     for i in break {}
    //     match break {}
    // }
    if p.at_ts(EXPR_FIRST) && !(r.forbid_structs && p.at(L_CURLY)) {
        expr(p);
    }
    m.complete(p, BREAK_EXPR)
}
<|endoftext|>


<commit_before>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use font_descriptor;
use font_descriptor::{CTFontDescriptor, CTFontDescriptorCreateMatchingFontDescriptors};
use font_manager::CTFontManagerCopyAvailableFontFamilyNames;

use core_foundation::array::{CFArray, CFArrayRef};
use core_foundation::base::{CFRelease, CFRetain, CFTypeID, CFTypeRef, TCFType};
use core_foundation::dictionary::{CFDictionary, CFDictionaryRef};
use core_foundation::number::CFNumber;
use core_foundation::set::CFSet;
use core_foundation::string::{CFString, CFStringRef};

use libc::c_void;
use std::mem;
use std::ptr;

#[repr(C)]
pub struct __CTFontCollection(c_void);

pub type CTFontCollectionRef = *const __CTFontCollection;

#[derive(Debug)]
pub struct CTFontCollection {
    obj: CTFontCollectionRef,
}

impl Drop for CTFontCollection {
    fn drop(&mut self) {
        unsafe {
            CFRelease(self.as_CFTypeRef())
        }
    }
}

impl TCFType for CTFontCollection {
    type Ref = CTFontCollectionRef;

    #[inline]
    fn as_concrete_TypeRef(&self) -> CTFontCollectionRef {
        self.obj
    }

    #[inline]
    unsafe fn wrap_under_get_rule(reference: CTFontCollectionRef) -> CTFontCollection {
        let reference: CTFontCollectionRef = mem::transmute(CFRetain(mem::transmute(reference)));
        TCFType::wrap_under_create_rule(reference)
    }

    #[inline]
    unsafe fn wrap_under_create_rule(obj: CTFontCollectionRef) -> CTFontCollection {
        CTFontCollection {
            obj: obj,
        }
    }

    #[inline]
    fn as_CFTypeRef(&self) -> CFTypeRef {
        unsafe {
            mem::transmute(self.as_concrete_TypeRef())
        }
    }

    #[inline]
    fn type_id() -> CFTypeID {
        unsafe {
            CTFontCollectionGetTypeID()
        }
    }
}

impl CTFontCollection {
    pub fn get_descriptors(&self) -> CFArray {
        // surprise! this function follows the Get rule, despite being named *Create*.
        // So we have to addRef it to avoid CTFontCollection from double freeing it later.
        unsafe {
            TCFType::wrap_under_get_rule(CTFontCollectionCreateMatchingFontDescriptors(self.obj))
        }
    }
}

pub fn new_from_descriptors(descs: &CFArray) -> CTFontCollection {
    unsafe {
        let key: CFString = TCFType::wrap_under_get_rule(kCTFontCollectionRemoveDuplicatesOption);
        let value = CFNumber::from_i64(1);
        let options = CFDictionary::from_CFType_pairs(&[ (key.as_CFType(), value.as_CFType()) ]);
        let font_collection_ref =
            CTFontCollectionCreateWithFontDescriptors(descs.as_concrete_TypeRef(),
                                                      options.as_concrete_TypeRef());
        TCFType::wrap_under_create_rule(font_collection_ref)
    }
}

pub fn create_for_all_families() -> CTFontCollection {
    unsafe {
        let key: CFString = TCFType::wrap_under_get_rule(kCTFontCollectionRemoveDuplicatesOption);
        let value = CFNumber::from_i64(1);
        let options = CFDictionary::from_CFType_pairs(&[ (key.as_CFType(), value.as_CFType()) ]);
        let font_collection_ref =
            CTFontCollectionCreateFromAvailableFonts(options.as_concrete_TypeRef());
        TCFType::wrap_under_create_rule(font_collection_ref)
    }
}

pub fn create_for_family(family: &str) -> Option<CTFontCollection> {
    use font_descriptor::kCTFontFamilyNameAttribute;

    unsafe {
        let family_attr: CFString = TCFType::wrap_under_get_rule(kCTFontFamilyNameAttribute);
        let family_name: CFString = family.parse().unwrap();
        let specified_attrs = CFDictionary::from_CFType_pairs(&[
            (family_attr.as_CFType(), family_name.as_CFType())
        ]);

        let wildcard_desc: CTFontDescriptor =
            font_descriptor::new_from_attributes(&specified_attrs);
        let mandatory_attrs = CFSet::from_slice(&[ family_attr.as_CFType() ]);
        let matched_descs = CTFontDescriptorCreateMatchingFontDescriptors(
                wildcard_desc.as_concrete_TypeRef(),
                mandatory_attrs.as_concrete_TypeRef());
        if matched_descs == ptr::null() {
            return None;
        }
        let matched_descs: CFArray = TCFType::wrap_under_create_rule(matched_descs);
        // I suppose one doesn't even need the CTFontCollection object at this point.
        // But we stick descriptors into and out of it just to provide a nice wrapper API.
        Some(new_from_descriptors(&matched_descs))
    }
}

pub fn get_family_names() -> CFArray {
    unsafe {
        TCFType::wrap_under_create_rule(CTFontManagerCopyAvailableFontFamilyNames())
    }
}

extern {
    /*
     * CTFontCollection.h
     */

    static kCTFontCollectionRemoveDuplicatesOption: CFStringRef;

    //fn CTFontCollectionCreateCopyWithFontDescriptors(original: CTFontCollectionRef,
    //                                                 descriptors: CFArrayRef,
    //                                                 options: CFDictionaryRef) -> CTFontCollectionRef;
    fn CTFontCollectionCreateFromAvailableFonts(options: CFDictionaryRef) -> CTFontCollectionRef;
    // this stupid function doesn't actually do any wildcard expansion;
    // it just chooses the best match. Use
    // CTFontDescriptorCreateMatchingDescriptors instead.
    fn CTFontCollectionCreateMatchingFontDescriptors(collection: CTFontCollectionRef) -> CFArrayRef;
    fn CTFontCollectionCreateWithFontDescriptors(descriptors: CFArrayRef,
                                                 options: CFDictionaryRef) -> CTFontCollectionRef;
    //fn CTFontCollectionCreateMatchingFontDescriptorsSortedWithCallback;
    fn CTFontCollectionGetTypeID() -> CFTypeID;
}
<commit_msg>Update to new CFNumber constructor<commit_after>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use font_descriptor;
use font_descriptor::{CTFontDescriptor, CTFontDescriptorCreateMatchingFontDescriptors};
use font_manager::CTFontManagerCopyAvailableFontFamilyNames;

use core_foundation::array::{CFArray, CFArrayRef};
use core_foundation::base::{CFRelease, CFRetain, CFTypeID, CFTypeRef, TCFType};
use core_foundation::dictionary::{CFDictionary, CFDictionaryRef};
use core_foundation::number::CFNumber;
use core_foundation::set::CFSet;
use core_foundation::string::{CFString, CFStringRef};

use libc::c_void;
use std::mem;
use std::ptr;

#[repr(C)]
pub struct __CTFontCollection(c_void);

pub type CTFontCollectionRef = *const __CTFontCollection;

#[derive(Debug)]
pub struct CTFontCollection {
    obj: CTFontCollectionRef,
}

impl Drop for CTFontCollection {
    fn drop(&mut self) {
        unsafe {
            CFRelease(self.as_CFTypeRef())
        }
    }
}

impl TCFType for CTFontCollection {
    type Ref = CTFontCollectionRef;

    #[inline]
    fn as_concrete_TypeRef(&self) -> CTFontCollectionRef {
        self.obj
    }

    #[inline]
    unsafe fn wrap_under_get_rule(reference: CTFontCollectionRef) -> CTFontCollection {
        let reference: CTFontCollectionRef = mem::transmute(CFRetain(mem::transmute(reference)));
        TCFType::wrap_under_create_rule(reference)
    }

    #[inline]
    unsafe fn wrap_under_create_rule(obj: CTFontCollectionRef) -> CTFontCollection {
        CTFontCollection {
            obj: obj,
        }
    }

    #[inline]
    fn as_CFTypeRef(&self) -> CFTypeRef {
        unsafe {
            mem::transmute(self.as_concrete_TypeRef())
        }
    }

    #[inline]
    fn type_id() -> CFTypeID {
        unsafe {
            CTFontCollectionGetTypeID()
        }
    }
}

impl CTFontCollection {
    pub fn get_descriptors(&self) -> CFArray {
        // surprise! this function follows the Get rule, despite being named *Create*.
        // So we have to addRef it to avoid CTFontCollection from double freeing it later.
        unsafe {
            TCFType::wrap_under_get_rule(CTFontCollectionCreateMatchingFontDescriptors(self.obj))
        }
    }
}

pub fn new_from_descriptors(descs: &CFArray) -> CTFontCollection {
    unsafe {
        let key: CFString = TCFType::wrap_under_get_rule(kCTFontCollectionRemoveDuplicatesOption);
        let value = CFNumber::from(1i64);
        let options = CFDictionary::from_CFType_pairs(&[ (key.as_CFType(), value.as_CFType()) ]);
        let font_collection_ref =
            CTFontCollectionCreateWithFontDescriptors(descs.as_concrete_TypeRef(),
                                                      options.as_concrete_TypeRef());
        TCFType::wrap_under_create_rule(font_collection_ref)
    }
}

pub fn create_for_all_families() -> CTFontCollection {
    unsafe {
        let key: CFString = TCFType::wrap_under_get_rule(kCTFontCollectionRemoveDuplicatesOption);
        let value = CFNumber::from(1i64);
        let options = CFDictionary::from_CFType_pairs(&[ (key.as_CFType(), value.as_CFType()) ]);
        let font_collection_ref =
            CTFontCollectionCreateFromAvailableFonts(options.as_concrete_TypeRef());
        TCFType::wrap_under_create_rule(font_collection_ref)
    }
}

pub fn create_for_family(family: &str) -> Option<CTFontCollection> {
    use font_descriptor::kCTFontFamilyNameAttribute;

    unsafe {
        let family_attr: CFString = TCFType::wrap_under_get_rule(kCTFontFamilyNameAttribute);
        let family_name: CFString = family.parse().unwrap();
        let specified_attrs = CFDictionary::from_CFType_pairs(&[
            (family_attr.as_CFType(), family_name.as_CFType())
        ]);

        let wildcard_desc: CTFontDescriptor =
            font_descriptor::new_from_attributes(&specified_attrs);
        let mandatory_attrs = CFSet::from_slice(&[ family_attr.as_CFType() ]);
        let matched_descs = CTFontDescriptorCreateMatchingFontDescriptors(
                wildcard_desc.as_concrete_TypeRef(),
                mandatory_attrs.as_concrete_TypeRef());
        if matched_descs == ptr::null() {
            return None;
        }
        let matched_descs: CFArray = TCFType::wrap_under_create_rule(matched_descs);
        // I suppose one doesn't even need the CTFontCollection object at this point.
        // But we stick descriptors into and out of it just to provide a nice wrapper API.
        Some(new_from_descriptors(&matched_descs))
    }
}

pub fn get_family_names() -> CFArray {
    unsafe {
        TCFType::wrap_under_create_rule(CTFontManagerCopyAvailableFontFamilyNames())
    }
}

extern {
    /*
     * CTFontCollection.h
     */

    static kCTFontCollectionRemoveDuplicatesOption: CFStringRef;

    //fn CTFontCollectionCreateCopyWithFontDescriptors(original: CTFontCollectionRef,
    //                                                 descriptors: CFArrayRef,
    //                                                 options: CFDictionaryRef) -> CTFontCollectionRef;
    fn CTFontCollectionCreateFromAvailableFonts(options: CFDictionaryRef) -> CTFontCollectionRef;
    // this stupid function doesn't actually do any wildcard expansion;
    // it just chooses the best match. Use
    // CTFontDescriptorCreateMatchingDescriptors instead.
    fn CTFontCollectionCreateMatchingFontDescriptors(collection: CTFontCollectionRef) -> CFArrayRef;
    fn CTFontCollectionCreateWithFontDescriptors(descriptors: CFArrayRef,
                                                 options: CFDictionaryRef) -> CTFontCollectionRef;
    //fn CTFontCollectionCreateMatchingFontDescriptorsSortedWithCallback;
    fn CTFontCollectionGetTypeID() -> CFTypeID;
}
<|endoftext|>


<commit_before>#![cfg_attr(feature = "cargo-clippy", allow(needless_pass_by_value))]
//! This is a webui about to replace the TUI, to be nice, better accessable, and
//! new technology using websockets

mod actors;
mod json;
mod pages;
mod rest_mod;

use super::{
    super::{
        common::{config, paths::SearchPath},
        ctrl::InternalUiMsg,
        net::subs::peer_representation::PeerRepresentation,
    },
    CollectionPathAlive,
};
use actix::{prelude::Addr, Actor};
use actix_web::{
    web::{self, HttpResponse},
    App, Error, HttpRequest, HttpServer,
};
use actix_web_actors::ws;
use actors::{ActorSyncStartup, ActorWSServerMonitor, ActorWebSocket, MRegisterWSClient};
use crossbeam::sync::WaitGroup;
use if_addrs;
use std::{
    io,
    net::IpAddr,
    sync::{mpsc::Receiver, Arc, Mutex},
};

pub struct WebServerState {
    id: PeerRepresentation,
    nr_connections: Arc<Mutex<usize>>,
    web_port: u16,
}

pub struct WebUI {
    id: PeerRepresentation,
    #[allow(dead_code)]
    serve_others: bool,
    paths: Arc<Mutex<SearchPath>>,
}

impl WebUI {
    pub fn new(id: PeerRepresentation, serve_others: bool, paths: Arc<Mutex<SearchPath>>) -> Self {
        Self {
            id,
            serve_others,
            paths,
        }
    }

    pub async fn run(
        &self,
        receiver: Receiver<InternalUiMsg>,
        wait_ui_sync: Option<WaitGroup>,
        web_port: u16,
    ) -> io::Result<()> {
        let connection_count = Arc::new(Mutex::new(0));
        let path_arc = self.paths.clone();

        let local_addresses = if_addrs::get_if_addrs().unwrap();

        // data
        let initial_state = Arc::new(Mutex::new(WebServerState {
            id: self.id.clone(),
            nr_connections: connection_count.clone(),
            web_port,
        }));

        // very important: after this the actix system, message loop,
        // whatever ... is UP!!!
        let sys = actix::System::new("http-server");

        let web_socket_handler = Arc::new(Mutex::new(
            ActorWSServerMonitor {
                receiver,
                listeners: vec![],
                paths: self.paths.clone(),
            }
            .start(),
        ));

        let address_for_sync_startup =
            ActorSyncStartup::new(wait_ui_sync, web_socket_handler.lock().unwrap().clone()).start();
        let startup_actor_handle = Arc::new(Mutex::new(address_for_sync_startup));

        // count bindings
        let mut nr_bindings: usize = 0;

        // take all local addresses and start if necessary
        // one server with multiple binds
        let web_server = local_addresses
            .into_iter()
            .filter(|ipaddr| {
                let name = ipaddr.name.clone();
                // only use loopback addresses no 2nd ethernet cards
                if ipaddr.addr.is_loopback() {
                    info!(
                        "{} {:?} is a loopback device, good!",
                        ipaddr.addr.ip().to_string(),
                        name
                    );
                    true
                } else {
                    warn!("{} is not a loopback device!", ipaddr.addr.ip().to_string());
                    true
                }
            })
            .fold(
                Ok(HttpServer::new(move || {
                    App::new()
                        // each server has an initial state (e.g. 0 connections)
                        .data(initial_state.clone())
                        .data(web_socket_handler.clone())
                        .data(startup_actor_handle.clone())
                        .data(path_arc.clone())
                        .service(web::resource("/app.js").to(pages::js_app))
                        .default_service(web::resource("").to(pages::single_page))
                        .service(web::resource("peer_page.html").to(|| {
                            HttpResponse::Ok()
                                .content_type("text/html; charset=utf-8")
                                .body(*config::webui::PEER_PAGE)
                        }))
                        .service(web::resource("/jquery.min.js").to(|| {
                            HttpResponse::Ok()
                                .content_type("application/javascript; charset=utf-8")
                                .body(*config::webui::jquery::JS_JQUERY)
                        }))
                        .service(web::resource("/ws_events_dispatcher.js").to(|| {
                            HttpResponse::Ok()
                                .content_type("application/javascript; charset=utf-8")
                                .body(*config::webui::JS_WS_EVENT_DISPATCHER)
                        }))
                        .service(web::resource("favicon.png").to(|| {
                            HttpResponse::Ok()
                                .content_type("image/png")
                                .body(*config::webui::FAVICON)
                        }))
                        .service(web::resource("sheep.svg").to(|| {
                            HttpResponse::Ok()
                                .content_type("image/svg+xml")
                                .body(*config::webui::PIC_SHEEP)
                        }))
                        .service(
                            web::resource("/css/{name}").route(web::get().to(pages::bootstrap_css)),
                        )
                        .service(
                            web::resource("/js/{name}").route(web::get().to(pages::bootstrap_js)),
                        )
                        .service(web::resource("/ws").route(web::get().to(WebUI::websocket_answer)))
                })),
                |web_server_binding_chain: Result<HttpServer<_, _, _, _>, io::Error>, ipaddr| {
                    web_server_binding_chain.and_then(|webserver| {
                        let bind_format = match ipaddr.addr.ip() {
                            IpAddr::V4(ipv4) => format!("{}:{:?}", ipv4.to_string(), web_port),
                            IpAddr::V6(ipv6) => format!("{}:{:?}", ipv6.to_string(), web_port),
                        };
                        let try_bind = webserver.bind(bind_format.clone()).map_err(|error| {
                            error!("binding with ip ({:?}): {:?}", bind_format, error);
                            error
                        })?;
                        nr_bindings += 1;
                        Ok(try_bind)
                    })
                },
            );

        if nr_bindings > 0 {
            web_server
                .map_err(|error| {
                    // clean up
                    actix::System::current().stop();
                    error!("webserver could not start up");
                    error
                })?
                .run();
            sys.run()
        } else {
            // no bindings, no nothing
            error!("No bindings to any http addresses!");
            Err(io::Error::new(
                io::ErrorKind::Other,
                format!("no bindings to port {}", &web_port),
            ))
        }
    }

    async fn websocket_answer(
        req: HttpRequest,
        stream: web::Payload,
        data_monitor: web::Data<Arc<Mutex<Addr<ActorWSServerMonitor>>>>,
        data_sync: web::Data<Arc<Mutex<Addr<ActorSyncStartup>>>>,
        data_path: web::Data<Arc<Mutex<SearchPath>>>,
    ) -> Result<HttpResponse, Error> {
        trace!("new websocket answered!");
        let (addr, res) = ws::start_with_addr(
            ActorWebSocket {
                starter: data_sync.lock().unwrap().clone(),
                paths: data_path.get_ref().clone(),
            },
            &req,
            stream,
        )?;
        data_monitor
            .lock()
            .unwrap()
            .do_send(MRegisterWSClient { addr });

        Ok(res)
    }
}
<commit_msg>[documentation] added more description<commit_after>#![cfg_attr(feature = "cargo-clippy", allow(needless_pass_by_value))]
//! This is a webui about to replace the TUI, to be nice, better accessable, and
//! new technology using websockets

mod actors;
mod json;
mod pages;
mod rest_mod;

use super::{
    super::{
        common::{config, paths::SearchPath},
        ctrl::InternalUiMsg,
        net::subs::peer_representation::PeerRepresentation,
    },
    CollectionPathAlive,
};
use actix::{prelude::Addr, Actor};
use actix_web::{
    web::{self, HttpResponse},
    App, Error, HttpRequest, HttpServer,
};
use actix_web_actors::ws;
use actors::{ActorSyncStartup, ActorWSServerMonitor, ActorWebSocket, MRegisterWSClient};
use crossbeam::sync::WaitGroup;
use if_addrs;
use std::{
    io,
    net::IpAddr,
    sync::{mpsc::Receiver, Arc, Mutex},
};

/// Data of the webserver
pub struct WebServerState {
    id: PeerRepresentation,
    nr_connections: Arc<Mutex<usize>>,
    web_port: u16,
}

/// WebUI keep data
pub struct WebUI {
    id: PeerRepresentation,
    paths: Arc<Mutex<SearchPath>>,
    #[allow(dead_code)]
    serve_others: bool, //todo: use it
}

impl WebUI {
    pub fn new(id: PeerRepresentation, serve_others: bool, paths: Arc<Mutex<SearchPath>>) -> Self {
        Self {
            id,
            serve_others,
            paths,
        }
    }

    pub async fn run(
        &self,
        receiver: Receiver<InternalUiMsg>,
        wait_ui_sync: Option<WaitGroup>,
        web_port: u16,
    ) -> io::Result<()> {
        let connection_count = Arc::new(Mutex::new(0));
        let path_arc = self.paths.clone();

        let local_addresses = if_addrs::get_if_addrs().unwrap();

        // data
        let initial_state = Arc::new(Mutex::new(WebServerState {
            id: self.id.clone(),
            nr_connections: connection_count.clone(),
            web_port,
        }));

        // very important: after this the actix system, message loop,
        // whatever ... is UP!!!
        let sys = actix::System::new("http-server");

        let web_socket_handler = Arc::new(Mutex::new(
            ActorWSServerMonitor {
                receiver,
                listeners: vec![],
                paths: self.paths.clone(),
            }
            .start(),
        ));

        let address_for_sync_startup =
            ActorSyncStartup::new(wait_ui_sync, web_socket_handler.lock().unwrap().clone()).start();
        let startup_actor_handle = Arc::new(Mutex::new(address_for_sync_startup));

        // count bindings
        let mut nr_bindings: usize = 0;

        // take all local addresses and start if necessary
        // one server with multiple binds
        let web_server = local_addresses
            .into_iter()
            .filter(|ipaddr| {
                let name = ipaddr.name.clone();
                // only use loopback addresses no 2nd ethernet cards
                if ipaddr.addr.is_loopback() {
                    info!(
                        "{} {:?} is a loopback device, good!",
                        ipaddr.addr.ip().to_string(),
                        name
                    );
                    true
                } else {
                    warn!("{} is not a loopback device!", ipaddr.addr.ip().to_string());
                    true
                }
            })
            .fold(
                Ok(HttpServer::new(move || {
                    App::new()
                        // each server has an initial state (e.g. 0 connections)
                        .data(initial_state.clone())
                        .data(web_socket_handler.clone())
                        .data(startup_actor_handle.clone())
                        .data(path_arc.clone())
                        .service(web::resource("/app.js").to(pages::js_app))
                        .default_service(web::resource("").to(pages::single_page))
                        .service(web::resource("peer_page.html").to(|| {
                            HttpResponse::Ok()
                                .content_type("text/html; charset=utf-8")
                                .body(*config::webui::PEER_PAGE)
                        }))
                        .service(web::resource("/jquery.min.js").to(|| {
                            HttpResponse::Ok()
                                .content_type("application/javascript; charset=utf-8")
                                .body(*config::webui::jquery::JS_JQUERY)
                        }))
                        .service(web::resource("/ws_events_dispatcher.js").to(|| {
                            HttpResponse::Ok()
                                .content_type("application/javascript; charset=utf-8")
                                .body(*config::webui::JS_WS_EVENT_DISPATCHER)
                        }))
                        .service(web::resource("favicon.png").to(|| {
                            HttpResponse::Ok()
                                .content_type("image/png")
                                .body(*config::webui::FAVICON)
                        }))
                        .service(web::resource("sheep.svg").to(|| {
                            HttpResponse::Ok()
                                .content_type("image/svg+xml")
                                .body(*config::webui::PIC_SHEEP)
                        }))
                        .service(
                            web::resource("/css/{name}").route(web::get().to(pages::bootstrap_css)),
                        )
                        .service(
                            web::resource("/js/{name}").route(web::get().to(pages::bootstrap_js)),
                        )
                        .service(web::resource("/ws").route(web::get().to(WebUI::websocket_answer)))
                })),
                |web_server_binding_chain: Result<HttpServer<_, _, _, _>, io::Error>, ipaddr| {
                    web_server_binding_chain.and_then(|webserver| {
                        let bind_format = match ipaddr.addr.ip() {
                            IpAddr::V4(ipv4) => format!("{}:{:?}", ipv4.to_string(), web_port),
                            IpAddr::V6(ipv6) => format!("{}:{:?}", ipv6.to_string(), web_port),
                        };
                        let try_bind = webserver.bind(bind_format.clone()).map_err(|error| {
                            error!("binding with ip ({:?}): {:?}", bind_format, error);
                            error
                        })?;
                        nr_bindings += 1;
                        Ok(try_bind)
                    })
                },
            );

        if nr_bindings > 0 {
            web_server
                .map_err(|error| {
                    // clean up
                    actix::System::current().stop();
                    error!("webserver could not start up");
                    error
                })?
                .run();
            sys.run()
        } else {
            // no bindings, no nothing
            error!("No bindings to any http addresses!");
            Err(io::Error::new(
                io::ErrorKind::Other,
                format!("no bindings to port {}", &web_port),
            ))
        }
    }

    async fn websocket_answer(
        req: HttpRequest,
        stream: web::Payload,
        data_monitor: web::Data<Arc<Mutex<Addr<ActorWSServerMonitor>>>>,
        data_sync: web::Data<Arc<Mutex<Addr<ActorSyncStartup>>>>,
        data_path: web::Data<Arc<Mutex<SearchPath>>>,
    ) -> Result<HttpResponse, Error> {
        trace!("new websocket answered!");
        let (addr, res) = ws::start_with_addr(
            ActorWebSocket {
                starter: data_sync.lock().unwrap().clone(),
                paths: data_path.get_ref().clone(),
            },
            &req,
            stream,
        )?;
        data_monitor
            .lock()
            .unwrap()
            .do_send(MRegisterWSClient { addr });

        Ok(res)
    }
}
<|endoftext|>


<commit_before>use std::fmt::Write;

use hir::{AdtDef, Ty, source_binder};
use hir::db::HirDatabase;

use ra_syntax::ast::{self, AstNode, Expr};

use crate::{AssistCtx, Assist, AssistId};

pub(crate) fn fill_struct_fields(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
    let struct_lit = ctx.node_at_offset::<ast::StructLit>()?;

    // If we already have existing struct fields, don't provide the assist.
    match struct_lit.named_field_list() {
        Some(named_field_list) if named_field_list.fields().count() > 0 => {
            return None;
        }
        _ => {}
    }

    let expr: &Expr = struct_lit.into();
    let function =
        source_binder::function_from_child_node(ctx.db, ctx.frange.file_id, struct_lit.syntax())?;

    let infer_result = function.infer(ctx.db);
    let source_map = function.body_source_map(ctx.db);
    let node_expr = source_map.node_expr(expr)?;
    let struct_lit_ty = infer_result[node_expr].clone();
    let struct_def = match struct_lit_ty {
        Ty::Adt { def_id: AdtDef::Struct(s), .. } => s,
        _ => return None,
    };

    let struct_name = struct_def.name(ctx.db)?;
    let db = ctx.db;

    ctx.add_action(AssistId("fill_struct_fields"), "fill struct fields", |edit| {
        let mut buf = format!("{} {{\n", struct_name);
        let struct_fields = struct_def.fields(db);
        for field in struct_fields {
            let field_name = field.name(db).to_string();
            write!(&mut buf, "    {}: (),\n", field_name).unwrap();
        }
        buf.push_str("}");

        edit.target(struct_lit.syntax().range());
        edit.set_cursor(expr.syntax().range().start());
        edit.replace_node_and_indent(struct_lit.syntax(), buf);
    });

    ctx.build()
}

#[cfg(test)]
mod tests {
    use crate::helpers::{check_assist, check_assist_target};

    use super::fill_struct_fields;

    #[test]
    fn fill_struct_fields_empty_body() {
        check_assist(
            fill_struct_fields,
            r#"
            struct S<'a, D> {
                a: u32,
                b: String,
                c: (i32, i32),
                d: D,
                r: &'a str,
            }

            fn main() {
                let s = S<|> {}
            }
            "#,
            r#"
            struct S<'a, D> {
                a: u32,
                b: String,
                c: (i32, i32),
                d: D,
                r: &'a str,
            }

            fn main() {
                let s = <|>S {
                    a: (),
                    b: (),
                    c: (),
                    d: (),
                    r: (),
                }
            }
            "#,
        );
    }

    #[test]
    fn fill_struct_fields_target() {
        check_assist_target(
            fill_struct_fields,
            r#"
            struct S<'a, D> {
                a: u32,
                b: String,
                c: (i32, i32),
                d: D,
                r: &'a str,
            }

            fn main() {
                let s = S<|> {}
            }
            "#,
            "S {}",
        );
    }
}
<commit_msg>Only replace NamedFieldList and add test for preserving Self<commit_after>use std::fmt::Write;

use hir::{AdtDef, Ty, source_binder};
use hir::db::HirDatabase;

use ra_syntax::ast::{self, AstNode};

use crate::{AssistCtx, Assist, AssistId};

pub(crate) fn fill_struct_fields(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
    let struct_lit = ctx.node_at_offset::<ast::StructLit>()?;
    let named_field_list = struct_lit.named_field_list()?;

    // If we already have existing struct fields, don't provide the assist.
    if named_field_list.fields().count() > 0 {
        return None;
    }

    let function =
        source_binder::function_from_child_node(ctx.db, ctx.frange.file_id, struct_lit.syntax())?;

    let infer_result = function.infer(ctx.db);
    let source_map = function.body_source_map(ctx.db);
    let node_expr = source_map.node_expr(struct_lit.into())?;
    let struct_lit_ty = infer_result[node_expr].clone();
    let struct_def = match struct_lit_ty {
        Ty::Adt { def_id: AdtDef::Struct(s), .. } => s,
        _ => return None,
    };

    let db = ctx.db;
    ctx.add_action(AssistId("fill_struct_fields"), "fill struct fields", |edit| {
        let mut buf = String::from("{\n");
        let struct_fields = struct_def.fields(db);
        for field in struct_fields {
            let field_name = field.name(db).to_string();
            write!(&mut buf, "    {}: (),\n", field_name).unwrap();
        }
        buf.push_str("}");

        edit.target(struct_lit.syntax().range());
        edit.set_cursor(struct_lit.syntax().range().start());
        edit.replace_node_and_indent(named_field_list.syntax(), buf);
    });

    ctx.build()
}

#[cfg(test)]
mod tests {
    use crate::helpers::{check_assist, check_assist_target};

    use super::fill_struct_fields;

    #[test]
    fn fill_struct_fields_empty_body() {
        check_assist(
            fill_struct_fields,
            r#"
            struct S<'a, D> {
                a: u32,
                b: String,
                c: (i32, i32),
                d: D,
                r: &'a str,
            }

            fn main() {
                let s = S<|> {}
            }
            "#,
            r#"
            struct S<'a, D> {
                a: u32,
                b: String,
                c: (i32, i32),
                d: D,
                r: &'a str,
            }

            fn main() {
                let s = <|>S {
                    a: (),
                    b: (),
                    c: (),
                    d: (),
                    r: (),
                }
            }
            "#,
        );
    }

    #[test]
    fn fill_struct_fields_target() {
        check_assist_target(
            fill_struct_fields,
            r#"
            struct S<'a, D> {
                a: u32,
                b: String,
                c: (i32, i32),
                d: D,
                r: &'a str,
            }

            fn main() {
                let s = S<|> {}
            }
            "#,
            "S {}",
        );
    }

    #[test]
    fn fill_struct_fields_preserve_self() {
        check_assist(
            fill_struct_fields,
            r#"
            struct Foo {
                foo: u8,
                bar: String,
                baz: i128,
            }

            impl Foo {
                pub fn new() -> Self {
                    Self <|>{}
                }
            }
            "#,
            r#"
            struct Foo {
                foo: u8,
                bar: String,
                baz: i128,
            }

            impl Foo {
                pub fn new() -> Self {
                    <|>Self {
                        foo: (),
                        bar: (),
                        baz: (),
                    }
                }
            }
            "#,
        );
    }
}
<|endoftext|>


<commit_before>use std::io::Read;
use std::marker::PhantomData;

use crate::{
    context::{Has, HasMut},
    GameError, GameResult,
};

use super::{
    context::GraphicsContext,
    gpu::{
        arc::{ArcBindGroup, ArcBindGroupLayout, ArcBuffer, ArcShaderModule},
        bind_group::BindGroupBuilder,
    },
    image::Image,
    sampler::Sampler,
};
use crevice::std140::Std140;
use wgpu::util::DeviceExt;

#[derive(Debug, PartialEq, Eq)]
enum ShaderSource<'a> {
    None,
    Path(&'a str),
    Code(&'a str),
}

/// Builder pattern for assembling shaders.
#[derive(Debug)]
pub struct ShaderBuilder<'a> {
    fs: ShaderSource<'a>,
    vs: ShaderSource<'a>,
}

impl<'a> ShaderBuilder<'a> {
    /// Create a new builder with no associated shader code.
    pub fn new_wgsl() -> Self {
        ShaderBuilder {
            fs: ShaderSource::None,
            vs: ShaderSource::None,
        }
    }

    /// Use this wgsl shader code for the fragment shader.
    pub fn fragment_code(self, source: &'a str) -> Self {
        ShaderBuilder {
            fs: ShaderSource::Code(source),
            vs: self.vs,
        }
    }
    /// Use this wgsl code resource path for the fragment shader.
    pub fn fragment_path(self, path: &'a str) -> Self {
        ShaderBuilder {
            fs: ShaderSource::Path(path),
            vs: self.vs,
        }
    }

    /// Use this wgsl shader code for the vertex shader.
    pub fn vertex_code(self, source: &'a str) -> Self {
        ShaderBuilder {
            fs: self.vs,
            vs: ShaderSource::Code(source),
        }
    }

    /// Use this wgsl code resource path for the vertex shader.
    pub fn vertex_path(self, path: &'a str) -> Self {
        ShaderBuilder {
            fs: self.vs,
            vs: ShaderSource::Path(path),
        }
    }

    /// Use this wgsl code as both a vertex and fragment shader.
    pub fn combined_code(self, source: &'a str) -> Self {
        ShaderBuilder {
            fs: ShaderSource::Code(source),
            vs: ShaderSource::Code(source),
        }
    }

    /// Use a single wgsl resource as both a vertex and fragment shader.
    pub fn combined_path(self, path: &'a str) -> Self {
        ShaderBuilder {
            fs: ShaderSource::Path(path),
            vs: ShaderSource::Path(path),
        }
    }

    /// Create a Shader from the builder.
    pub fn build(self, gfx: &impl Has<GraphicsContext>) -> GameResult<Shader> {
        let gfx = gfx.retrieve();
        let load = |s: &str| {
            Some(ArcShaderModule::new(gfx.wgpu.device.create_shader_module(
                wgpu::ShaderModuleDescriptor {
                    label: None,
                    source: wgpu::ShaderSource::Wgsl(s.into()),
                },
            )))
        };
        let load_resource = |path: &str| -> GameResult<Option<ArcShaderModule>> {
            let mut encoded = Vec::new();
            _ = gfx.fs.open(path)?.read_to_end(&mut encoded)?;
            Ok(load(
                &String::from_utf8(encoded).map_err(GameError::ShaderEncodingError)?,
            ))
        };
        let load_any = |source| -> GameResult<Option<ArcShaderModule>> {
            Ok(match source {
                ShaderSource::Code(source) => load(source),
                ShaderSource::Path(source) => load_resource(source)?,
                ShaderSource::None => None,
            })
        };
        Ok(if self.vs == self.fs {
            let module = load_any(self.vs)?;
            Shader {
                vs_module: module.clone(),
                fs_module: module,
            }
        } else {
            Shader {
                vs_module: load_any(self.vs)?,
                fs_module: load_any(self.fs)?,
            }
        })
    }
}

/// A custom shader that can be used to render with shader effects.
///
/// The shader may have a user specified vertex module, fragment module, both,
/// or neither. The fragment module entry point must be named fs_main. The
/// vertex module entry point must be named vs_main. The vertex module must
/// have an output of type
/// ```wgsl
/// struct VertexOutput {
///     @builtin(position) position: vec4<f32>,
///     @location(0) uv: vec2<f32>,
///     @location(1) color: vec4<f32>,
/// }
/// ```
/// if the fragment module is left unspecified (default).
///
/// Produce a Shader using [ShaderBuilder].
///
/// Adapted from the `shader.rs` example:
/// ```rust
/// # use ggez::*;
/// # use ggez::graphics::*;
/// #[derive(AsStd140)]
/// struct Dim {
///     rate: f32,
/// }
///
/// struct MainState {}
///
/// impl event::EventHandler for MainState {
/// #   fn update(&mut self, _ctx: &mut Context) -> Result<(), GameError> { Ok(()) }
///     fn draw(&mut self, ctx: &mut Context) -> GameResult {
///         let mut canvas = graphics::Canvas::from_frame(ctx, Color::BLACK);
///         let dim = Dim { rate: 0.5 };
///         // NOTE: This is for simplicity; do not recreate your shader every frame like this!
///         //       For more info look at the full example.
///         let shader = ShaderBuilder::new_wgsl()
///             .fragment_code(include_str!("../../resources/dimmer.wgsl"))
///             .build(&mut ctx.gfx);
///         let params = ShaderParamsBuilder::new(&dim).build(&mut ctx.gfx);
///         params.set_uniforms(ctx, &dim);
///
///         canvas.set_shader(shader);
///         canvas.set_shader_params(params);
///         // draw something...
///         canvas.finish(ctx)
///     }
///
///     /* ... */
/// }
/// ```
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Shader {
    pub(crate) vs_module: Option<ArcShaderModule>,
    pub(crate) fs_module: Option<ArcShaderModule>,
}

pub use crevice::std140::AsStd140;

/// A builder for [ShaderParams]
#[derive(Debug)]
pub struct ShaderParamsBuilder<'a, Uniforms: AsStd140> {
    uniforms: &'a Uniforms,
    images: &'a [(&'a Image, Sampler)],
    images_vs_visible: bool,
}

impl<'a, Uniforms: AsStd140> ShaderParamsBuilder<'a, Uniforms> {
    /// Creates a new builder for [ShaderParams].
    ///
    /// # Arguments
    ///
    /// * `uniforms` - Initial uniforms.
    pub fn new(uniforms: &'a Uniforms) -> Self {
        ShaderParamsBuilder {
            uniforms,
            images: &[],
            images_vs_visible: false,
        }
    }

    /// Provides images to the shaders.
    ///
    /// # Arguments
    ///
    /// * `vs_visible` - If the images should also be visible to the vertex shader, rather
    ///    than just the fragment shader.
    pub fn images(self, images: &'a [(&'a Image, Sampler)], vs_visible: bool) -> Self {
        ShaderParamsBuilder {
            uniforms: self.uniforms,
            images,
            images_vs_visible: vs_visible,
        }
    }

    /// Produce a [ShaderParams] from the builder.
    pub fn build(self, gfx: &mut impl HasMut<GraphicsContext>) -> ShaderParams<Uniforms> {
        let gfx = gfx.retrieve_mut();
        let uniforms = ArcBuffer::new(gfx.wgpu.device.create_buffer_init(
            &wgpu::util::BufferInitDescriptor {
                label: None,
                usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
                contents: self.uniforms.as_std140().as_bytes(),
            },
        ));

        let mut builder = BindGroupBuilder::new();
        builder = builder.buffer(
            &uniforms,
            0,
            wgpu::ShaderStages::VERTEX_FRAGMENT,
            wgpu::BufferBindingType::Uniform,
            false,
            None,
        );

        let vis = if self.images_vs_visible {
            wgpu::ShaderStages::VERTEX_FRAGMENT
        } else {
            wgpu::ShaderStages::FRAGMENT
        };
        for (image, _) in self.images {
            builder = builder.image(&image.view, vis);
        }

        let samplers = self
            .images
            .iter()
            .map(|&(_, sampler)| gfx.sampler_cache.get(&gfx.wgpu.device, sampler))
            .collect::<Vec<_>>();
        for sampler in &samplers {
            builder = builder.sampler(sampler, vis);
        }

        let (bind_group, layout) = builder.create_uncached(&gfx.wgpu.device);

        ShaderParams {
            uniforms,
            layout,
            bind_group,
            _marker: PhantomData,
        }
    }
}

/// Parameters that can be passed to a custom shader, including uniforms, images, and samplers.
///
/// Create with [ShaderParamsBuilder].
///
/// These parameters are bound to group 3. With WGSL, for example,
/// ```rust,ignore
/// ggez::graphics::ShaderParamsBuilder::new(&my_uniforms)
///     .images(&[(&image1, &sampler1), (&image2, &sampler2)], false)
///     .build(&mut ctx.gfx)
/// ```
/// Corresponds to...
/// ```ignore
/// @group(3) @binding(0)
/// var<uniform> my_uniforms: MyUniforms;
/// @group(3) @binding(1)
/// var image1: texture_2d<f32>;
/// @group(3) @binding(2)
/// var image2: texture_2d<f32>;
/// @group(3) @binding(3)
/// var sampler1: sampler;
/// @group(3) @binding(4)
/// var sampler2: sampler;
/// ```
#[derive(Debug, PartialEq, Eq)]
pub struct ShaderParams<Uniforms: AsStd140> {
    pub(crate) uniforms: ArcBuffer,
    pub(crate) layout: ArcBindGroupLayout,
    pub(crate) bind_group: ArcBindGroup,
    _marker: PhantomData<Uniforms>,
}

impl<Uniforms: AsStd140> ShaderParams<Uniforms> {
    /// Updates the uniform data.
    pub fn set_uniforms(&self, gfx: &impl Has<GraphicsContext>, uniforms: &Uniforms) {
        let gfx = gfx.retrieve();
        gfx.wgpu
            .queue
            .write_buffer(&self.uniforms, 0, uniforms.as_std140().as_bytes());
    }
}

impl<Uniforms: AsStd140> Clone for ShaderParams<Uniforms> {
    fn clone(&self) -> Self {
        Self {
            uniforms: self.uniforms.clone(),
            layout: self.layout.clone(),
            bind_group: self.bind_group.clone(),
            _marker: PhantomData,
        }
    }
}

pub use wgpu::{BlendComponent, BlendFactor, BlendOperation};

/// Describes the blend mode used when drawing images.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct BlendMode {
    /// The blend mode for the color channels.
    pub color: BlendComponent,
    /// The blend mode for the alpha channel.
    pub alpha: BlendComponent,
}

impl BlendMode {
    /// When combining two fragments, add their values together, saturating
    /// at 1.0
    pub const ADD: Self = BlendMode {
        color: BlendComponent {
            src_factor: BlendFactor::SrcAlpha,
            dst_factor: BlendFactor::One,
            operation: BlendOperation::Add,
        },
        alpha: BlendComponent {
            src_factor: BlendFactor::OneMinusDstAlpha,
            dst_factor: BlendFactor::One,
            operation: BlendOperation::Add,
        },
    };

    /// When combining two fragments, subtract the source value from the
    /// destination value
    pub const SUBTRACT: Self = BlendMode {
        color: BlendComponent {
            src_factor: BlendFactor::SrcAlpha,
            dst_factor: BlendFactor::One,
            operation: BlendOperation::ReverseSubtract,
        },
        alpha: BlendComponent {
            src_factor: BlendFactor::Zero,
            dst_factor: BlendFactor::One,
            operation: BlendOperation::Add,
        },
    };

    /// When combining two fragments, add the value of the source times its
    /// alpha channel with the value of the destination multiplied by the inverse
    /// of the source alpha channel. Has the usual transparency effect: mixes the
    /// two colors using a fraction of each one specified by the alpha of the source.
    pub const ALPHA: Self = BlendMode {
        color: BlendComponent {
            src_factor: BlendFactor::SrcAlpha,
            dst_factor: BlendFactor::OneMinusSrcAlpha,
            operation: BlendOperation::Add,
        },
        alpha: BlendComponent {
            src_factor: BlendFactor::OneMinusDstAlpha,
            dst_factor: BlendFactor::One,
            operation: BlendOperation::Add,
        },
    };

    /// When combining two fragments, subtract the destination color from a constant
    /// color using the source color as weight. Has an invert effect with the constant
    /// color as base and source color controlling displacement from the base color.
    /// A white source color and a white value results in plain invert. The output
    /// alpha is same as destination alpha.
    pub const INVERT: Self = BlendMode {
        color: BlendComponent {
            src_factor: BlendFactor::Constant,
            dst_factor: BlendFactor::Src,
            operation: BlendOperation::Subtract,
        },
        alpha: BlendComponent {
            src_factor: BlendFactor::Zero,
            dst_factor: BlendFactor::One,
            operation: BlendOperation::Add,
        },
    };

    /// When combining two fragments, multiply their values together (including alpha)
    pub const MULTIPLY: Self = BlendMode {
        color: BlendComponent {
            src_factor: BlendFactor::Dst,
            dst_factor: BlendFactor::Zero,
            operation: BlendOperation::Add,
        },
        alpha: BlendComponent {
            src_factor: BlendFactor::DstAlpha,
            dst_factor: BlendFactor::Zero,
            operation: BlendOperation::Add,
        },
    };

    /// When combining two fragments, choose the source value (including source alpha)
    pub const REPLACE: Self = BlendMode {
        color: wgpu::BlendState::REPLACE.color,
        alpha: wgpu::BlendState::REPLACE.alpha,
    };

    /// When combining two fragments, choose the lighter value
    pub const LIGHTEN: Self = BlendMode {
        color: BlendComponent {
            src_factor: BlendFactor::SrcAlpha,
            dst_factor: BlendFactor::One,
            operation: BlendOperation::Max,
        },
        alpha: BlendComponent {
            src_factor: BlendFactor::OneMinusDstAlpha,
            dst_factor: BlendFactor::One,
            operation: BlendOperation::Add,
        },
    };

    /// When combining two fragments, choose the darker value
    pub const DARKEN: Self = BlendMode {
        color: BlendComponent {
            src_factor: BlendFactor::SrcAlpha,
            dst_factor: BlendFactor::One,
            operation: BlendOperation::Min,
        },
        alpha: BlendComponent {
            src_factor: BlendFactor::OneMinusDstAlpha,
            dst_factor: BlendFactor::One,
            operation: BlendOperation::Add,
        },
    };

    /// When using premultiplied alpha, use this.
    ///
    /// You usually want to use this blend mode for drawing canvases
    /// containing semi-transparent imagery.
    /// For an explanation on this see: <https://github.com/ggez/ggez/issues/694#issuecomment-853724926>
    pub const PREMULTIPLIED: Self = BlendMode {
        color: BlendComponent {
            src_factor: BlendFactor::One,
            dst_factor: BlendFactor::OneMinusSrcAlpha,
            operation: BlendOperation::Add,
        },
        alpha: BlendComponent {
            src_factor: BlendFactor::OneMinusDstAlpha,
            dst_factor: BlendFactor::One,
            operation: BlendOperation::Add,
        },
    };
}
<commit_msg>Images N:N samplers<commit_after>use std::io::Read;
use std::marker::PhantomData;

use crate::{
    context::{Has, HasMut},
    GameError, GameResult,
};

use super::{
    context::GraphicsContext,
    gpu::{
        arc::{ArcBindGroup, ArcBindGroupLayout, ArcBuffer, ArcShaderModule},
        bind_group::BindGroupBuilder,
    },
    image::Image,
    sampler::Sampler,
};
use crevice::std140::Std140;
use wgpu::util::DeviceExt;

#[derive(Debug, PartialEq, Eq)]
enum ShaderSource<'a> {
    None,
    Path(&'a str),
    Code(&'a str),
}

/// Builder pattern for assembling shaders.
#[derive(Debug)]
pub struct ShaderBuilder<'a> {
    fs: ShaderSource<'a>,
    vs: ShaderSource<'a>,
}

impl<'a> ShaderBuilder<'a> {
    /// Create a new builder with no associated shader code.
    pub fn new_wgsl() -> Self {
        ShaderBuilder {
            fs: ShaderSource::None,
            vs: ShaderSource::None,
        }
    }

    /// Use this wgsl shader code for the fragment shader.
    pub fn fragment_code(self, source: &'a str) -> Self {
        ShaderBuilder {
            fs: ShaderSource::Code(source),
            vs: self.vs,
        }
    }
    /// Use this wgsl code resource path for the fragment shader.
    pub fn fragment_path(self, path: &'a str) -> Self {
        ShaderBuilder {
            fs: ShaderSource::Path(path),
            vs: self.vs,
        }
    }

    /// Use this wgsl shader code for the vertex shader.
    pub fn vertex_code(self, source: &'a str) -> Self {
        ShaderBuilder {
            fs: self.vs,
            vs: ShaderSource::Code(source),
        }
    }

    /// Use this wgsl code resource path for the vertex shader.
    pub fn vertex_path(self, path: &'a str) -> Self {
        ShaderBuilder {
            fs: self.vs,
            vs: ShaderSource::Path(path),
        }
    }

    /// Use this wgsl code as both a vertex and fragment shader.
    pub fn combined_code(self, source: &'a str) -> Self {
        ShaderBuilder {
            fs: ShaderSource::Code(source),
            vs: ShaderSource::Code(source),
        }
    }

    /// Use a single wgsl resource as both a vertex and fragment shader.
    pub fn combined_path(self, path: &'a str) -> Self {
        ShaderBuilder {
            fs: ShaderSource::Path(path),
            vs: ShaderSource::Path(path),
        }
    }

    /// Create a Shader from the builder.
    pub fn build(self, gfx: &impl Has<GraphicsContext>) -> GameResult<Shader> {
        let gfx = gfx.retrieve();
        let load = |s: &str| {
            Some(ArcShaderModule::new(gfx.wgpu.device.create_shader_module(
                wgpu::ShaderModuleDescriptor {
                    label: None,
                    source: wgpu::ShaderSource::Wgsl(s.into()),
                },
            )))
        };
        let load_resource = |path: &str| -> GameResult<Option<ArcShaderModule>> {
            let mut encoded = Vec::new();
            _ = gfx.fs.open(path)?.read_to_end(&mut encoded)?;
            Ok(load(
                &String::from_utf8(encoded).map_err(GameError::ShaderEncodingError)?,
            ))
        };
        let load_any = |source| -> GameResult<Option<ArcShaderModule>> {
            Ok(match source {
                ShaderSource::Code(source) => load(source),
                ShaderSource::Path(source) => load_resource(source)?,
                ShaderSource::None => None,
            })
        };
        Ok(if self.vs == self.fs {
            let module = load_any(self.vs)?;
            Shader {
                vs_module: module.clone(),
                fs_module: module,
            }
        } else {
            Shader {
                vs_module: load_any(self.vs)?,
                fs_module: load_any(self.fs)?,
            }
        })
    }
}

/// A custom shader that can be used to render with shader effects.
///
/// The shader may have a user specified vertex module, fragment module, both,
/// or neither. The fragment module entry point must be named fs_main. The
/// vertex module entry point must be named vs_main. The vertex module must
/// have an output of type
/// ```wgsl
/// struct VertexOutput {
///     @builtin(position) position: vec4<f32>,
///     @location(0) uv: vec2<f32>,
///     @location(1) color: vec4<f32>,
/// }
/// ```
/// if the fragment module is left unspecified (default).
///
/// Produce a Shader using [ShaderBuilder].
///
/// Adapted from the `shader.rs` example:
/// ```rust
/// # use ggez::*;
/// # use ggez::graphics::*;
/// #[derive(AsStd140)]
/// struct Dim {
///     rate: f32,
/// }
///
/// struct MainState {}
///
/// impl event::EventHandler for MainState {
/// #   fn update(&mut self, _ctx: &mut Context) -> Result<(), GameError> { Ok(()) }
///     fn draw(&mut self, ctx: &mut Context) -> GameResult {
///         let mut canvas = graphics::Canvas::from_frame(ctx, Color::BLACK);
///         let dim = Dim { rate: 0.5 };
///         // NOTE: This is for simplicity; do not recreate your shader every frame like this!
///         //       For more info look at the full example.
///         let shader = ShaderBuilder::new_wgsl()
///             .fragment_code(include_str!("../../resources/dimmer.wgsl"))
///             .build(&mut ctx.gfx);
///         let params = ShaderParamsBuilder::new(&dim).build(&mut ctx.gfx);
///         params.set_uniforms(ctx, &dim);
///
///         canvas.set_shader(shader);
///         canvas.set_shader_params(params);
///         // draw something...
///         canvas.finish(ctx)
///     }
///
///     /* ... */
/// }
/// ```
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Shader {
    pub(crate) vs_module: Option<ArcShaderModule>,
    pub(crate) fs_module: Option<ArcShaderModule>,
}

pub use crevice::std140::AsStd140;

/// A builder for [ShaderParams]
#[derive(Debug)]
pub struct ShaderParamsBuilder<'a, Uniforms: AsStd140> {
    uniforms: &'a Uniforms,
    images: &'a [&'a Image],
    samplers: &'a [Sampler],
    images_vs_visible: bool,
}

impl<'a, Uniforms: AsStd140> ShaderParamsBuilder<'a, Uniforms> {
    /// Creates a new builder for [ShaderParams].
    ///
    /// # Arguments
    ///
    /// * `uniforms` - Initial uniforms.
    pub fn new(uniforms: &'a Uniforms) -> Self {
        ShaderParamsBuilder {
            uniforms,
            images: &[],
            samplers: &[],
            images_vs_visible: false,
        }
    }

    /// Provides images to the shaders.
    ///
    /// # Arguments
    ///
    /// * `vs_visible` - If the images should also be visible to the vertex shader, rather
    ///    than just the fragment shader.
    pub fn images(
        self,
        images: &'a [&'a Image],
        samplers: &'a [Sampler],
        vs_visible: bool,
    ) -> Self {
        ShaderParamsBuilder {
            uniforms: self.uniforms,
            images,
            samplers,
            images_vs_visible: vs_visible,
        }
    }

    /// Produce a [ShaderParams] from the builder.
    pub fn build(self, gfx: &mut impl HasMut<GraphicsContext>) -> ShaderParams<Uniforms> {
        let gfx = gfx.retrieve_mut();
        let uniforms = ArcBuffer::new(gfx.wgpu.device.create_buffer_init(
            &wgpu::util::BufferInitDescriptor {
                label: None,
                usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
                contents: self.uniforms.as_std140().as_bytes(),
            },
        ));

        let mut builder = BindGroupBuilder::new();
        builder = builder.buffer(
            &uniforms,
            0,
            wgpu::ShaderStages::VERTEX_FRAGMENT,
            wgpu::BufferBindingType::Uniform,
            false,
            None,
        );

        let vis = if self.images_vs_visible {
            wgpu::ShaderStages::VERTEX_FRAGMENT
        } else {
            wgpu::ShaderStages::FRAGMENT
        };
        for image in self.images {
            builder = builder.image(&image.view, vis);
        }

        let samplers = self
            .samplers
            .iter()
            .map(|&sampler| gfx.sampler_cache.get(&gfx.wgpu.device, sampler))
            .collect::<Vec<_>>();
        for sampler in &samplers {
            builder = builder.sampler(sampler, vis);
        }

        let (bind_group, layout) = builder.create_uncached(&gfx.wgpu.device);

        ShaderParams {
            uniforms,
            layout,
            bind_group,
            _marker: PhantomData,
        }
    }
}

/// Parameters that can be passed to a custom shader, including uniforms, images, and samplers.
///
/// Create with [ShaderParamsBuilder].
///
/// These parameters are bound to group 3. With WGSL, for example,
/// ```rust,ignore
/// ggez::graphics::ShaderParamsBuilder::new(&my_uniforms)
///     .images(&[&image1, &image2], &[sampler1], false)
///     .build(&mut ctx.gfx)
/// ```
/// Corresponds to...
/// ```ignore
/// @group(3) @binding(0)
/// var<uniform> my_uniforms: MyUniforms;
/// @group(3) @binding(1)
/// var image1: texture_2d<f32>;
/// @group(3) @binding(2)
/// var image2: texture_2d<f32>;
/// @group(3) @binding(3)
/// var sampler1: sampler;
/// ```
#[derive(Debug, PartialEq, Eq)]
pub struct ShaderParams<Uniforms: AsStd140> {
    pub(crate) uniforms: ArcBuffer,
    pub(crate) layout: ArcBindGroupLayout,
    pub(crate) bind_group: ArcBindGroup,
    _marker: PhantomData<Uniforms>,
}

impl<Uniforms: AsStd140> ShaderParams<Uniforms> {
    /// Updates the uniform data.
    pub fn set_uniforms(&self, gfx: &impl Has<GraphicsContext>, uniforms: &Uniforms) {
        let gfx = gfx.retrieve();
        gfx.wgpu
            .queue
            .write_buffer(&self.uniforms, 0, uniforms.as_std140().as_bytes());
    }
}

impl<Uniforms: AsStd140> Clone for ShaderParams<Uniforms> {
    fn clone(&self) -> Self {
        Self {
            uniforms: self.uniforms.clone(),
            layout: self.layout.clone(),
            bind_group: self.bind_group.clone(),
            _marker: PhantomData,
        }
    }
}

pub use wgpu::{BlendComponent, BlendFactor, BlendOperation};

/// Describes the blend mode used when drawing images.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct BlendMode {
    /// The blend mode for the color channels.
    pub color: BlendComponent,
    /// The blend mode for the alpha channel.
    pub alpha: BlendComponent,
}

impl BlendMode {
    /// When combining two fragments, add their values together, saturating
    /// at 1.0
    pub const ADD: Self = BlendMode {
        color: BlendComponent {
            src_factor: BlendFactor::SrcAlpha,
            dst_factor: BlendFactor::One,
            operation: BlendOperation::Add,
        },
        alpha: BlendComponent {
            src_factor: BlendFactor::OneMinusDstAlpha,
            dst_factor: BlendFactor::One,
            operation: BlendOperation::Add,
        },
    };

    /// When combining two fragments, subtract the source value from the
    /// destination value
    pub const SUBTRACT: Self = BlendMode {
        color: BlendComponent {
            src_factor: BlendFactor::SrcAlpha,
            dst_factor: BlendFactor::One,
            operation: BlendOperation::ReverseSubtract,
        },
        alpha: BlendComponent {
            src_factor: BlendFactor::Zero,
            dst_factor: BlendFactor::One,
            operation: BlendOperation::Add,
        },
    };

    /// When combining two fragments, add the value of the source times its
    /// alpha channel with the value of the destination multiplied by the inverse
    /// of the source alpha channel. Has the usual transparency effect: mixes the
    /// two colors using a fraction of each one specified by the alpha of the source.
    pub const ALPHA: Self = BlendMode {
        color: BlendComponent {
            src_factor: BlendFactor::SrcAlpha,
            dst_factor: BlendFactor::OneMinusSrcAlpha,
            operation: BlendOperation::Add,
        },
        alpha: BlendComponent {
            src_factor: BlendFactor::OneMinusDstAlpha,
            dst_factor: BlendFactor::One,
            operation: BlendOperation::Add,
        },
    };

    /// When combining two fragments, subtract the destination color from a constant
    /// color using the source color as weight. Has an invert effect with the constant
    /// color as base and source color controlling displacement from the base color.
    /// A white source color and a white value results in plain invert. The output
    /// alpha is same as destination alpha.
    pub const INVERT: Self = BlendMode {
        color: BlendComponent {
            src_factor: BlendFactor::Constant,
            dst_factor: BlendFactor::Src,
            operation: BlendOperation::Subtract,
        },
        alpha: BlendComponent {
            src_factor: BlendFactor::Zero,
            dst_factor: BlendFactor::One,
            operation: BlendOperation::Add,
        },
    };

    /// When combining two fragments, multiply their values together (including alpha)
    pub const MULTIPLY: Self = BlendMode {
        color: BlendComponent {
            src_factor: BlendFactor::Dst,
            dst_factor: BlendFactor::Zero,
            operation: BlendOperation::Add,
        },
        alpha: BlendComponent {
            src_factor: BlendFactor::DstAlpha,
            dst_factor: BlendFactor::Zero,
            operation: BlendOperation::Add,
        },
    };

    /// When combining two fragments, choose the source value (including source alpha)
    pub const REPLACE: Self = BlendMode {
        color: wgpu::BlendState::REPLACE.color,
        alpha: wgpu::BlendState::REPLACE.alpha,
    };

    /// When combining two fragments, choose the lighter value
    pub const LIGHTEN: Self = BlendMode {
        color: BlendComponent {
            src_factor: BlendFactor::SrcAlpha,
            dst_factor: BlendFactor::One,
            operation: BlendOperation::Max,
        },
        alpha: BlendComponent {
            src_factor: BlendFactor::OneMinusDstAlpha,
            dst_factor: BlendFactor::One,
            operation: BlendOperation::Add,
        },
    };

    /// When combining two fragments, choose the darker value
    pub const DARKEN: Self = BlendMode {
        color: BlendComponent {
            src_factor: BlendFactor::SrcAlpha,
            dst_factor: BlendFactor::One,
            operation: BlendOperation::Min,
        },
        alpha: BlendComponent {
            src_factor: BlendFactor::OneMinusDstAlpha,
            dst_factor: BlendFactor::One,
            operation: BlendOperation::Add,
        },
    };

    /// When using premultiplied alpha, use this.
    ///
    /// You usually want to use this blend mode for drawing canvases
    /// containing semi-transparent imagery.
    /// For an explanation on this see: <https://github.com/ggez/ggez/issues/694#issuecomment-853724926>
    pub const PREMULTIPLIED: Self = BlendMode {
        color: BlendComponent {
            src_factor: BlendFactor::One,
            dst_factor: BlendFactor::OneMinusSrcAlpha,
            operation: BlendOperation::Add,
        },
        alpha: BlendComponent {
            src_factor: BlendFactor::OneMinusDstAlpha,
            dst_factor: BlendFactor::One,
            operation: BlendOperation::Add,
        },
    };
}
<|endoftext|>