<commit_before>// (c) 2016 Productize SPRL <joost@productize.be>

use std::fmt;
use std::result;
use std::str::FromStr;
use std::path::PathBuf;

// get from parent
use Result;
use str_error as err;
use read_file;
use parse_split_quote_aware;
use schematic;
use str_error;

#[derive(Debug)]
pub struct SymbolLib {
    pub symbols:Vec<Symbol>,
}

// DEF name reference unused text_offset draw_pinnumber draw_pinname unit_count units_locked option_flag
#[derive(Debug,Clone)]
pub struct Symbol {
    pub name:String,
    pub reference:String,
    pub text_offset:f64,
    pub draw_pinnumber: bool,
    pub draw_pinname: bool,
    pub unit_count: i64,
    pub unit_locked: bool,
    pub is_power: bool,
    pub fields:Vec<Field>,
    pub draw:Vec<String>, // TODO parse draw
}
// F n “text” posx posy dimension orientation visibility hjustify vjustify/italic/bold “name”
// F0 "#PWR" 0 0 30 H I C CNN

#[derive(Debug,Clone)]
pub struct Field {
    pub i:i64,
    pub value:String,
    pub x:f64,
    pub y:f64,
    pub dimension:i64,
    pub orientation:schematic::Orientation,
    pub visible:bool,
    pub hjustify:schematic::Justify,
    pub vjustify:schematic::Justify,
    pub italic:bool,
    pub bold:bool,
    pub name:String,
}

impl SymbolLib {
    fn new() -> SymbolLib {
        SymbolLib {
            symbols:vec![]
        }
    }

    pub fn find<F>(&self, filter:F) -> Option<&Symbol>
        where F:Fn(&Symbol) -> bool
    {
        for symbol in &self.symbols {
            if filter(symbol) {
                return Some(symbol)
            }
        }
        None
    }
}

impl fmt::Display for SymbolLib {
    fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
        try!(writeln!(f, "EESchema-LIBRARY Version 2.3"));
        try!(writeln!(f, "#encoding utf-8"));
        try!(writeln!(f, "#"));
        for i in &self.symbols {
            try!(write!(f, "{}", i))
        };
        writeln!(f, "#End Library")
    }
}

impl Symbol {
    fn new(name:String, reference:String) -> Symbol {
        Symbol {
            name:name,
            reference:reference,
            text_offset:0.0,
            draw_pinnumber:false,
            draw_pinname:false,
            unit_count:1,
            unit_locked:false,
            is_power:false,
            fields:vec![],
            draw:vec![],
        }
    }
    pub fn set_name(&mut self, name:String) {
        if char_at(&self.name, 0) == '~' {
            self.name = format!("~{}", name)
        } else {
            self.name = name.clone()
        }
        let mut field = &mut self.fields[1];
        field.value = name
    }
}

impl fmt::Display for Symbol {
    fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
        try!(writeln!(f, "# {}", &&self.fields[1].value));
        try!(writeln!(f, "#"));
        try!(writeln!(f, "DEF {} {} 0 {} {} {} {} {} {}",
                     self.name, self.reference, self.text_offset,
                     if self.draw_pinnumber { "Y" } else { "N" },
                     if self.draw_pinname { "Y" } else { "N" },
                     self.unit_count,
                     if self.unit_locked { "L" } else { "F" },
                     if self.is_power { "P" } else { "N" },
                     ));
        for field in &self.fields {
            try!(writeln!(f, "{}", field))
        };
        try!(writeln!(f, "DRAW"));
        for draw in &self.draw {
            try!(writeln!(f, "{}", draw))
        };
        try!(writeln!(f, "ENDDRAW"));
        try!(writeln!(f, "ENDDEF"));
        writeln!(f, "#")
    }
}

impl Field {
    fn new() -> Field {
        Field {
            i:0,
            value:String::from(""),
            x:0.0,
            y:0.0,
            dimension:0,
            orientation:schematic::Orientation::Horizontal,
            visible:false,
            hjustify:schematic::Justify::Center,
            vjustify:schematic::Justify::Center,
            italic:false,
            bold:false,
            name:String::from(""),
        }
    }
}

impl fmt::Display for Field {
    fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
        try!(write!(f, "F{} \"{}\" {} {} {} {} {} {} {}{}{}",
               self.i, self.value, self.x, self.y, self.dimension,
               self.orientation,
               if self.visible { "V" } else { "I" },
               self.hjustify, self.vjustify,
               if self.italic { "I" } else { "N" },
               if self.bold { "I" } else { "N" },
                    ));
        if self.i > 3 {
            try!(write!(f, " \"{}\"", self.name))
        };
        Ok(())
    }
}

macro_rules! assume_line {
    ($s:expr, $exp:expr) => (
        if $s.eof() {
            return str_error("end of file reached".to_string())
        }
        if $s.here() != $exp {
            return str_error(format!("expected '{}', got '{}'", $exp, $s.here()))
        }
        $s.i += 1;
    )
}

#[derive(Debug)]
struct ParseState {
    i:usize,
    v:Vec<String>,
}

impl ParseState {
    fn new(v2:Vec<&str>) -> ParseState {
        ParseState {
            i:0,
            v:v2.iter().map(|x| String::from(*x)).collect(),
        }
    }

    fn here(&self) -> String {
        let s =  (self.v[self.i]).clone();
        //println!("{}", s);
        s
    }

    fn next(&mut self) {
        self.i += 1;
    }

    fn eof(&self) -> bool {
        self.i >= self.v.len()
    }
}

fn assume_string(e:&'static str, s:&String) -> Result<()> {
    if String::from(e) != *s {
        return str_error(format!("expecting: {}, actually: {}", e, s))
    }
    return Ok(())
}

fn i64_from_string(p:&ParseState, s:&String) -> Result<i64> {
    match i64::from_str(&s[..]) {
        Ok(i) => Ok(i),
        _ => str_error(format!("int parse error in {}; line: {}", s, p.here()))
    }
}

fn f64_from_string(p:&ParseState, s:&String) -> Result<f64> {
    match f64::from_str(&s[..]) {
        Ok(i) => Ok(i),
        _ => str_error(format!("float parse error in {}; line: {}", s, p.here()))
    }
}

fn bool_from_string(s:&String, t:&'static str, f:&'static str) -> Result<bool> {
    if &s[..] == t {
        return Ok(true)
    }
    if &s[..] == f {
        return Ok(false)
    }
    str_error(format!("unknown boolean {}, expected {} or {}", s, t, f))
}

fn char_at(s:&String, p:usize) -> char {
    let v:Vec<char> = s.chars().collect();
    v[..][p]
}

fn parse_symbol(p:&mut ParseState) -> Result<Symbol> {
    p.next(); // skip line like # name
    assume_line!(p,"#");
    let s = p.here();
    let v = &parse_split_quote_aware(&s);
    if v.len() != 10 {
        return str_error(format!("unexpected elements in {}", s))
    }
    try!(assume_string("DEF", &v[0]));
    let mut s = Symbol::new(v[1].clone(), v[2].clone());
    s.text_offset = try!(f64_from_string(p, &v[4]));
    s.draw_pinnumber = try!(bool_from_string(&v[5], "Y", "N"));
    s.draw_pinname = try!(bool_from_string(&v[6], "Y", "N"));
    s.unit_count = try!(i64_from_string(p, &v[7]));
    s.unit_locked = try!(bool_from_string(&v[8], "L", "F"));
    s.is_power = try!(bool_from_string(&v[9], "P", "N"));
    // TODO fields
    // TODO draw
    p.next();
    loop {
        let s2 = p.here();
        if char_at(&s2, 0) == 'F' {
            let f = try!(parse_field(p, &s2));
            s.fields.push(f);
            p.next();
        } else {
            break;
        }
    }
    if &p.here() == "$FPLIST" {
        p.next();
        // skip FPLIST for now
        while !p.eof() {
            if &p.here() == "$ENDFPLIST" {
                p.next();
                break;
            }
            p.next()
        }
    }
    assume_line!(p, "DRAW");
    while !p.eof() {
        let s2 = p.here();
        if &s2 == "ENDDRAW" {
            p.next();
            break;
        }
        s.draw.push(s2.clone());
        p.next()
    }
    assume_line!(p,"ENDDEF");
    assume_line!(p,"#");
    Ok(s)
}

fn bool_from<T: PartialEq + fmt::Display>(i:T, t:T, f:T) -> Result<bool> {
    if i == t {
        return Ok(true)
    }
    if i == f {
        return Ok(false)
    }
    str_error(format!("unknown boolean {}, expected {} or {}", i, t, f))
}

// F0 "L" 0 50 40 H V C CNN
fn parse_field(p:&mut ParseState, line:&String) -> Result<Field> {
    let mut f = Field::new();
    let v = &parse_split_quote_aware(&line);
    if v.len() != 9 && v.len() != 10 {
        return str_error(format!("unexpected elements in {}", line))
    }
    f.i = try!(i64_from_string(p, &String::from(&v[0][1..])));
    let name = if v.len() == 10 {
        v[9].clone()
    } else {
        match f.i {
            0 => String::from("Reference"),
            1 => String::from("Value"),
            2 => String::from("Footprint"),
            3 => String::from("UserDocLink"),
            _ => return str_error(format!("expecting name for componentfield > 3")),
        }
    };
    f.value = v[1].clone();
    f.x = try!(f64_from_string(p, &v[2]));
    f.y = try!(f64_from_string(p, &v[3]));
    f.dimension = try!(i64_from_string(p, &v[4]));
    f.orientation = try!(schematic::Orientation::new(char_at(&v[5], 0)));
    f.visible = try!(bool_from_string(&v[6], "V", "I"));
    f.hjustify = try!(schematic::Justify::new(char_at(&v[7], 0)));
    f.vjustify = try!(schematic::Justify::new(char_at(&v[8], 0)));
    f.italic = try!(bool_from(char_at(&v[8], 1), 'I', 'N'));
    f.bold = try!(bool_from(char_at(&v[8], 2), 'B', 'N'));
    f.name = name;
    Ok(f)
}

fn parse(s: &str) -> Result<SymbolLib> {
    let mut lib = SymbolLib::new();
    let v:Vec<&str> = s.lines().collect();
    let p = &mut ParseState::new(v);
    assume_line!(p, "EESchema-LIBRARY Version 2.3");
    assume_line!(p, "#encoding utf-8");
    assume_line!(p,"#");
    while !p.eof() {
        //println!("here: {}", &p.here());
        if &p.here() == "#End Library" {
            break;
        }
        let s = try!(parse_symbol(p));
        //println!("new symbol: {}", &s);
        lib.symbols.push(s)
    }
    Ok(lib)
}

pub fn parse_str(s:&str) -> Result<SymbolLib> {
    parse(s)
}

pub fn parse_file(filename:&PathBuf) -> Result<SymbolLib> {
    let name = filename.to_str().unwrap();
    let s = try!(read_file(name));
    parse(&s[..])
}
<commit_msg>make public<commit_after>// (c) 2016 Productize SPRL <joost@productize.be>

use std::fmt;
use std::result;
use std::str::FromStr;
use std::path::PathBuf;

// get from parent
use Result;
use str_error as err;
use read_file;
use parse_split_quote_aware;
use schematic;
use str_error;

#[derive(Debug)]
pub struct SymbolLib {
    pub symbols:Vec<Symbol>,
}

// DEF name reference unused text_offset draw_pinnumber draw_pinname unit_count units_locked option_flag
#[derive(Debug,Clone)]
pub struct Symbol {
    pub name:String,
    pub reference:String,
    pub text_offset:f64,
    pub draw_pinnumber: bool,
    pub draw_pinname: bool,
    pub unit_count: i64,
    pub unit_locked: bool,
    pub is_power: bool,
    pub fields:Vec<Field>,
    pub draw:Vec<String>, // TODO parse draw
}
// F n “text” posx posy dimension orientation visibility hjustify vjustify/italic/bold “name”
// F0 "#PWR" 0 0 30 H I C CNN

#[derive(Debug,Clone)]
pub struct Field {
    pub i:i64,
    pub value:String,
    pub x:f64,
    pub y:f64,
    pub dimension:i64,
    pub orientation:schematic::Orientation,
    pub visible:bool,
    pub hjustify:schematic::Justify,
    pub vjustify:schematic::Justify,
    pub italic:bool,
    pub bold:bool,
    pub name:String,
}

impl SymbolLib {
    fn new() -> SymbolLib {
        SymbolLib {
            symbols:vec![]
        }
    }

    pub fn find<F>(&self, filter:F) -> Option<&Symbol>
        where F:Fn(&Symbol) -> bool
    {
        for symbol in &self.symbols {
            if filter(symbol) {
                return Some(symbol)
            }
        }
        None
    }
}

impl fmt::Display for SymbolLib {
    fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
        try!(writeln!(f, "EESchema-LIBRARY Version 2.3"));
        try!(writeln!(f, "#encoding utf-8"));
        try!(writeln!(f, "#"));
        for i in &self.symbols {
            try!(write!(f, "{}", i))
        };
        writeln!(f, "#End Library")
    }
}

impl Symbol {
    pub fn new(name:String, reference:String) -> Symbol {
        Symbol {
            name:name,
            reference:reference,
            text_offset:0.0,
            draw_pinnumber:false,
            draw_pinname:false,
            unit_count:1,
            unit_locked:false,
            is_power:false,
            fields:vec![],
            draw:vec![],
        }
    }
    pub fn set_name(&mut self, name:String) {
        if char_at(&self.name, 0) == '~' {
            self.name = format!("~{}", name)
        } else {
            self.name = name.clone()
        }
        let mut field = &mut self.fields[1];
        field.value = name
    }
}

impl fmt::Display for Symbol {
    fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
        try!(writeln!(f, "# {}", &&self.fields[1].value));
        try!(writeln!(f, "#"));
        try!(writeln!(f, "DEF {} {} 0 {} {} {} {} {} {}",
                     self.name, self.reference, self.text_offset,
                     if self.draw_pinnumber { "Y" } else { "N" },
                     if self.draw_pinname { "Y" } else { "N" },
                     self.unit_count,
                     if self.unit_locked { "L" } else { "F" },
                     if self.is_power { "P" } else { "N" },
                     ));
        for field in &self.fields {
            try!(writeln!(f, "{}", field))
        };
        try!(writeln!(f, "DRAW"));
        for draw in &self.draw {
            try!(writeln!(f, "{}", draw))
        };
        try!(writeln!(f, "ENDDRAW"));
        try!(writeln!(f, "ENDDEF"));
        writeln!(f, "#")
    }
}

impl Field {
    pub fn new() -> Field {
        Field {
            i:0,
            value:String::from(""),
            x:0.0,
            y:0.0,
            dimension:0,
            orientation:schematic::Orientation::Horizontal,
            visible:false,
            hjustify:schematic::Justify::Center,
            vjustify:schematic::Justify::Center,
            italic:false,
            bold:false,
            name:String::from(""),
        }
    }
}

impl fmt::Display for Field {
    fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
        try!(write!(f, "F{} \"{}\" {} {} {} {} {} {} {}{}{}",
               self.i, self.value, self.x, self.y, self.dimension,
               self.orientation,
               if self.visible { "V" } else { "I" },
               self.hjustify, self.vjustify,
               if self.italic { "I" } else { "N" },
               if self.bold { "I" } else { "N" },
                    ));
        if self.i > 3 {
            try!(write!(f, " \"{}\"", self.name))
        };
        Ok(())
    }
}

macro_rules! assume_line {
    ($s:expr, $exp:expr) => (
        if $s.eof() {
            return str_error("end of file reached".to_string())
        }
        if $s.here() != $exp {
            return str_error(format!("expected '{}', got '{}'", $exp, $s.here()))
        }
        $s.i += 1;
    )
}

#[derive(Debug)]
struct ParseState {
    i:usize,
    v:Vec<String>,
}

impl ParseState {
    fn new(v2:Vec<&str>) -> ParseState {
        ParseState {
            i:0,
            v:v2.iter().map(|x| String::from(*x)).collect(),
        }
    }

    fn here(&self) -> String {
        let s =  (self.v[self.i]).clone();
        //println!("{}", s);
        s
    }

    fn next(&mut self) {
        self.i += 1;
    }

    fn eof(&self) -> bool {
        self.i >= self.v.len()
    }
}

fn assume_string(e:&'static str, s:&String) -> Result<()> {
    if String::from(e) != *s {
        return str_error(format!("expecting: {}, actually: {}", e, s))
    }
    return Ok(())
}

fn i64_from_string(p:&ParseState, s:&String) -> Result<i64> {
    match i64::from_str(&s[..]) {
        Ok(i) => Ok(i),
        _ => str_error(format!("int parse error in {}; line: {}", s, p.here()))
    }
}

fn f64_from_string(p:&ParseState, s:&String) -> Result<f64> {
    match f64::from_str(&s[..]) {
        Ok(i) => Ok(i),
        _ => str_error(format!("float parse error in {}; line: {}", s, p.here()))
    }
}

fn bool_from_string(s:&String, t:&'static str, f:&'static str) -> Result<bool> {
    if &s[..] == t {
        return Ok(true)
    }
    if &s[..] == f {
        return Ok(false)
    }
    str_error(format!("unknown boolean {}, expected {} or {}", s, t, f))
}

fn char_at(s:&String, p:usize) -> char {
    let v:Vec<char> = s.chars().collect();
    v[..][p]
}

fn parse_symbol(p:&mut ParseState) -> Result<Symbol> {
    p.next(); // skip line like # name
    assume_line!(p,"#");
    let s = p.here();
    let v = &parse_split_quote_aware(&s);
    if v.len() != 10 {
        return str_error(format!("unexpected elements in {}", s))
    }
    try!(assume_string("DEF", &v[0]));
    let mut s = Symbol::new(v[1].clone(), v[2].clone());
    s.text_offset = try!(f64_from_string(p, &v[4]));
    s.draw_pinnumber = try!(bool_from_string(&v[5], "Y", "N"));
    s.draw_pinname = try!(bool_from_string(&v[6], "Y", "N"));
    s.unit_count = try!(i64_from_string(p, &v[7]));
    s.unit_locked = try!(bool_from_string(&v[8], "L", "F"));
    s.is_power = try!(bool_from_string(&v[9], "P", "N"));
    // TODO fields
    // TODO draw
    p.next();
    loop {
        let s2 = p.here();
        if char_at(&s2, 0) == 'F' {
            let f = try!(parse_field(p, &s2));
            s.fields.push(f);
            p.next();
        } else {
            break;
        }
    }
    if &p.here() == "$FPLIST" {
        p.next();
        // skip FPLIST for now
        while !p.eof() {
            if &p.here() == "$ENDFPLIST" {
                p.next();
                break;
            }
            p.next()
        }
    }
    assume_line!(p, "DRAW");
    while !p.eof() {
        let s2 = p.here();
        if &s2 == "ENDDRAW" {
            p.next();
            break;
        }
        s.draw.push(s2.clone());
        p.next()
    }
    assume_line!(p,"ENDDEF");
    assume_line!(p,"#");
    Ok(s)
}

fn bool_from<T: PartialEq + fmt::Display>(i:T, t:T, f:T) -> Result<bool> {
    if i == t {
        return Ok(true)
    }
    if i == f {
        return Ok(false)
    }
    str_error(format!("unknown boolean {}, expected {} or {}", i, t, f))
}

// F0 "L" 0 50 40 H V C CNN
fn parse_field(p:&mut ParseState, line:&String) -> Result<Field> {
    let mut f = Field::new();
    let v = &parse_split_quote_aware(&line);
    if v.len() != 9 && v.len() != 10 {
        return str_error(format!("unexpected elements in {}", line))
    }
    f.i = try!(i64_from_string(p, &String::from(&v[0][1..])));
    let name = if v.len() == 10 {
        v[9].clone()
    } else {
        match f.i {
            0 => String::from("Reference"),
            1 => String::from("Value"),
            2 => String::from("Footprint"),
            3 => String::from("UserDocLink"),
            _ => return str_error(format!("expecting name for componentfield > 3")),
        }
    };
    f.value = v[1].clone();
    f.x = try!(f64_from_string(p, &v[2]));
    f.y = try!(f64_from_string(p, &v[3]));
    f.dimension = try!(i64_from_string(p, &v[4]));
    f.orientation = try!(schematic::Orientation::new(char_at(&v[5], 0)));
    f.visible = try!(bool_from_string(&v[6], "V", "I"));
    f.hjustify = try!(schematic::Justify::new(char_at(&v[7], 0)));
    f.vjustify = try!(schematic::Justify::new(char_at(&v[8], 0)));
    f.italic = try!(bool_from(char_at(&v[8], 1), 'I', 'N'));
    f.bold = try!(bool_from(char_at(&v[8], 2), 'B', 'N'));
    f.name = name;
    Ok(f)
}

fn parse(s: &str) -> Result<SymbolLib> {
    let mut lib = SymbolLib::new();
    let v:Vec<&str> = s.lines().collect();
    let p = &mut ParseState::new(v);
    assume_line!(p, "EESchema-LIBRARY Version 2.3");
    assume_line!(p, "#encoding utf-8");
    assume_line!(p,"#");
    while !p.eof() {
        //println!("here: {}", &p.here());
        if &p.here() == "#End Library" {
            break;
        }
        let s = try!(parse_symbol(p));
        //println!("new symbol: {}", &s);
        lib.symbols.push(s)
    }
    Ok(lib)
}

pub fn parse_str(s:&str) -> Result<SymbolLib> {
    parse(s)
}

pub fn parse_file(filename:&PathBuf) -> Result<SymbolLib> {
    let name = filename.to_str().unwrap();
    let s = try!(read_file(name));
    parse(&s[..])
}
<|endoftext|>


<commit_before>extern crate bindgen;

fn osx_version() -> Result<String, std::io::Error> {
    use std::process::Command;

    let output = Command::new("defaults")
        .arg("read")
        .arg("loginwindow")
        .arg("SystemVersionStampAsString")
        .output()?
        .stdout;
    let version_str = std::str::from_utf8(&output).expect("invalid output from `defaults`");
    let version = version_str.trim_right();

    Ok(version.to_owned())
}

fn parse_version(version: &str) -> Option<i32> {
    version
        .split(".")
        .skip(1)
        .next()
        .and_then(|m| m.parse::<i32>().ok())
}

fn frameworks_path() -> Result<String, std::io::Error> {
    // For 10.13 and higher:
    //
    // While macOS has its system frameworks located at "/System/Library/Frameworks"
    // for actually linking against them (especially for cross-compilation) once
    // has to refer to the frameworks as found within "Xcode.app/Contents/Developer/…".

    if osx_version()
        .and_then(|version| Ok(parse_version(&version).map(|v| v >= 13).unwrap_or(false)))
        .unwrap_or(false)
    {
        use std::process::Command;

        let output = Command::new("xcode-select").arg("-p").output()?.stdout;
        let prefix_str = std::str::from_utf8(&output).expect("invalid output from `xcode-select`");
        let prefix = prefix_str.trim_right();

        let platform = if cfg!(target_os = "macos") {
            "MacOSX"
        } else if cfg!(target_os = "ios") {
            "iPhoneOS"
        } else {
            unreachable!();
        };

        let infix = format!(
            "Platforms/{}.platform/Developer/SDKs/{}.sdk",
            platform, platform
        );
        let suffix = "System/Library/Frameworks";
        let directory = format!("{}/{}/{}", prefix, infix, suffix);

        Ok(directory)
    } else {
        Ok("/System/Library/Frameworks".to_string())
    }
}

fn build(frameworks_path: &str) {
    // Generate one large set of bindings for all frameworks.
    //
    // We do this rather than generating a module per framework as some frameworks depend on other
    // frameworks and in turn share types. To ensure all types are compatible across each
    // framework, we feed all headers to bindgen at once.
    //
    // Only link to each framework and include their headers if their features are enabled and they
    // are available on the target os.

    use std::env;
    use std::path::PathBuf;

    let mut frameworks = vec![];
    let mut headers = vec![];

    #[cfg(feature = "audio_toolbox")]
    {
        println!("cargo:rustc-link-lib=framework=AudioToolbox");
        frameworks.push("AudioToolbox");
        headers.push("AudioToolbox.framework/Headers/AudioToolbox.h");
    }

    #[cfg(feature = "audio_unit")]
    {
        println!("cargo:rustc-link-lib=framework=AudioUnit");
        frameworks.push("AudioUnit");
        headers.push("AudioUnit.framework/Headers/AudioUnit.h");
    }

    #[cfg(feature = "core_audio")]
    {
        println!("cargo:rustc-link-lib=framework=CoreAudio");
        frameworks.push("CoreAudio");
        headers.push("CoreAudio.framework/Headers/CoreAudio.h");
    }

    #[cfg(feature = "open_al")]
    {
        println!("cargo:rustc-link-lib=framework=OpenAL");
        frameworks.push("OpenAL");
        headers.push("OpenAL.framework/Headers/al.h");
        headers.push("OpenAL.framework/Headers/alc.h");
    }

    #[cfg(all(feature = "core_midi", target_os = "macos"))]
    {
        println!("cargo:rustc-link-lib=framework=CoreMIDI");
        frameworks.push("CoreMIDI");
        headers.push("CoreMIDI.framework/Headers/CoreMIDI.h");
    }

    // Get the cargo out directory.
    let out_dir = PathBuf::from(env::var("OUT_DIR").expect("env variable OUT_DIR not found"));

    // Begin building the bindgen params.
    let mut builder = bindgen::Builder::default();

    builder = builder.clang_arg(format!("-F/{}", frameworks_path));

    // Add all headers.
    for relative_path in headers {
        let absolute_path = format!("{}/{}", frameworks_path, relative_path);
        builder = builder.header(absolute_path);
    }

    // Link to all frameworks.
    for relative_path in frameworks {
        let absolute_path = format!("{}/{}", frameworks_path, relative_path);
        builder = builder.link_framework(absolute_path);
    }

    // Generate the bindings.
    let bindings = builder
        .trust_clang_mangling(false)
        .derive_default(true)
        .generate()
        .expect("unable to generate bindings");

    // Write them to the crate root.
    bindings
        .write_to_file(out_dir.join("coreaudio.rs"))
        .expect("could not write bindings");
}

#[cfg(any(target_os = "macos", target_os = "ios"))]
fn main() {
    if let Ok(directory) = frameworks_path() {
        build(&directory);
    } else {
        eprintln!("coreaudio-sys could not find frameworks path");
    }
}

#[cfg(not(any(target_os = "macos", target_os = "ios")))]
fn main() {
    eprintln!("coreaudio-sys requires macos or ios target");
}
<commit_msg>Disable rustfmt on the bindings, as it can occasionally cause build breakage<commit_after>extern crate bindgen;

fn osx_version() -> Result<String, std::io::Error> {
    use std::process::Command;

    let output = Command::new("defaults")
        .arg("read")
        .arg("loginwindow")
        .arg("SystemVersionStampAsString")
        .output()?
        .stdout;
    let version_str = std::str::from_utf8(&output).expect("invalid output from `defaults`");
    let version = version_str.trim_right();

    Ok(version.to_owned())
}

fn parse_version(version: &str) -> Option<i32> {
    version
        .split(".")
        .skip(1)
        .next()
        .and_then(|m| m.parse::<i32>().ok())
}

fn frameworks_path() -> Result<String, std::io::Error> {
    // For 10.13 and higher:
    //
    // While macOS has its system frameworks located at "/System/Library/Frameworks"
    // for actually linking against them (especially for cross-compilation) once
    // has to refer to the frameworks as found within "Xcode.app/Contents/Developer/…".

    if osx_version()
        .and_then(|version| Ok(parse_version(&version).map(|v| v >= 13).unwrap_or(false)))
        .unwrap_or(false)
    {
        use std::process::Command;

        let output = Command::new("xcode-select").arg("-p").output()?.stdout;
        let prefix_str = std::str::from_utf8(&output).expect("invalid output from `xcode-select`");
        let prefix = prefix_str.trim_right();

        let platform = if cfg!(target_os = "macos") {
            "MacOSX"
        } else if cfg!(target_os = "ios") {
            "iPhoneOS"
        } else {
            unreachable!();
        };

        let infix = format!(
            "Platforms/{}.platform/Developer/SDKs/{}.sdk",
            platform, platform
        );
        let suffix = "System/Library/Frameworks";
        let directory = format!("{}/{}/{}", prefix, infix, suffix);

        Ok(directory)
    } else {
        Ok("/System/Library/Frameworks".to_string())
    }
}

fn build(frameworks_path: &str) {
    // Generate one large set of bindings for all frameworks.
    //
    // We do this rather than generating a module per framework as some frameworks depend on other
    // frameworks and in turn share types. To ensure all types are compatible across each
    // framework, we feed all headers to bindgen at once.
    //
    // Only link to each framework and include their headers if their features are enabled and they
    // are available on the target os.

    use std::env;
    use std::path::PathBuf;

    let mut frameworks = vec![];
    let mut headers = vec![];

    #[cfg(feature = "audio_toolbox")]
    {
        println!("cargo:rustc-link-lib=framework=AudioToolbox");
        frameworks.push("AudioToolbox");
        headers.push("AudioToolbox.framework/Headers/AudioToolbox.h");
    }

    #[cfg(feature = "audio_unit")]
    {
        println!("cargo:rustc-link-lib=framework=AudioUnit");
        frameworks.push("AudioUnit");
        headers.push("AudioUnit.framework/Headers/AudioUnit.h");
    }

    #[cfg(feature = "core_audio")]
    {
        println!("cargo:rustc-link-lib=framework=CoreAudio");
        frameworks.push("CoreAudio");
        headers.push("CoreAudio.framework/Headers/CoreAudio.h");
    }

    #[cfg(feature = "open_al")]
    {
        println!("cargo:rustc-link-lib=framework=OpenAL");
        frameworks.push("OpenAL");
        headers.push("OpenAL.framework/Headers/al.h");
        headers.push("OpenAL.framework/Headers/alc.h");
    }

    #[cfg(all(feature = "core_midi", target_os = "macos"))]
    {
        println!("cargo:rustc-link-lib=framework=CoreMIDI");
        frameworks.push("CoreMIDI");
        headers.push("CoreMIDI.framework/Headers/CoreMIDI.h");
    }

    // Get the cargo out directory.
    let out_dir = PathBuf::from(env::var("OUT_DIR").expect("env variable OUT_DIR not found"));

    // Begin building the bindgen params.
    let mut builder = bindgen::Builder::default();

    builder = builder.clang_arg(format!("-F/{}", frameworks_path));

    // Add all headers.
    for relative_path in headers {
        let absolute_path = format!("{}/{}", frameworks_path, relative_path);
        builder = builder.header(absolute_path);
    }

    // Link to all frameworks.
    for relative_path in frameworks {
        let absolute_path = format!("{}/{}", frameworks_path, relative_path);
        builder = builder.link_framework(absolute_path);
    }

    // Generate the bindings.
    let bindings = builder
        .trust_clang_mangling(false)
        .derive_default(true)
        .rustfmt_bindings(false)
        .generate()
        .expect("unable to generate bindings");

    // Write them to the crate root.
    bindings
        .write_to_file(out_dir.join("coreaudio.rs"))
        .expect("could not write bindings");
}

#[cfg(any(target_os = "macos", target_os = "ios"))]
fn main() {
    if let Ok(directory) = frameworks_path() {
        build(&directory);
    } else {
        eprintln!("coreaudio-sys could not find frameworks path");
    }
}

#[cfg(not(any(target_os = "macos", target_os = "ios")))]
fn main() {
    eprintln!("coreaudio-sys requires macos or ios target");
}
<|endoftext|>


<commit_before>extern crate phf_codegen;

use std::fs::File;
use std::io::{BufWriter, Write};
use std::path::Path;

fn main() {
    let path = Path::new(env!("OUT_DIR")).join("codegen.rs");
    let mut file = BufWriter::new(File::create(&path).unwrap());

    write!(&mut file, "static PROPERTY_KEYS: phf::Set<&'static str> = ").unwrap();
    phf_codegen::Set::new()
        .entry("properties")
        .entry("patternProperties")
        .build(&mut file)
        .unwrap();
    write!(&mut file, ";\n").unwrap();

    write!(&mut file, "static NON_SCHEMA_KEYS: phf::Set<&'static str> = ").unwrap();
    phf_codegen::Set::new()
        .entry("properties")
        .entry("patternProperties")
        .entry("dependencies")
        .entry("definitions")
        .entry("anyOf")
        .entry("allOf")
        .entry("oneOf")
        .build(&mut file)
        .unwrap();
    write!(&mut file, ";\n").unwrap();

    write!(&mut file, "static FINAL_KEYS: phf::Set<&'static str> = ").unwrap();
    phf_codegen::Set::new()
        .entry("enum")
        .entry("required")
        .entry("type")
        .build(&mut file)
        .unwrap();
    write!(&mut file, ";\n").unwrap();

    write!(&mut file, "const ALLOW_NON_CONSUMED_KEYS: phf::Set<&'static str> = ").unwrap();
    phf_codegen::Set::new()
        .entry("definitions")
        .entry("$schema")
        .entry("id")
        .entry("default")
        .entry("description")
        .entry("format")
        .build(&mut file)
        .unwrap();
    write!(&mut file, ";\n").unwrap();
}
<commit_msg>fix(build): read OUT_DIR at runtime instead of compile time<commit_after>extern crate phf_codegen;

use std::fs::File;
use std::io::{BufWriter, Write};
use std::path::Path;
use std::env;

fn main() {
    let path = Path::new(&env::var("OUT_DIR").unwrap()).join("codegen.rs");
    let mut file = BufWriter::new(File::create(&path).unwrap());

    write!(&mut file, "static PROPERTY_KEYS: phf::Set<&'static str> = ").unwrap();
    phf_codegen::Set::new()
        .entry("properties")
        .entry("patternProperties")
        .build(&mut file)
        .unwrap();
    write!(&mut file, ";\n").unwrap();

    write!(&mut file, "static NON_SCHEMA_KEYS: phf::Set<&'static str> = ").unwrap();
    phf_codegen::Set::new()
        .entry("properties")
        .entry("patternProperties")
        .entry("dependencies")
        .entry("definitions")
        .entry("anyOf")
        .entry("allOf")
        .entry("oneOf")
        .build(&mut file)
        .unwrap();
    write!(&mut file, ";\n").unwrap();

    write!(&mut file, "static FINAL_KEYS: phf::Set<&'static str> = ").unwrap();
    phf_codegen::Set::new()
        .entry("enum")
        .entry("required")
        .entry("type")
        .build(&mut file)
        .unwrap();
    write!(&mut file, ";\n").unwrap();

    write!(&mut file, "const ALLOW_NON_CONSUMED_KEYS: phf::Set<&'static str> = ").unwrap();
    phf_codegen::Set::new()
        .entry("definitions")
        .entry("$schema")
        .entry("id")
        .entry("default")
        .entry("description")
        .entry("format")
        .build(&mut file)
        .unwrap();
    write!(&mut file, ";\n").unwrap();
}
<|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/. */

//! Layout for elements with a CSS `display` property of `flex`.

#![deny(unsafe_code)]

use block::BlockFlow;
use context::LayoutContext;
use display_list_builder::FlexFlowDisplayListBuilding;
use floats::FloatKind;
use flow;
use flow::{Flow, FlowClass, OpaqueFlow};
use flow::{HAS_LEFT_FLOATED_DESCENDANTS, HAS_RIGHT_FLOATED_DESCENDANTS};
use flow::IS_ABSOLUTELY_POSITIONED;
use flow::mut_base;
use fragment::{Fragment, FragmentBorderBoxIterator};
use layout_debug;
use model::{IntrinsicISizes};
use style::computed_values::{flex_direction, float};
use style::properties::style_structs;
use style::values::computed::LengthOrPercentageOrAuto;

use euclid::{Point2D, Rect};
use gfx::display_list::DisplayList;
use std::cmp::max;
use std::sync::Arc;
use style::properties::ComputedValues;
use util::geometry::Au;
use util::logical_geometry::LogicalSize;
use util::opts;

// A mode describes which logical axis a flex axis is parallel with.
// The logical axises are inline and block, the flex axises are main and cross.
// When the flex container has flex-direction: column or flex-direction: column-reverse, the main axis
// should be block. Otherwise, it should be inline.
#[derive(Debug)]
enum Mode {
    Inline,
    Block
}

/// A block with the CSS `display` property equal to `flex`.
#[derive(Debug)]
pub struct FlexFlow {
    /// Data common to all block flows.
    block_flow: BlockFlow,
    /// The logical axis which the main axis will be parallel with.
    /// The cross axis will be parallel with the opposite logical axis.
    main_mode: Mode,
}

fn flex_style(fragment: &Fragment) -> &style_structs::Flex {
    fragment.style.get_flex()
}

// TODO(zentner): This function should use flex-basis.
fn flex_item_inline_sizes(flow: &mut Flow) -> IntrinsicISizes {
    let _scope = layout_debug_scope!("flex::flex_item_inline_sizes");
    debug!("flex_item_inline_sizes");
    let base = flow::mut_base(flow);

    debug!("FlexItem intrinsic inline sizes: {:?}, {:?}",
           base.intrinsic_inline_sizes.minimum_inline_size,
           base.intrinsic_inline_sizes.preferred_inline_size);

    IntrinsicISizes {
        minimum_inline_size: base.intrinsic_inline_sizes.minimum_inline_size,
        preferred_inline_size: base.intrinsic_inline_sizes.preferred_inline_size,
    }
}

impl FlexFlow {
    pub fn from_fragment(fragment: Fragment,
                         flotation: Option<FloatKind>)
                         -> FlexFlow {

        let main_mode = match flex_style(&fragment).flex_direction {
            flex_direction::T::row_reverse    => Mode::Inline,
            flex_direction::T::row            => Mode::Inline,
            flex_direction::T::column_reverse => Mode::Block,
            flex_direction::T::column         => Mode::Block
        };

        let this = FlexFlow {
            block_flow: BlockFlow::from_fragment(fragment, flotation),
            main_mode: main_mode
        };

        this
    }

    // TODO(zentner): This function should use flex-basis.
    // Currently, this is the core of BlockFlow::bubble_inline_sizes() with all float logic
    // stripped out, and max replaced with union_nonbreaking_inline.
    fn inline_mode_bubble_inline_sizes(&mut self) {
        let fixed_width = match self.block_flow.fragment.style().get_box().width {
            LengthOrPercentageOrAuto::Length(_) => true,
            _ => false,
        };

        let mut computation = self.block_flow.fragment.compute_intrinsic_inline_sizes();
        if !fixed_width {
            for kid in self.block_flow.base.child_iter() {
                let is_absolutely_positioned =
                    flow::base(kid).flags.contains(IS_ABSOLUTELY_POSITIONED);
                if !is_absolutely_positioned {
                    computation.union_nonbreaking_inline(&flex_item_inline_sizes(kid));
                }
            }
        }
        self.block_flow.base.intrinsic_inline_sizes = computation.finish();
    }

    // TODO(zentner): This function should use flex-basis.
    // Currently, this is the core of BlockFlow::bubble_inline_sizes() with all float logic
    // stripped out.
    fn block_mode_bubble_inline_sizes(&mut self) {
        let fixed_width = match self.block_flow.fragment.style().get_box().width {
            LengthOrPercentageOrAuto::Length(_) => true,
            _ => false,
        };

        let mut computation = self.block_flow.fragment.compute_intrinsic_inline_sizes();
        if !fixed_width {
            for kid in self.block_flow.base.child_iter() {
                let is_absolutely_positioned =
                    flow::base(kid).flags.contains(IS_ABSOLUTELY_POSITIONED);
                let child_base = flow::mut_base(kid);
                if !is_absolutely_positioned {
                    computation.content_intrinsic_sizes.minimum_inline_size =
                        max(computation.content_intrinsic_sizes.minimum_inline_size,
                            child_base.intrinsic_inline_sizes.minimum_inline_size);

                    computation.content_intrinsic_sizes.preferred_inline_size =
                        max(computation.content_intrinsic_sizes.preferred_inline_size,
                            child_base.intrinsic_inline_sizes.preferred_inline_size);
                }
            }
        }
        self.block_flow.base.intrinsic_inline_sizes = computation.finish();
    }
}

impl Flow for FlexFlow {
    fn class(&self) -> FlowClass {
        FlowClass::Flex
    }

    fn as_block<'a>(&'a mut self) -> &'a mut BlockFlow {
        &mut self.block_flow
    }

    fn bubble_inline_sizes(&mut self) {
        let _scope = layout_debug_scope!("flex::bubble_inline_sizes {:x}", self.block_flow.base.debug_id());

        // Flexbox Section 9.0: Generate anonymous flex items:
        // This part was handled in the flow constructor.

        // Flexbox Section 9.1: Re-order the flex items (and any absolutely positioned flex
        // container children) according to their order.
        // TODO(zentner): We need to re-order the items at some point. However, all the operations
        // here ignore order, so we can afford to do it later, if necessary.

        // `flex item`s (our children) cannot be floated. Furthermore, they all establish BFC's.
        // Therefore, we do not have to handle any floats here.

        let mut flags = self.block_flow.base.flags;
        flags.remove(HAS_LEFT_FLOATED_DESCENDANTS);
        flags.remove(HAS_RIGHT_FLOATED_DESCENDANTS);

        match self.main_mode {
            Mode::Inline => self.inline_mode_bubble_inline_sizes(),
            Mode::Block  => self.block_mode_bubble_inline_sizes()
        }

        // Although our children can't be floated, we can.
        match self.block_flow.fragment.style().get_box().float {
            float::T::none => {}
            float::T::left => flags.insert(HAS_LEFT_FLOATED_DESCENDANTS),
            float::T::right => flags.insert(HAS_RIGHT_FLOATED_DESCENDANTS),
        }
        self.block_flow.base.flags = flags
    }

    fn assign_inline_sizes(&mut self, layout_context: &LayoutContext) {
        self.block_flow.assign_inline_sizes(layout_context);
    }

    fn assign_block_size<'a>(&mut self, layout_context: &'a LayoutContext<'a>) {
        self.block_flow.assign_block_size(layout_context);
    }

    fn compute_absolute_position(&mut self, layout_context: &LayoutContext) {
        self.block_flow.compute_absolute_position(layout_context)
    }

    fn place_float_if_applicable<'a>(&mut self, layout_context: &'a LayoutContext<'a>) {
        self.block_flow.place_float_if_applicable(layout_context)
    }

    fn update_late_computed_inline_position_if_necessary(&mut self, inline_position: Au) {
        self.block_flow.update_late_computed_inline_position_if_necessary(inline_position)
    }

    fn update_late_computed_block_position_if_necessary(&mut self, block_position: Au) {
        self.block_flow.update_late_computed_block_position_if_necessary(block_position)
    }

    fn build_display_list(&mut self, layout_context: &LayoutContext) {
        self.build_display_list_for_flex(Box::new(DisplayList::new()), layout_context);

        if opts::get().validate_display_list_geometry {
            self.block_flow.base.validate_display_list_geometry();
        }
    }

    fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
        self.block_flow.repair_style(new_style)
    }

    fn compute_overflow(&self) -> Rect<Au> {
        self.block_flow.compute_overflow()
    }

    fn generated_containing_block_size(&self, flow: OpaqueFlow) -> LogicalSize<Au> {
        self.block_flow.generated_containing_block_size(flow)
    }

    fn iterate_through_fragment_border_boxes(&self,
                                             iterator: &mut FragmentBorderBoxIterator,
                                             level: i32,
                                             stacking_context_position: &Point2D<Au>) {
        self.block_flow.iterate_through_fragment_border_boxes(iterator, level, stacking_context_position);
    }

    fn mutate_fragments(&mut self, mutator: &mut FnMut(&mut Fragment)) {
        self.block_flow.mutate_fragments(mutator);
    }
}
<commit_msg>Implement FlexFlow::assign_inline_sizes().<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/. */

//! Layout for elements with a CSS `display` property of `flex`.

#![deny(unsafe_code)]

use block::BlockFlow;
use context::LayoutContext;
use display_list_builder::FlexFlowDisplayListBuilding;
use floats::FloatKind;
use flow;
use flow::{Flow, FlowClass, OpaqueFlow};
use flow::{HAS_LEFT_FLOATED_DESCENDANTS, HAS_RIGHT_FLOATED_DESCENDANTS};
use flow::ImmutableFlowUtils;
use flow::INLINE_POSITION_IS_STATIC;
use flow::IS_ABSOLUTELY_POSITIONED;
use flow::mut_base;
use fragment::{Fragment, FragmentBorderBoxIterator};
use incremental::{REFLOW, REFLOW_OUT_OF_FLOW};
use layout_debug;
use model::{IntrinsicISizes};
use style::computed_values::{flex_direction, float};
use style::properties::style_structs;
use style::values::computed::LengthOrPercentageOrAuto;

use euclid::{Point2D, Rect};
use gfx::display_list::DisplayList;
use std::cmp::max;
use std::sync::Arc;
use style::properties::ComputedValues;
use util::geometry::Au;
use util::logical_geometry::LogicalSize;
use util::opts;

// A mode describes which logical axis a flex axis is parallel with.
// The logical axises are inline and block, the flex axises are main and cross.
// When the flex container has flex-direction: column or flex-direction: column-reverse, the main axis
// should be block. Otherwise, it should be inline.
#[derive(Debug)]
enum Mode {
    Inline,
    Block
}

/// A block with the CSS `display` property equal to `flex`.
#[derive(Debug)]
pub struct FlexFlow {
    /// Data common to all block flows.
    block_flow: BlockFlow,
    /// The logical axis which the main axis will be parallel with.
    /// The cross axis will be parallel with the opposite logical axis.
    main_mode: Mode,
}

fn flex_style(fragment: &Fragment) -> &style_structs::Flex {
    fragment.style.get_flex()
}

// TODO(zentner): This function should use flex-basis.
fn flex_item_inline_sizes(flow: &mut Flow) -> IntrinsicISizes {
    let _scope = layout_debug_scope!("flex::flex_item_inline_sizes");
    debug!("flex_item_inline_sizes");
    let base = flow::mut_base(flow);

    debug!("FlexItem intrinsic inline sizes: {:?}, {:?}",
           base.intrinsic_inline_sizes.minimum_inline_size,
           base.intrinsic_inline_sizes.preferred_inline_size);

    IntrinsicISizes {
        minimum_inline_size: base.intrinsic_inline_sizes.minimum_inline_size,
        preferred_inline_size: base.intrinsic_inline_sizes.preferred_inline_size,
    }
}

impl FlexFlow {
    pub fn from_fragment(fragment: Fragment,
                         flotation: Option<FloatKind>)
                         -> FlexFlow {

        let main_mode = match flex_style(&fragment).flex_direction {
            flex_direction::T::row_reverse    => Mode::Inline,
            flex_direction::T::row            => Mode::Inline,
            flex_direction::T::column_reverse => Mode::Block,
            flex_direction::T::column         => Mode::Block
        };

        let this = FlexFlow {
            block_flow: BlockFlow::from_fragment(fragment, flotation),
            main_mode: main_mode
        };

        this
    }

    // TODO(zentner): This function should use flex-basis.
    // Currently, this is the core of BlockFlow::bubble_inline_sizes() with all float logic
    // stripped out, and max replaced with union_nonbreaking_inline.
    fn inline_mode_bubble_inline_sizes(&mut self) {
        let fixed_width = match self.block_flow.fragment.style().get_box().width {
            LengthOrPercentageOrAuto::Length(_) => true,
            _ => false,
        };

        let mut computation = self.block_flow.fragment.compute_intrinsic_inline_sizes();
        if !fixed_width {
            for kid in self.block_flow.base.child_iter() {
                let is_absolutely_positioned =
                    flow::base(kid).flags.contains(IS_ABSOLUTELY_POSITIONED);
                if !is_absolutely_positioned {
                    computation.union_nonbreaking_inline(&flex_item_inline_sizes(kid));
                }
            }
        }
        self.block_flow.base.intrinsic_inline_sizes = computation.finish();
    }

    // TODO(zentner): This function should use flex-basis.
    // Currently, this is the core of BlockFlow::bubble_inline_sizes() with all float logic
    // stripped out.
    fn block_mode_bubble_inline_sizes(&mut self) {
        let fixed_width = match self.block_flow.fragment.style().get_box().width {
            LengthOrPercentageOrAuto::Length(_) => true,
            _ => false,
        };

        let mut computation = self.block_flow.fragment.compute_intrinsic_inline_sizes();
        if !fixed_width {
            for kid in self.block_flow.base.child_iter() {
                let is_absolutely_positioned =
                    flow::base(kid).flags.contains(IS_ABSOLUTELY_POSITIONED);
                let child_base = flow::mut_base(kid);
                if !is_absolutely_positioned {
                    computation.content_intrinsic_sizes.minimum_inline_size =
                        max(computation.content_intrinsic_sizes.minimum_inline_size,
                            child_base.intrinsic_inline_sizes.minimum_inline_size);

                    computation.content_intrinsic_sizes.preferred_inline_size =
                        max(computation.content_intrinsic_sizes.preferred_inline_size,
                            child_base.intrinsic_inline_sizes.preferred_inline_size);
                }
            }
        }
        self.block_flow.base.intrinsic_inline_sizes = computation.finish();
    }

    // TODO(zentner): This function needs to be radically different for multi-line flexbox.
    // Currently, this is the core of BlockFlow::propagate_assigned_inline_size_to_children() with
    // all float and table logic stripped out.
    fn block_mode_assign_inline_sizes(&mut self,
                                      _layout_context: &LayoutContext,
                                      inline_start_content_edge: Au,
                                      inline_end_content_edge: Au,
                                      content_inline_size: Au) {
        let _scope = layout_debug_scope!("flex::block_mode_assign_inline_sizes");
        debug!("block_mode_assign_inline_sizes");

        // Calculate non-auto block size to pass to children.
        let content_block_size = self.block_flow.fragment.style().content_block_size();

        let explicit_content_size =
            match (content_block_size, self.block_flow.base.block_container_explicit_block_size) {
            (LengthOrPercentageOrAuto::Percentage(percent), Some(container_size)) => {
                Some(container_size.scale_by(percent))
            }
            (LengthOrPercentageOrAuto::Percentage(_), None) |
            (LengthOrPercentageOrAuto::Auto, _) => None,
            (LengthOrPercentageOrAuto::Length(length), _) => Some(length),
        };

        // FIXME (mbrubeck): Get correct mode for absolute containing block
        let containing_block_mode = self.block_flow.base.writing_mode;

        let mut iterator = self.block_flow.base.child_iter().enumerate().peekable();
        while let Some((_, kid)) = iterator.next() {
            {
                let kid_base = flow::mut_base(kid);
                kid_base.block_container_explicit_block_size = explicit_content_size;
            }

            // The inline-start margin edge of the child flow is at our inline-start content edge,
            // and its inline-size is our content inline-size.
            let kid_mode = flow::base(kid).writing_mode;
            {
                let kid_base = flow::mut_base(kid);
                if kid_base.flags.contains(INLINE_POSITION_IS_STATIC) {
                    kid_base.position.start.i =
                        if kid_mode.is_bidi_ltr() == containing_block_mode.is_bidi_ltr() {
                            inline_start_content_edge
                        } else {
                            // The kid's inline 'start' is at the parent's 'end'
                            inline_end_content_edge
                        };
                }
                kid_base.block_container_inline_size = content_inline_size;
                kid_base.block_container_writing_mode = containing_block_mode;
            }
        }
    }

    // TODO(zentner): This function should actually flex elements!
    // Currently, this is the core of InlineFlow::propagate_assigned_inline_size_to_children() with
    // fragment logic stripped out.
    fn inline_mode_assign_inline_sizes(&mut self,
                                       _layout_context: &LayoutContext,
                                       inline_start_content_edge: Au,
                                       _inline_end_content_edge: Au,
                                       content_inline_size: Au) {
        let _scope = layout_debug_scope!("flex::inline_mode_assign_inline_sizes");
        debug!("inline_mode_assign_inline_sizes");

        debug!("content_inline_size = {:?}", content_inline_size);
        debug!("child_count = {:?}", ImmutableFlowUtils::child_count(self as &Flow) as i32);
        let even_content_inline_size = content_inline_size / ImmutableFlowUtils::child_count(self as &Flow) as i32;

        let inline_size = self.block_flow.base.block_container_inline_size;
        let container_mode = self.block_flow.base.block_container_writing_mode;
        self.block_flow.base.position.size.inline = inline_size;

        let block_container_explicit_block_size = self.block_flow.base.block_container_explicit_block_size;
        let mut inline_child_start = inline_start_content_edge;
        for kid in self.block_flow.base.child_iter() {
            let kid_base = flow::mut_base(kid);

            kid_base.block_container_inline_size = even_content_inline_size;
            kid_base.block_container_writing_mode = container_mode;
            kid_base.block_container_explicit_block_size = block_container_explicit_block_size;
            kid_base.position.start.i = inline_child_start;
            inline_child_start = inline_child_start + even_content_inline_size;
        }
    }
}

impl Flow for FlexFlow {
    fn class(&self) -> FlowClass {
        FlowClass::Flex
    }

    fn as_block<'a>(&'a mut self) -> &'a mut BlockFlow {
        &mut self.block_flow
    }

    fn bubble_inline_sizes(&mut self) {
        let _scope = layout_debug_scope!("flex::bubble_inline_sizes {:x}", self.block_flow.base.debug_id());

        // Flexbox Section 9.0: Generate anonymous flex items:
        // This part was handled in the flow constructor.

        // Flexbox Section 9.1: Re-order the flex items (and any absolutely positioned flex
        // container children) according to their order.
        // TODO(zentner): We need to re-order the items at some point. However, all the operations
        // here ignore order, so we can afford to do it later, if necessary.

        // `flex item`s (our children) cannot be floated. Furthermore, they all establish BFC's.
        // Therefore, we do not have to handle any floats here.

        let mut flags = self.block_flow.base.flags;
        flags.remove(HAS_LEFT_FLOATED_DESCENDANTS);
        flags.remove(HAS_RIGHT_FLOATED_DESCENDANTS);

        match self.main_mode {
            Mode::Inline => self.inline_mode_bubble_inline_sizes(),
            Mode::Block  => self.block_mode_bubble_inline_sizes()
        }

        // Although our children can't be floated, we can.
        match self.block_flow.fragment.style().get_box().float {
            float::T::none => {}
            float::T::left => flags.insert(HAS_LEFT_FLOATED_DESCENDANTS),
            float::T::right => flags.insert(HAS_RIGHT_FLOATED_DESCENDANTS),
        }
        self.block_flow.base.flags = flags
    }

    fn assign_inline_sizes(&mut self, layout_context: &LayoutContext) {
        let _scope = layout_debug_scope!("flex::assign_inline_sizes {:x}", self.block_flow.base.debug_id());
        debug!("assign_inline_sizes");

        if !self.block_flow.base.restyle_damage.intersects(REFLOW_OUT_OF_FLOW | REFLOW) {
            return
        }

        // Our inline-size was set to the inline-size of the containing block by the flow's parent.
        // Now compute the real value.
        let containing_block_inline_size = self.block_flow.base.block_container_inline_size;
        self.block_flow.compute_used_inline_size(layout_context, containing_block_inline_size);
        if self.block_flow.base.flags.is_float() {
            self.block_flow.float.as_mut().unwrap().containing_inline_size = containing_block_inline_size
        }

        // Move in from the inline-start border edge.
        let inline_start_content_edge = self.block_flow.fragment.border_box.start.i +
            self.block_flow.fragment.border_padding.inline_start;

        debug!("inline_start_content_edge = {:?}", inline_start_content_edge);

        let padding_and_borders = self.block_flow.fragment.border_padding.inline_start_end();

        // Distance from the inline-end margin edge to the inline-end content edge.
        let inline_end_content_edge =
            self.block_flow.fragment.margin.inline_end +
            self.block_flow.fragment.border_padding.inline_end;

        debug!("padding_and_borders = {:?}", padding_and_borders);
        debug!("self.block_flow.fragment.border_box.size.inline = {:?}",
               self.block_flow.fragment.border_box.size.inline);
        let content_inline_size = self.block_flow.fragment.border_box.size.inline - padding_and_borders;

        match self.main_mode {
            Mode::Inline =>
                self.inline_mode_assign_inline_sizes(layout_context,
                                                     inline_start_content_edge,
                                                     inline_end_content_edge,
                                                     content_inline_size),
            Mode::Block  =>
                self.block_mode_assign_inline_sizes(layout_context,
                                                    inline_start_content_edge,
                                                    inline_end_content_edge,
                                                    content_inline_size)
        }
    }

    fn assign_block_size<'a>(&mut self, layout_context: &'a LayoutContext<'a>) {
        self.block_flow.assign_block_size(layout_context);
    }

    fn compute_absolute_position(&mut self, layout_context: &LayoutContext) {
        self.block_flow.compute_absolute_position(layout_context)
    }

    fn place_float_if_applicable<'a>(&mut self, layout_context: &'a LayoutContext<'a>) {
        self.block_flow.place_float_if_applicable(layout_context)
    }

    fn update_late_computed_inline_position_if_necessary(&mut self, inline_position: Au) {
        self.block_flow.update_late_computed_inline_position_if_necessary(inline_position)
    }

    fn update_late_computed_block_position_if_necessary(&mut self, block_position: Au) {
        self.block_flow.update_late_computed_block_position_if_necessary(block_position)
    }

    fn build_display_list(&mut self, layout_context: &LayoutContext) {
        self.build_display_list_for_flex(Box::new(DisplayList::new()), layout_context);

        if opts::get().validate_display_list_geometry {
            self.block_flow.base.validate_display_list_geometry();
        }
    }

    fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
        self.block_flow.repair_style(new_style)
    }

    fn compute_overflow(&self) -> Rect<Au> {
        self.block_flow.compute_overflow()
    }

    fn generated_containing_block_size(&self, flow: OpaqueFlow) -> LogicalSize<Au> {
        self.block_flow.generated_containing_block_size(flow)
    }

    fn iterate_through_fragment_border_boxes(&self,
                                             iterator: &mut FragmentBorderBoxIterator,
                                             level: i32,
                                             stacking_context_position: &Point2D<Au>) {
        self.block_flow.iterate_through_fragment_border_boxes(iterator, level, stacking_context_position);
    }

    fn mutate_fragments(&mut self, mutator: &mut FnMut(&mut Fragment)) {
        self.block_flow.mutate_fragments(mutator);
    }
}
<|endoftext|>


<commit_before>// Rust Bitcoin Library
// Written in 2014 by
//     Andrew Poelstra <apoelstra@wpsoftware.net>
//
// To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this software to
// the public domain worldwide. This software is distributed without
// any warranty.
//
// You should have received a copy of the CC0 Public Domain Dedication
// along with this software.
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
//

//! # Blockdata network messages
//!
//! This module describes network messages which are used for passing
//! Bitcoin data (blocks and transactions) around.
//!

use network::constants;
use network::encodable::{ConsensusDecodable, ConsensusEncodable};
use network::serialize::{SimpleDecoder, SimpleEncoder};
use util::hash::Sha256dHash;

#[derive(PartialEq, Eq, Clone, Debug)]
/// The type of an inventory object
pub enum InvType {
    /// Error --- these inventories can be ignored
    Error,
    /// Transaction
    Transaction,
    /// Block
    Block
}

// Some simple messages

/// The `getblocks` message
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct GetBlocksMessage {
    /// The protocol version
    pub version: u32,
    /// Locator hashes --- ordered newest to oldest. The remote peer will
    /// reply with its longest known chain, starting from a locator hash
    /// if possible and block 1 otherwise.
    pub locator_hashes: Vec<Sha256dHash>,
    /// References the block to stop at, or zero to just fetch the maximum 500 blocks
    pub stop_hash: Sha256dHash
}

/// The `getheaders` message
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct GetHeadersMessage {
    /// The protocol version
    pub version: u32,
    /// Locator hashes --- ordered newest to oldest. The remote peer will
    /// reply with its longest known chain, starting from a locator hash
    /// if possible and block 1 otherwise.
    pub locator_hashes: Vec<Sha256dHash>,
    /// References the header to stop at, or zero to just fetch the maximum 2000 headers
    pub stop_hash: Sha256dHash
}

/// An inventory object --- a reference to a Bitcoin object
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct Inventory {
    /// The type of object that is referenced
    pub inv_type: InvType,
    /// The object's hash
    pub hash: Sha256dHash
}

impl GetBlocksMessage {
    /// Construct a new `getblocks` message
    pub fn new(locator_hashes: Vec<Sha256dHash>, stop_hash: Sha256dHash) -> GetBlocksMessage {
        GetBlocksMessage {
            version: constants::PROTOCOL_VERSION,
            locator_hashes: locator_hashes.clone(),
            stop_hash: stop_hash
        }
    }
}

impl_consensus_encoding!(GetBlocksMessage, version, locator_hashes, stop_hash);

impl GetHeadersMessage {
    /// Construct a new `getheaders` message
    pub fn new(locator_hashes: Vec<Sha256dHash>, stop_hash: Sha256dHash) -> GetHeadersMessage {
        GetHeadersMessage {
            version: constants::PROTOCOL_VERSION,
            locator_hashes: locator_hashes,
            stop_hash: stop_hash
        }
    }
}

impl_consensus_encoding!(GetHeadersMessage, version, locator_hashes, stop_hash);

impl<S: SimpleEncoder> ConsensusEncodable<S> for Inventory {
    #[inline]
    fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> {
        try!(match self.inv_type {
            InvType::Error => 0u32,
            InvType::Transaction => 1,
            InvType::Block => 2
        }.consensus_encode(s));
        self.hash.consensus_encode(s)
    }
}

impl<D: SimpleDecoder> ConsensusDecodable<D> for Inventory {
    #[inline]
    fn consensus_decode(d: &mut D) -> Result<Inventory, D::Error> {
        let int_type: u32 = try!(ConsensusDecodable::consensus_decode(d));
        Ok(Inventory {
            inv_type: match int_type {
                0 => InvType::Error,
                1 => InvType::Transaction,
                2 => InvType::Block,
                // TODO do not fail here
                _ => { panic!("bad inventory type field") }
            },
            hash: try!(ConsensusDecodable::consensus_decode(d))
        })
    }
}

#[cfg(test)]
mod tests {
    use super::{GetHeadersMessage, GetBlocksMessage};

    use serialize::hex::FromHex;

    use network::serialize::{deserialize, serialize};
    use std::default::Default;

    #[test]
    fn getblocks_message_test() {
        let from_sat = "72110100014a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b0000000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap();
        let genhash = "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".from_hex().unwrap();

        let decode: Result<GetBlocksMessage, _> = deserialize(&from_sat);
        assert!(decode.is_ok());
        let real_decode = decode.unwrap();
        assert_eq!(real_decode.version, 70002);
        assert_eq!(real_decode.locator_hashes.len(), 1);
        assert_eq!(serialize(&real_decode.locator_hashes[0]).ok(), Some(genhash));
        assert_eq!(real_decode.stop_hash, Default::default());

        assert_eq!(serialize(&real_decode).ok(), Some(from_sat));
    }

    #[test]
    fn getheaders_message_test() {
        let from_sat = "72110100014a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b0000000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap();
        let genhash = "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".from_hex().unwrap();

        let decode: Result<GetHeadersMessage, _> = deserialize(&from_sat);
        assert!(decode.is_ok());
        let real_decode = decode.unwrap();
        assert_eq!(real_decode.version, 70002);
        assert_eq!(real_decode.locator_hashes.len(), 1);
        assert_eq!(serialize(&real_decode.locator_hashes[0]).ok(), Some(genhash));
        assert_eq!(real_decode.stop_hash, Default::default());

        assert_eq!(serialize(&real_decode).ok(), Some(from_sat));
    }
}

<commit_msg>add witness inv types<commit_after>// Rust Bitcoin Library
// Written in 2014 by
//     Andrew Poelstra <apoelstra@wpsoftware.net>
//
// To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this software to
// the public domain worldwide. This software is distributed without
// any warranty.
//
// You should have received a copy of the CC0 Public Domain Dedication
// along with this software.
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
//

//! # Blockdata network messages
//!
//! This module describes network messages which are used for passing
//! Bitcoin data (blocks and transactions) around.
//!

use network::constants;
use network::encodable::{ConsensusDecodable, ConsensusEncodable};
use network::serialize::{SimpleDecoder, SimpleEncoder};
use util::hash::Sha256dHash;

#[derive(PartialEq, Eq, Clone, Debug)]
/// The type of an inventory object
pub enum InvType {
    /// Error --- these inventories can be ignored
    Error,
    /// Transaction
    Transaction,
    /// Block
    Block,
    /// Witness Block
    WitnessBlock,
    /// Witness Transaction
    WitnessTransaction
}

// Some simple messages

/// The `getblocks` message
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct GetBlocksMessage {
    /// The protocol version
    pub version: u32,
    /// Locator hashes --- ordered newest to oldest. The remote peer will
    /// reply with its longest known chain, starting from a locator hash
    /// if possible and block 1 otherwise.
    pub locator_hashes: Vec<Sha256dHash>,
    /// References the block to stop at, or zero to just fetch the maximum 500 blocks
    pub stop_hash: Sha256dHash
}

/// The `getheaders` message
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct GetHeadersMessage {
    /// The protocol version
    pub version: u32,
    /// Locator hashes --- ordered newest to oldest. The remote peer will
    /// reply with its longest known chain, starting from a locator hash
    /// if possible and block 1 otherwise.
    pub locator_hashes: Vec<Sha256dHash>,
    /// References the header to stop at, or zero to just fetch the maximum 2000 headers
    pub stop_hash: Sha256dHash
}

/// An inventory object --- a reference to a Bitcoin object
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct Inventory {
    /// The type of object that is referenced
    pub inv_type: InvType,
    /// The object's hash
    pub hash: Sha256dHash
}

impl GetBlocksMessage {
    /// Construct a new `getblocks` message
    pub fn new(locator_hashes: Vec<Sha256dHash>, stop_hash: Sha256dHash) -> GetBlocksMessage {
        GetBlocksMessage {
            version: constants::PROTOCOL_VERSION,
            locator_hashes: locator_hashes.clone(),
            stop_hash: stop_hash
        }
    }
}

impl_consensus_encoding!(GetBlocksMessage, version, locator_hashes, stop_hash);

impl GetHeadersMessage {
    /// Construct a new `getheaders` message
    pub fn new(locator_hashes: Vec<Sha256dHash>, stop_hash: Sha256dHash) -> GetHeadersMessage {
        GetHeadersMessage {
            version: constants::PROTOCOL_VERSION,
            locator_hashes: locator_hashes,
            stop_hash: stop_hash
        }
    }
}

impl_consensus_encoding!(GetHeadersMessage, version, locator_hashes, stop_hash);

impl<S: SimpleEncoder> ConsensusEncodable<S> for Inventory {
    #[inline]
    fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> {
        try!(match self.inv_type {
            InvType::Error => 0u32,
            InvType::Transaction => 1,
            InvType::Block => 2,
            InvType::WitnessBlock => 0x40000002,
            InvType::WitnessTransaction => 0x40000001
        }.consensus_encode(s));
        self.hash.consensus_encode(s)
    }
}

impl<D: SimpleDecoder> ConsensusDecodable<D> for Inventory {
    #[inline]
    fn consensus_decode(d: &mut D) -> Result<Inventory, D::Error> {
        let int_type: u32 = try!(ConsensusDecodable::consensus_decode(d));
        Ok(Inventory {
            inv_type: match int_type {
                0 => InvType::Error,
                1 => InvType::Transaction,
                2 => InvType::Block,
                // TODO do not fail here
                _ => { panic!("bad inventory type field") }
            },
            hash: try!(ConsensusDecodable::consensus_decode(d))
        })
    }
}

#[cfg(test)]
mod tests {
    use super::{GetHeadersMessage, GetBlocksMessage};

    use serialize::hex::FromHex;

    use network::serialize::{deserialize, serialize};
    use std::default::Default;

    #[test]
    fn getblocks_message_test() {
        let from_sat = "72110100014a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b0000000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap();
        let genhash = "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".from_hex().unwrap();

        let decode: Result<GetBlocksMessage, _> = deserialize(&from_sat);
        assert!(decode.is_ok());
        let real_decode = decode.unwrap();
        assert_eq!(real_decode.version, 70002);
        assert_eq!(real_decode.locator_hashes.len(), 1);
        assert_eq!(serialize(&real_decode.locator_hashes[0]).ok(), Some(genhash));
        assert_eq!(real_decode.stop_hash, Default::default());

        assert_eq!(serialize(&real_decode).ok(), Some(from_sat));
    }

    #[test]
    fn getheaders_message_test() {
        let from_sat = "72110100014a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b0000000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap();
        let genhash = "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".from_hex().unwrap();

        let decode: Result<GetHeadersMessage, _> = deserialize(&from_sat);
        assert!(decode.is_ok());
        let real_decode = decode.unwrap();
        assert_eq!(real_decode.version, 70002);
        assert_eq!(real_decode.locator_hashes.len(), 1);
        assert_eq!(serialize(&real_decode.locator_hashes[0]).ok(), Some(genhash));
        assert_eq!(real_decode.stop_hash, Default::default());

        assert_eq!(serialize(&real_decode).ok(), Some(from_sat));
    }
}

<|endoftext|>


<commit_before>use crate::{
    builtin_functions::BuiltinFunction,
    compiler::hir::{self, Expression},
    input::InputReference,
};

use super::{
    run::{Discover, DiscoverResult},
    value::{Environment, Value},
};

pub fn run_builtin_function(
    db: &dyn Discover,
    input_reference: InputReference,
    builtin_function: BuiltinFunction,
    arguments: Vec<hir::Id>,
    environment: Environment,
) -> DiscoverResult {
    log::info!(
        "run_builtin_function: {:?} {}",
        builtin_function,
        arguments.len()
    );
    // Handle builtin functions that don't need to resolve the
    match builtin_function {
        BuiltinFunction::IfElse => return if_else(db, input_reference, arguments, environment),
        _ => {}
    }

    let arguments =
        db.run_multiple_with_environment(input_reference.to_owned(), arguments, environment)?;
    let arguments = match arguments {
        Ok(arguments) => arguments,
        Err(error) => return Some(Err(error)),
    };
    match builtin_function {
        BuiltinFunction::Add => Some(Ok(add(arguments))),
        BuiltinFunction::Equals => Some(Ok(equals(arguments))),
        BuiltinFunction::GetArgumentCount => get_argument_count(db, input_reference, arguments),
        BuiltinFunction::Panic => panic(arguments),
        BuiltinFunction::Print => Some(Ok(print(arguments))),
        BuiltinFunction::TypeOf => Some(Ok(type_of(arguments))),
        _ => panic!("Unhandled builtin function: {:?}", builtin_function),
    }
}

macro_rules! destructure {
    ($arguments:expr, $enum:pat, $body:block) => {{
        if let $enum = &$arguments[..] {
            $body
        } else {
            panic!()
        }
    }};
}

fn add(arguments: Vec<Value>) -> Value {
    destructure!(arguments, [Value::Int(a), Value::Int(b)], {
        Value::Int(a + b)
    })
}

fn equals(arguments: Vec<Value>) -> Value {
    destructure!(arguments, [a, b], { (a == b).into() })
}

fn get_argument_count(
    db: &dyn Discover,
    input_reference: InputReference,
    arguments: Vec<Value>,
) -> DiscoverResult {
    destructure!(arguments, [Value::Lambda(function)], {
        // TODO: support parameter counts > 2^64 on 128-bit systems and better
        let expression = match db.find_expression(input_reference, function.id.to_owned())? {
            Expression::Lambda(lambda) => lambda,
            _ => return None,
        };
        Some(Ok((expression.parameters.len() as u64).into()))
    })
}

fn if_else(
    db: &dyn Discover,
    input_reference: InputReference,
    arguments: Vec<hir::Id>,
    environment: Environment,
) -> DiscoverResult {
    if let [condition, then, else_] = &arguments[..] {
        let body_id = match db.run_with_environment(
            input_reference.clone(),
            condition.to_owned(),
            environment.to_owned(),
        )? {
            Ok(value) if value == Value::bool_true() => then,
            Ok(value) if value == Value::bool_false() => else_,
            Ok(_) => return None,
            Err(error) => return Some(Err(error)),
        };

        db.run_call(input_reference, body_id.to_owned(), vec![], environment)
    } else {
        panic!()
    }
}

fn panic(arguments: Vec<Value>) -> DiscoverResult {
    destructure!(arguments, [value], { Some(Err(value.clone())) })
}

fn print(arguments: Vec<Value>) -> Value {
    destructure!(arguments, [value], {
        println!("{:?}", value);
        Value::nothing()
    })
}

fn type_of(arguments: Vec<Value>) -> Value {
    destructure!(arguments, [value], {
        match value {
            Value::Int(_) => Value::Symbol("Int".to_owned()),
            Value::Text(_) => Value::Symbol("Text".to_owned()),
            Value::Symbol(_) => Value::Symbol("Symbol".to_owned()),
            Value::Lambda(_) => Value::Symbol("Function".to_owned()),
        }
    })
}

pub trait DestructureTuple<T> {
    fn tuple2(self, function_name: &str) -> Result<(T, T), Value>;
}
impl<T> DestructureTuple<T> for Vec<T> {
    fn tuple2(self, function_name: &str) -> Result<(T, T), Value> {
        if self.len() != 2 {
            Err(Value::argument_count_mismatch_text(
                function_name,
                self.len(),
                2,
            ))
        } else {
            let mut iter = self.into_iter();
            let first = iter.next().unwrap();
            let second = iter.next().unwrap();
            assert!(matches!(iter.next(), None));
            Ok((first, second))
        }
    }
}
<commit_msg>feat(compiler): generate Candy panics for invalid calls to builtin functions<commit_after>use crate::{
    builtin_functions::BuiltinFunction,
    compiler::hir::{self, Expression},
    input::InputReference,
};

use super::{
    run::{Discover, DiscoverResult},
    value::{Environment, Value},
};

pub fn run_builtin_function(
    db: &dyn Discover,
    input_reference: InputReference,
    builtin_function: BuiltinFunction,
    arguments: Vec<hir::Id>,
    environment: Environment,
) -> DiscoverResult {
    log::info!(
        "run_builtin_function: {:?} {}",
        builtin_function,
        arguments.len()
    );
    // Handle builtin functions that don't need to resolve the
    match builtin_function {
        BuiltinFunction::IfElse => return if_else(db, input_reference, arguments, environment),
        _ => {}
    }

    let arguments =
        db.run_multiple_with_environment(input_reference.to_owned(), arguments, environment)?;
    let arguments = match arguments {
        Ok(arguments) => arguments,
        Err(error) => return Some(Err(error)),
    };
    match builtin_function {
        BuiltinFunction::Add => add(arguments),
        BuiltinFunction::Equals => equals(arguments),
        BuiltinFunction::GetArgumentCount => get_argument_count(db, input_reference, arguments),
        BuiltinFunction::Panic => panic(arguments),
        BuiltinFunction::Print => print(arguments),
        BuiltinFunction::TypeOf => type_of(arguments),
        _ => panic!("Unhandled builtin function: {:?}", builtin_function),
    }
}

macro_rules! destructure {
    ($arguments:expr, $enum:pat, $body:block) => {{
        if let $enum = &$arguments[..] {
            $body
        } else {
            Some(Err(Value::Text(format!("Invalid arguments").to_owned())))
        }
    }};
}

fn add(arguments: Vec<Value>) -> DiscoverResult {
    destructure!(arguments, [Value::Int(a), Value::Int(b)], {
        Some(Ok(Value::Int(a + b)))
    })
}

fn equals(arguments: Vec<Value>) -> DiscoverResult {
    destructure!(arguments, [a, b], { Some(Ok((a == b).into())) })
}

fn get_argument_count(
    db: &dyn Discover,
    input_reference: InputReference,
    arguments: Vec<Value>,
) -> DiscoverResult {
    destructure!(arguments, [Value::Lambda(function)], {
        // TODO: support parameter counts > 2^64 on 128-bit systems and better
        let expression = match db.find_expression(input_reference, function.id.to_owned())? {
            Expression::Lambda(lambda) => lambda,
            _ => return None,
        };
        Some(Ok((expression.parameters.len() as u64).into()))
    })
}

fn if_else(
    db: &dyn Discover,
    input_reference: InputReference,
    arguments: Vec<hir::Id>,
    environment: Environment,
) -> DiscoverResult {
    log::error!("{:?}", arguments);
    if let [condition, then, else_] = &arguments[..] {
        let body_id = match db.run_with_environment(
            input_reference.clone(),
            condition.to_owned(),
            environment.to_owned(),
        )? {
            Ok(value) if value == Value::bool_true() => then,
            Ok(value) if value == Value::bool_false() => else_,
            Ok(_) => return None,
            Err(error) => return Some(Err(error)),
        };

        db.run_call(input_reference, body_id.to_owned(), vec![], environment)
    } else {
        Some(Err(Value::Text(
            format!(
                "Builtin if/else called with wrong number of arguments: {}, expected: {}",
                arguments.len(),
                3
            )
            .into(),
        )))
    }
}

fn panic(arguments: Vec<Value>) -> DiscoverResult {
    destructure!(arguments, [value], { Some(Err(value.clone())) })
}

fn print(arguments: Vec<Value>) -> DiscoverResult {
    destructure!(arguments, [value], {
        println!("{:?}", value);
        Some(Ok(Value::nothing()))
    })
}

fn type_of(arguments: Vec<Value>) -> DiscoverResult {
    destructure!(arguments, [value], {
        match value {
            Value::Int(_) => Some(Ok(Value::Symbol("Int".to_owned()))),
            Value::Text(_) => Some(Ok(Value::Symbol("Text".to_owned()))),
            Value::Symbol(_) => Some(Ok(Value::Symbol("Symbol".to_owned()))),
            Value::Lambda(_) => Some(Ok(Value::Symbol("Function".to_owned()))),
        }
    })
}
<|endoftext|>


<commit_before>extern crate byteorder;
extern crate find_folder;
extern crate rs6502;
extern crate sdl2;
extern crate vm;

mod ship;

use std::path::Path;

use byteorder::{ByteOrder, LittleEndian};

use find_folder::Search;

use sdl2::event::Event;
use sdl2::keyboard::{Keycode, Scancode};
use sdl2::image::LoadTexture;
use sdl2::pixels::Color;
use sdl2::rect::Rect;
use sdl2::render::{Renderer, TextureQuery};

use rs6502::{Assembler, CodeSegment, Cpu};
use vm::{Console, Position, Text, VirtualMachine};

const FPS_STEP: u32 = 1000 / 60;

fn main() {

    let window_width = 1280;
    let window_height = 720;

    let sdl_context = sdl2::init().unwrap();
    let ttf_context = sdl2::ttf::init().unwrap();
    let video_subsystem = sdl_context.video().unwrap();

    let window = video_subsystem.window("hakka", window_width, window_height)
        .build()
        .unwrap();

    let (window_width, _) = window.size();

    let mut renderer = window.renderer()
        .accelerated()
        .build()
        .unwrap();

    let local = Search::Parents(3).for_folder("training-1").unwrap();
    let assets = Search::KidsThenParents(3, 3).for_folder("assets").unwrap();

    let ship_texture = renderer.load_texture(&assets.join("ship.png")).unwrap();
    let ship_flame_texture = renderer.load_texture(&assets.join("ship-flame.png"))
        .unwrap();

    let font = assets.join("FantasqueSansMono-Bold.ttf");

    let finish_text = Text::new(&ttf_context,
                                &mut renderer,
                                "FINISH",
                                Position::HorizontalCenter((window_width / 2) as i32, 25),
                                56,
                                Color::RGBA(0, 0, 0, 255),
                                font.to_str().unwrap());

    let win_text = Text::new(&ttf_context,
                             &mut renderer,
                             "PASSED",
                             Position::HorizontalCenter((window_width / 2) as i32, 330),
                             64,
                             Color::RGBA(0, 0, 0, 255),
                             font.to_str().unwrap());

    let TextureQuery { width: ship_width, .. } = ship_texture.query();
    let cpu = init_cpu(&mut renderer, ship_width);
    let segments = assemble(local.join("level.asm"));
    let console = Console::new(&ttf_context, &mut renderer, font.to_str().unwrap());
    let mut vm = VirtualMachine::new(cpu, 150, console);
    vm.load_code_segments(segments);

    let mut events = sdl_context.event_pump().unwrap();

    let mut level_complete = false;
    let mut ship = ship::Ship::new(ship_texture,
                                   ship_flame_texture,
                                   Position::HorizontalCenter((window_width / 2) as i32, 500));
    let mut last_fps = 0;
    let mut monitor_last = 0;

    'running: loop {

        for event in events.poll_iter() {
            if vm.console.visible {
                vm.console.process(&event);
            } else {
                match event {
                    Event::Quit { .. } => break 'running,
                    Event::KeyUp { .. } => {
                        vm.cpu.memory[0x04] = 0;
                    }
                    Event::KeyDown { keycode, scancode, timestamp, .. } => {
                        if let Some(Scancode::Grave) = scancode {
                            vm.console.toggle(timestamp);
                        }

                        match keycode {
                            Some(Keycode::Escape) => break 'running,

                            //Movement
                            Some(Keycode::Up) => {
                                vm.cpu.memory[0x04] = 38;
                            },
                            Some(Keycode::Down) => {
                                vm.cpu.memory[0x04]  = 40;
                            },
                            _ => (),
                        }
                    }
                    _ => (),
                }
            }
        }

        if !level_complete {
            if let Some(cmd) = vm.console.try_process_command() {
                vm.execute_command(cmd);
            }
            ship.process(&vm.cpu.memory[..]);

            // Pull the ship back so it can't go past a certain spot
            if ship.y <= 0x190 && ship.y >= 0x100 && vm.cpu.memory[0x04] != 0 {
                vm.cpu.memory[0x02] = 0x90;
                vm.cpu.memory[0x03] = 0x01;
            }
        }

        let now = sdl_context.timer().unwrap().ticks();
        let delta = now - last_fps;
        if delta < FPS_STEP {
            sdl_context.timer().unwrap().delay(FPS_STEP - delta);
        } else {
            vm.cycle();
            if !vm.cpu.flags.interrupt_disabled {
                renderer.set_draw_color(Color::RGBA(0, 0, 0, 255));
                renderer.clear();
                if level_complete {
                    draw_passed_background(&mut renderer);
                    win_text.render(&mut renderer);
                }
                draw_finish_background(&mut renderer);
                finish_text.render(&mut renderer);
                if vm.cpu.memory[0x07] > 0 {
                    ship.render_flame(&mut renderer);
                }
                ship.render(&mut renderer);
                if ship.y <= 0x8C {
                    level_complete = true;
                }
                vm.render(&mut renderer);
                renderer.present();
                last_fps = now;
            }
        }

        // Dump the CPU memory at 1 second intervals if the monitor is enabled
        let delta = now - monitor_last;
        if delta > 1000 && vm.monitor.enabled {
            vm.dump_memory();
            monitor_last = now;
        }
    }
}

fn assemble<P>(path: P) -> Vec<CodeSegment>
    where P: AsRef<Path>
{
    let mut assembler = Assembler::new();
    assembler.assemble_file(path, 0xC000).unwrap()
}

fn init_cpu(renderer: &mut Renderer, ship_width: u32) -> Cpu {
    let mut cpu = Cpu::new();
    cpu.flags.interrupt_disabled = false;

    LittleEndian::write_u16(&mut cpu.memory[0..],
                            renderer.window().unwrap().size().0 as u16 / 2 -
                            (ship_width as u16 / 2));
    cpu.memory[0x02] = 0xFF;
    cpu.memory[0x03] = 0x01;
    cpu.memory[0x05] = 0x05;
    cpu.memory[0x06] = 0x00;

    cpu
}

fn draw_text_background(renderer: &mut Renderer, color: Color, y: i32) {
    let width = renderer.window().unwrap().size().0;
    renderer.set_draw_color(color);
    renderer.fill_rect(Rect::new(0, y, width, 120)).unwrap();
}

fn draw_finish_background(renderer: &mut Renderer) {
    draw_text_background(renderer, Color::RGB(0, 144, 192), 0);
}

fn draw_passed_background(renderer: &mut Renderer) {
    draw_text_background(renderer, Color::RGB(0, 255, 0), 300);
}
<commit_msg>Movement no longer stops when any key is released<commit_after>extern crate byteorder;
extern crate find_folder;
extern crate rs6502;
extern crate sdl2;
extern crate vm;

mod ship;

use std::path::Path;

use byteorder::{ByteOrder, LittleEndian};

use find_folder::Search;

use sdl2::event::Event;
use sdl2::keyboard::{Keycode, Scancode};
use sdl2::image::LoadTexture;
use sdl2::pixels::Color;
use sdl2::rect::Rect;
use sdl2::render::{Renderer, TextureQuery};

use rs6502::{Assembler, CodeSegment, Cpu};
use vm::{Console, Position, Text, VirtualMachine};

const FPS_STEP: u32 = 1000 / 60;

fn main() {

    let window_width = 1280;
    let window_height = 720;

    let sdl_context = sdl2::init().unwrap();
    let ttf_context = sdl2::ttf::init().unwrap();
    let video_subsystem = sdl_context.video().unwrap();

    let window = video_subsystem.window("hakka", window_width, window_height)
        .build()
        .unwrap();

    let (window_width, _) = window.size();

    let mut renderer = window.renderer()
        .accelerated()
        .build()
        .unwrap();

    let local = Search::Parents(3).for_folder("training-1").unwrap();
    let assets = Search::KidsThenParents(3, 3).for_folder("assets").unwrap();

    let ship_texture = renderer.load_texture(&assets.join("ship.png")).unwrap();
    let ship_flame_texture = renderer.load_texture(&assets.join("ship-flame.png"))
        .unwrap();

    let font = assets.join("FantasqueSansMono-Bold.ttf");

    let finish_text = Text::new(&ttf_context,
                                &mut renderer,
                                "FINISH",
                                Position::HorizontalCenter((window_width / 2) as i32, 25),
                                56,
                                Color::RGBA(0, 0, 0, 255),
                                font.to_str().unwrap());

    let win_text = Text::new(&ttf_context,
                             &mut renderer,
                             "PASSED",
                             Position::HorizontalCenter((window_width / 2) as i32, 330),
                             64,
                             Color::RGBA(0, 0, 0, 255),
                             font.to_str().unwrap());

    let TextureQuery { width: ship_width, .. } = ship_texture.query();
    let cpu = init_cpu(&mut renderer, ship_width);
    let segments = assemble(local.join("level.asm"));
    let console = Console::new(&ttf_context, &mut renderer, font.to_str().unwrap());
    let mut vm = VirtualMachine::new(cpu, 150, console);
    vm.load_code_segments(segments);

    let mut events = sdl_context.event_pump().unwrap();

    let mut level_complete = false;
    let mut ship = ship::Ship::new(ship_texture,
                                   ship_flame_texture,
                                   Position::HorizontalCenter((window_width / 2) as i32, 500));
    let mut last_fps = 0;
    let mut monitor_last = 0;

    'running: loop {

        for event in events.poll_iter() {
            if vm.console.visible {
                vm.console.process(&event);
            } else {
                match event {
                    Event::Quit { .. } => break 'running,
                    Event::KeyUp { keycode, .. } => {
                        match keycode {
                            Some(Keycode::Up) | Some(Keycode::Down) => {
                                vm.cpu.memory[0x04] = 0;
                            }
                            _ => (),
                        }
                    }
                    Event::KeyDown { keycode, scancode, timestamp, .. } => {
                        if let Some(Scancode::Grave) = scancode {
                            vm.console.toggle(timestamp);
                        }

                        match keycode {
                            Some(Keycode::Escape) => break 'running,

                            //Movement
                            Some(Keycode::Up) => {
                                vm.cpu.memory[0x04] = 38;
                            },
                            Some(Keycode::Down) => {
                                vm.cpu.memory[0x04]  = 40;
                            },
                            _ => (),
                        }
                    }
                    _ => (),
                }
            }
        }

        if !level_complete {
            if let Some(cmd) = vm.console.try_process_command() {
                vm.execute_command(cmd);
            }
            ship.process(&vm.cpu.memory[..]);

            // Pull the ship back so it can't go past a certain spot
            if ship.y <= 0x190 && ship.y >= 0x100 && vm.cpu.memory[0x04] != 0 {
                vm.cpu.memory[0x02] = 0x90;
                vm.cpu.memory[0x03] = 0x01;
            }
        }

        let now = sdl_context.timer().unwrap().ticks();
        let delta = now - last_fps;
        if delta < FPS_STEP {
            sdl_context.timer().unwrap().delay(FPS_STEP - delta);
        } else {
            vm.cycle();
            if !vm.cpu.flags.interrupt_disabled {
                renderer.set_draw_color(Color::RGBA(0, 0, 0, 255));
                renderer.clear();
                if level_complete {
                    draw_passed_background(&mut renderer);
                    win_text.render(&mut renderer);
                }
                draw_finish_background(&mut renderer);
                finish_text.render(&mut renderer);
                if vm.cpu.memory[0x07] > 0 {
                    ship.render_flame(&mut renderer);
                }
                ship.render(&mut renderer);
                if ship.y <= 0x8C {
                    level_complete = true;
                }
                vm.render(&mut renderer);
                renderer.present();
                last_fps = now;
            }
        }

        // Dump the CPU memory at 1 second intervals if the monitor is enabled
        let delta = now - monitor_last;
        if delta > 1000 && vm.monitor.enabled {
            vm.dump_memory();
            monitor_last = now;
        }
    }
}

fn assemble<P>(path: P) -> Vec<CodeSegment>
    where P: AsRef<Path>
{
    let mut assembler = Assembler::new();
    assembler.assemble_file(path, 0xC000).unwrap()
}

fn init_cpu(renderer: &mut Renderer, ship_width: u32) -> Cpu {
    let mut cpu = Cpu::new();
    cpu.flags.interrupt_disabled = false;

    LittleEndian::write_u16(&mut cpu.memory[0..],
                            renderer.window().unwrap().size().0 as u16 / 2 -
                            (ship_width as u16 / 2));
    cpu.memory[0x02] = 0xFF;
    cpu.memory[0x03] = 0x01;
    cpu.memory[0x05] = 0x05;
    cpu.memory[0x06] = 0x00;

    cpu
}

fn draw_text_background(renderer: &mut Renderer, color: Color, y: i32) {
    let width = renderer.window().unwrap().size().0;
    renderer.set_draw_color(color);
    renderer.fill_rect(Rect::new(0, y, width, 120)).unwrap();
}

fn draw_finish_background(renderer: &mut Renderer) {
    draw_text_background(renderer, Color::RGB(0, 144, 192), 0);
}

fn draw_passed_background(renderer: &mut Renderer) {
    draw_text_background(renderer, Color::RGB(0, 255, 0), 300);
}
<|endoftext|>