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

#![allow(non_upper_case_globals)]

use core_foundation::array::CFArrayRef;
use core_foundation::base::{CFType, CFTypeID, CFTypeRef, TCFType};
use core_foundation::dictionary::{CFDictionary, CFDictionaryRef};
use core_foundation::number::{CFNumber, CFNumberRef};
use core_foundation::set::CFSetRef;
use core_foundation::string::{CFString, CFStringRef};
use core_foundation::url::CFURL;
use core_graphics::base::CGFloat;

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

/*
* CTFontTraits.h
*/
// actually, these are extern enums
pub type CTFontFormat = u32;
pub const kCTFontFormatUnrecognized: CTFontFormat = 0;
pub const kCTFontFormatOpenTypePostScript: CTFontFormat = 1;
pub const kCTFontFormatOpenTypeTrueType: CTFontFormat = 2;
pub const kCTFontFormatTrueType: CTFontFormat = 3;
pub const kCTFontFormatPostScript: CTFontFormat = 4;
pub const kCTFontFormatBitmap: CTFontFormat = 5;

pub const kCTFontClassMaskShift: u32 = 28;

pub type CTFontSymbolicTraits = u32;
pub const kCTFontItalicTrait: CTFontSymbolicTraits = (1 << 0);
pub const kCTFontBoldTrait: CTFontSymbolicTraits = (1 << 1);
pub const kCTFontExpandedTrait: CTFontSymbolicTraits = (1 << 5);
pub const kCTFontCondensedTrait: CTFontSymbolicTraits = (1 << 6);
pub const kCTFontMonoSpaceTrait: CTFontSymbolicTraits = (1 << 10);
pub const kCTFontVerticalTrait: CTFontSymbolicTraits = (1 << 11);
pub const kCTFontUIOptimizedTrait: CTFontSymbolicTraits = (1 << 12);
pub const kCTFontColorGlyphsTrait: CTFontSymbolicTraits = (1 << 13);
pub const kCTFontClassMaskTrait: CTFontSymbolicTraits = (15 << kCTFontClassMaskShift );

pub trait SymbolicTraitAccessors {
    fn is_italic(&self) -> bool;
    fn is_bold(&self) -> bool;
    fn is_expanded(&self) -> bool;
    fn is_condensed(&self) -> bool;
    fn is_monospace(&self) -> bool;
}

impl SymbolicTraitAccessors for CTFontSymbolicTraits {
    fn is_italic(&self) -> bool { (*self & kCTFontItalicTrait) != 0 }
    fn is_bold(&self) -> bool { (*self & kCTFontBoldTrait) != 0 }
    fn is_expanded(&self) -> bool { (*self & kCTFontExpandedTrait) != 0 }
    fn is_condensed(&self) -> bool { (*self & kCTFontCondensedTrait) != 0 }
    fn is_monospace(&self) -> bool { (*self & kCTFontMonoSpaceTrait) != 0 }
}

pub type CTFontStylisticClass = u32;
pub const kCTFontUnknownClass: CTFontStylisticClass = (0 << kCTFontClassMaskShift );
pub const kCTFontOldStyleSerifsClass: CTFontStylisticClass = (1 << kCTFontClassMaskShift );
pub const kCTFontTransitionalSerifsClass: CTFontStylisticClass = (2 << kCTFontClassMaskShift );
pub const kCTFontModernSerifsClass: CTFontStylisticClass = (3 << kCTFontClassMaskShift );
pub const kCTFontClarendonSerifsClass: CTFontStylisticClass = (4 << kCTFontClassMaskShift );
pub const kCTFontSlabSerifsClass: CTFontStylisticClass = (5 << kCTFontClassMaskShift );
pub const kCTFontFreeformSerifsClass: CTFontStylisticClass = (7 << kCTFontClassMaskShift );
pub const kCTFontSansSerifClass: CTFontStylisticClass = (8 << kCTFontClassMaskShift );
pub const kCTFontOrnamentalsClass: CTFontStylisticClass = (9 << kCTFontClassMaskShift );
pub const kCTFontScriptsClass: CTFontStylisticClass = (10 << kCTFontClassMaskShift );
pub const kCTFontSymbolicClass: CTFontStylisticClass = (12 << kCTFontClassMaskShift );

pub trait StylisticClassAccessors {
    fn is_serif(&self) -> bool;
    fn is_sans_serif(&self) -> bool;
    fn is_script(&self) -> bool;
    fn is_fantasy(&self) -> bool;
    fn is_symbols(&self) -> bool;
}

impl StylisticClassAccessors for CTFontStylisticClass {
    fn is_serif(&self) -> bool {
        let any_serif_class = kCTFontOldStyleSerifsClass
            | kCTFontTransitionalSerifsClass
            | kCTFontModernSerifsClass
            | kCTFontClarendonSerifsClass
            | kCTFontSlabSerifsClass
            | kCTFontFreeformSerifsClass;

        return (*self & any_serif_class) != 0;
    }

    fn is_sans_serif(&self) -> bool {
        return (*self & kCTFontSansSerifClass) != 0;
    }

    fn is_script(&self) -> bool {
        return (*self & kCTFontScriptsClass) != 0;
    }

    fn is_fantasy(&self) -> bool {
        return (*self & kCTFontOrnamentalsClass) != 0;
    }

    fn is_symbols(&self) -> bool {
        return (*self & kCTFontSymbolicClass) != 0;
    }
}

pub type CTFontAttributes = CFDictionary;

pub type CTFontTraits = CFDictionary;

pub trait TraitAccessors {
    fn symbolic_traits(&self) -> CTFontSymbolicTraits;
    fn normalized_weight(&self) -> f64;
    fn normalized_width(&self) -> f64;
    fn normalized_slant(&self) -> f64;
}

trait TraitAccessorPrivate {
    unsafe fn extract_number_for_key(&self, key: CFStringRef) -> CFNumber;
}

impl TraitAccessorPrivate for CTFontTraits {
    unsafe fn extract_number_for_key(&self, key: CFStringRef) -> CFNumber {
        let cftype = self.get_CFType(mem::transmute(key));
        assert!(cftype.instance_of::<CFNumber>());
        CFNumber::wrap_under_get_rule(mem::transmute(cftype.as_CFTypeRef()))
    }

}

impl TraitAccessors for CTFontTraits {
    fn symbolic_traits(&self) -> CTFontSymbolicTraits {
        unsafe {
            let number = self.extract_number_for_key(kCTFontSymbolicTrait);
            number.to_i64().unwrap() as u32
        }
    }

    fn normalized_weight(&self) -> f64 {
        unsafe {
            let number = self.extract_number_for_key(kCTFontWeightTrait);
            number.to_f64().unwrap()
        }
    }

    fn normalized_width(&self) -> f64 {
        unsafe {
            let number = self.extract_number_for_key(kCTFontWidthTrait);
            number.to_f64().unwrap()
        }
    }

    fn normalized_slant(&self) -> f64 {
        unsafe {
            let number = self.extract_number_for_key(kCTFontSlantTrait);
            number.to_f64().unwrap()
        }
    }
}

/*
* CTFontDescriptor.h
*/
pub type CTFontOrientation = u32;
pub const kCTFontDefaultOrientation: CTFontOrientation = 0;
pub const kCTFontHorizontalOrientation: CTFontOrientation = 1;
pub const kCTFontVerticalOrientation: CTFontOrientation = 2;

pub type CTFontPriority = u32;
pub const kCTFontPrioritySystem: CTFontPriority = 10000;
pub const kCTFontPriorityNetwork: CTFontPriority = 20000;
pub const kCTFontPriorityComputer: CTFontPriority = 30000;
pub const kCTFontPriorityUser: CTFontPriority = 40000;
pub const kCTFontPriorityDynamic: CTFontPriority = 50000;
pub const kCTFontPriorityProcess: CTFontPriority = 60000;

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

pub type CTFontDescriptorRef = *const __CTFontDescriptor;

declare_TCFType! {
    CTFontDescriptor, CTFontDescriptorRef
}
impl_TCFType!(CTFontDescriptor, CTFontDescriptorRef, CTFontDescriptorGetTypeID);
impl_CFTypeDescription!(CTFontDescriptor);

impl CTFontDescriptor {
    fn get_string_attribute(&self, attribute: CFStringRef) -> Option<String> {
        unsafe {
            let value = CTFontDescriptorCopyAttribute(self.0, attribute);
            if value.is_null() {
                return None
            }

            let value = CFType::wrap_under_create_rule(value);
            assert!(value.instance_of::<CFString>());
            let s = CFString::wrap_under_get_rule(mem::transmute(value.as_CFTypeRef()));
            Some(s.to_string())
        }
    }

}

impl CTFontDescriptor {
    pub fn family_name(&self) -> String {
        unsafe {
            let value = self.get_string_attribute(kCTFontFamilyNameAttribute);
            value.expect("A font must have a non-null family name.")
        }
    }

    pub fn font_name(&self) -> String {
        unsafe {
            let value = self.get_string_attribute(kCTFontNameAttribute);
            value.expect("A font must have a non-null name.")
        }
    }

    pub fn style_name(&self) -> String {
        unsafe {
            let value = self.get_string_attribute(kCTFontStyleNameAttribute);
            value.expect("A font must have a non-null style name.")
        }
    }

    pub fn display_name(&self) -> String {
        unsafe {
            let value = self.get_string_attribute(kCTFontDisplayNameAttribute);
            value.expect("A font must have a non-null display name.")
        }
    }

    pub fn font_path(&self) -> Option<String> {
        unsafe {
            let value = CTFontDescriptorCopyAttribute(self.0, kCTFontURLAttribute);
            if value.is_null() {
                return None;
            }

            let value = CFType::wrap_under_create_rule(value);
            assert!(value.instance_of::<CFURL>());
            let url = CFURL::wrap_under_get_rule(mem::transmute(value.as_CFTypeRef()));
            Some(format!("{:?}", url))
        }
    }

    pub fn traits(&self) -> CTFontTraits {
        unsafe {
            let value = CTFontDescriptorCopyAttribute(self.0, kCTFontTraitsAttribute);
            assert!(!value.is_null());
            let value = CFType::wrap_under_create_rule(value);
            assert!(value.instance_of::<CFDictionary>());
            CFDictionary::wrap_under_get_rule(mem::transmute(value.as_CFTypeRef()))
        }
    }
}

pub fn new_from_attributes(attributes: &CFDictionary) -> CTFontDescriptor {
    unsafe {
        let result: CTFontDescriptorRef =
            CTFontDescriptorCreateWithAttributes(attributes.as_concrete_TypeRef());
        CTFontDescriptor::wrap_under_create_rule(result)
    }
}

pub fn debug_descriptor(desc: &CTFontDescriptor) {
    println!("family: {}", desc.family_name());
    println!("name: {}", desc.font_name());
    println!("style: {}", desc.style_name());
    println!("display: {}", desc.display_name());
    println!("path: {:?}", desc.font_path());
    desc.show();
}

extern {
    /*
     * CTFontTraits.h
     */

    // font trait constants
    pub static kCTFontSymbolicTrait: CFStringRef;
    pub static kCTFontWeightTrait: CFStringRef;
    pub static kCTFontWidthTrait: CFStringRef;
    pub static kCTFontSlantTrait: CFStringRef;

    /*
     * CTFontDescriptor.h
     */

    // font attribute constants. Note that the name-related attributes
    // here are somewhat flaky. Servo creates CTFont instances and
    // then uses CTFontCopyName to get more fine-grained names.
    pub static kCTFontURLAttribute:                  CFStringRef; // value: CFURLRef
    pub static kCTFontNameAttribute:                 CFStringRef; // value: CFStringRef
    pub static kCTFontDisplayNameAttribute:          CFStringRef; // value: CFStringRef
    pub static kCTFontFamilyNameAttribute:           CFStringRef; // value: CFStringRef
    pub static kCTFontStyleNameAttribute:            CFStringRef; // value: CFStringRef
    pub static kCTFontTraitsAttribute:               CFStringRef;
    pub static kCTFontVariationAttribute:            CFStringRef;
    pub static kCTFontSizeAttribute:                 CFStringRef;
    pub static kCTFontMatrixAttribute:               CFStringRef;
    pub static kCTFontCascadeListAttribute:          CFStringRef;
    pub static kCTFontCharacterSetAttribute:         CFStringRef;
    pub static kCTFontLanguagesAttribute:            CFStringRef;
    pub static kCTFontBaselineAdjustAttribute:       CFStringRef;
    pub static kCTFontMacintoshEncodingsAttribute:   CFStringRef;
    pub static kCTFontFeaturesAttribute:             CFStringRef;
    pub static kCTFontFeatureSettingsAttribute:      CFStringRef;
    pub static kCTFontFixedAdvanceAttribute:         CFStringRef;
    pub static kCTFontOrientationAttribute:          CFStringRef;
    pub static kCTFontFormatAttribute:               CFStringRef;
    pub static kCTFontRegistrationScopeAttribute:    CFStringRef;
    pub static kCTFontPriorityAttribute:             CFStringRef;
    pub static kCTFontEnabledAttribute:              CFStringRef;

    pub fn CTFontDescriptorCopyAttribute(descriptor: CTFontDescriptorRef,
                                         attribute: CFStringRef) -> CFTypeRef;
    pub fn CTFontDescriptorCopyAttributes(descriptor: CTFontDescriptorRef) -> CFDictionaryRef;
    pub fn CTFontDescriptorCopyLocalizedAttribute(descriptor: CTFontDescriptorRef,
                                                  attribute: CFStringRef,
                                                  language: *mut CFStringRef) -> CFTypeRef;
    pub fn CTFontDescriptorCreateCopyWithAttributes(original: CTFontDescriptorRef,
                                                    attributes: CFDictionaryRef) -> CTFontDescriptorRef;
    pub fn CTFontDescriptorCreateCopyWithFeature(original: CTFontDescriptorRef,
                                                 featureTypeIdentifier: CFNumberRef,
                                                 featureSelectorIdentifier: CFNumberRef) -> CTFontDescriptorRef;
    pub fn CTFontDescriptorCreateCopyWithVariation(original: CTFontDescriptorRef,
                                                   variationIdentifier: CFNumberRef,
                                                   variationValue: CGFloat) -> CTFontDescriptorRef;
    pub fn CTFontDescriptorCreateMatchingFontDescriptor(descriptor: CTFontDescriptorRef,
                                                        mandatoryAttributes: CFSetRef) -> CTFontDescriptorRef;
    pub fn CTFontDescriptorCreateWithAttributes(attributes: CFDictionaryRef) -> CTFontDescriptorRef;
    pub fn CTFontDescriptorCreateWithNameAndSize(name: CFStringRef, size: CGFloat) -> CTFontDescriptorRef;
    pub fn CTFontDescriptorGetTypeID() -> CFTypeID;
}

extern {
    pub fn CTFontDescriptorCreateMatchingFontDescriptors(descriptor: CTFontDescriptorRef,
                                                         mandatoryAttributes: CFSetRef) -> CFArrayRef;
}
<commit_msg>Make CTFontDescriptor::font_path() return filesystem path, not URL<commit_after>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![allow(non_upper_case_globals)]

use core_foundation::array::CFArrayRef;
use core_foundation::base::{CFType, CFTypeID, CFTypeRef, TCFType};
use core_foundation::dictionary::{CFDictionary, CFDictionaryRef};
use core_foundation::number::{CFNumber, CFNumberRef};
use core_foundation::set::CFSetRef;
use core_foundation::string::{CFString, CFStringRef};
use core_foundation::url::{CFURLCopyFileSystemPath, kCFURLPOSIXPathStyle, CFURL};
use core_graphics::base::CGFloat;

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

/*
* CTFontTraits.h
*/
// actually, these are extern enums
pub type CTFontFormat = u32;
pub const kCTFontFormatUnrecognized: CTFontFormat = 0;
pub const kCTFontFormatOpenTypePostScript: CTFontFormat = 1;
pub const kCTFontFormatOpenTypeTrueType: CTFontFormat = 2;
pub const kCTFontFormatTrueType: CTFontFormat = 3;
pub const kCTFontFormatPostScript: CTFontFormat = 4;
pub const kCTFontFormatBitmap: CTFontFormat = 5;

pub const kCTFontClassMaskShift: u32 = 28;

pub type CTFontSymbolicTraits = u32;
pub const kCTFontItalicTrait: CTFontSymbolicTraits = (1 << 0);
pub const kCTFontBoldTrait: CTFontSymbolicTraits = (1 << 1);
pub const kCTFontExpandedTrait: CTFontSymbolicTraits = (1 << 5);
pub const kCTFontCondensedTrait: CTFontSymbolicTraits = (1 << 6);
pub const kCTFontMonoSpaceTrait: CTFontSymbolicTraits = (1 << 10);
pub const kCTFontVerticalTrait: CTFontSymbolicTraits = (1 << 11);
pub const kCTFontUIOptimizedTrait: CTFontSymbolicTraits = (1 << 12);
pub const kCTFontColorGlyphsTrait: CTFontSymbolicTraits = (1 << 13);
pub const kCTFontClassMaskTrait: CTFontSymbolicTraits = (15 << kCTFontClassMaskShift );

pub trait SymbolicTraitAccessors {
    fn is_italic(&self) -> bool;
    fn is_bold(&self) -> bool;
    fn is_expanded(&self) -> bool;
    fn is_condensed(&self) -> bool;
    fn is_monospace(&self) -> bool;
}

impl SymbolicTraitAccessors for CTFontSymbolicTraits {
    fn is_italic(&self) -> bool { (*self & kCTFontItalicTrait) != 0 }
    fn is_bold(&self) -> bool { (*self & kCTFontBoldTrait) != 0 }
    fn is_expanded(&self) -> bool { (*self & kCTFontExpandedTrait) != 0 }
    fn is_condensed(&self) -> bool { (*self & kCTFontCondensedTrait) != 0 }
    fn is_monospace(&self) -> bool { (*self & kCTFontMonoSpaceTrait) != 0 }
}

pub type CTFontStylisticClass = u32;
pub const kCTFontUnknownClass: CTFontStylisticClass = (0 << kCTFontClassMaskShift );
pub const kCTFontOldStyleSerifsClass: CTFontStylisticClass = (1 << kCTFontClassMaskShift );
pub const kCTFontTransitionalSerifsClass: CTFontStylisticClass = (2 << kCTFontClassMaskShift );
pub const kCTFontModernSerifsClass: CTFontStylisticClass = (3 << kCTFontClassMaskShift );
pub const kCTFontClarendonSerifsClass: CTFontStylisticClass = (4 << kCTFontClassMaskShift );
pub const kCTFontSlabSerifsClass: CTFontStylisticClass = (5 << kCTFontClassMaskShift );
pub const kCTFontFreeformSerifsClass: CTFontStylisticClass = (7 << kCTFontClassMaskShift );
pub const kCTFontSansSerifClass: CTFontStylisticClass = (8 << kCTFontClassMaskShift );
pub const kCTFontOrnamentalsClass: CTFontStylisticClass = (9 << kCTFontClassMaskShift );
pub const kCTFontScriptsClass: CTFontStylisticClass = (10 << kCTFontClassMaskShift );
pub const kCTFontSymbolicClass: CTFontStylisticClass = (12 << kCTFontClassMaskShift );

pub trait StylisticClassAccessors {
    fn is_serif(&self) -> bool;
    fn is_sans_serif(&self) -> bool;
    fn is_script(&self) -> bool;
    fn is_fantasy(&self) -> bool;
    fn is_symbols(&self) -> bool;
}

impl StylisticClassAccessors for CTFontStylisticClass {
    fn is_serif(&self) -> bool {
        let any_serif_class = kCTFontOldStyleSerifsClass
            | kCTFontTransitionalSerifsClass
            | kCTFontModernSerifsClass
            | kCTFontClarendonSerifsClass
            | kCTFontSlabSerifsClass
            | kCTFontFreeformSerifsClass;

        return (*self & any_serif_class) != 0;
    }

    fn is_sans_serif(&self) -> bool {
        return (*self & kCTFontSansSerifClass) != 0;
    }

    fn is_script(&self) -> bool {
        return (*self & kCTFontScriptsClass) != 0;
    }

    fn is_fantasy(&self) -> bool {
        return (*self & kCTFontOrnamentalsClass) != 0;
    }

    fn is_symbols(&self) -> bool {
        return (*self & kCTFontSymbolicClass) != 0;
    }
}

pub type CTFontAttributes = CFDictionary;

pub type CTFontTraits = CFDictionary;

pub trait TraitAccessors {
    fn symbolic_traits(&self) -> CTFontSymbolicTraits;
    fn normalized_weight(&self) -> f64;
    fn normalized_width(&self) -> f64;
    fn normalized_slant(&self) -> f64;
}

trait TraitAccessorPrivate {
    unsafe fn extract_number_for_key(&self, key: CFStringRef) -> CFNumber;
}

impl TraitAccessorPrivate for CTFontTraits {
    unsafe fn extract_number_for_key(&self, key: CFStringRef) -> CFNumber {
        let cftype = self.get_CFType(mem::transmute(key));
        assert!(cftype.instance_of::<CFNumber>());
        CFNumber::wrap_under_get_rule(mem::transmute(cftype.as_CFTypeRef()))
    }

}

impl TraitAccessors for CTFontTraits {
    fn symbolic_traits(&self) -> CTFontSymbolicTraits {
        unsafe {
            let number = self.extract_number_for_key(kCTFontSymbolicTrait);
            number.to_i64().unwrap() as u32
        }
    }

    fn normalized_weight(&self) -> f64 {
        unsafe {
            let number = self.extract_number_for_key(kCTFontWeightTrait);
            number.to_f64().unwrap()
        }
    }

    fn normalized_width(&self) -> f64 {
        unsafe {
            let number = self.extract_number_for_key(kCTFontWidthTrait);
            number.to_f64().unwrap()
        }
    }

    fn normalized_slant(&self) -> f64 {
        unsafe {
            let number = self.extract_number_for_key(kCTFontSlantTrait);
            number.to_f64().unwrap()
        }
    }
}

/*
* CTFontDescriptor.h
*/
pub type CTFontOrientation = u32;
pub const kCTFontDefaultOrientation: CTFontOrientation = 0;
pub const kCTFontHorizontalOrientation: CTFontOrientation = 1;
pub const kCTFontVerticalOrientation: CTFontOrientation = 2;

pub type CTFontPriority = u32;
pub const kCTFontPrioritySystem: CTFontPriority = 10000;
pub const kCTFontPriorityNetwork: CTFontPriority = 20000;
pub const kCTFontPriorityComputer: CTFontPriority = 30000;
pub const kCTFontPriorityUser: CTFontPriority = 40000;
pub const kCTFontPriorityDynamic: CTFontPriority = 50000;
pub const kCTFontPriorityProcess: CTFontPriority = 60000;

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

pub type CTFontDescriptorRef = *const __CTFontDescriptor;

declare_TCFType! {
    CTFontDescriptor, CTFontDescriptorRef
}
impl_TCFType!(CTFontDescriptor, CTFontDescriptorRef, CTFontDescriptorGetTypeID);
impl_CFTypeDescription!(CTFontDescriptor);

impl CTFontDescriptor {
    fn get_string_attribute(&self, attribute: CFStringRef) -> Option<String> {
        unsafe {
            let value = CTFontDescriptorCopyAttribute(self.0, attribute);
            if value.is_null() {
                return None
            }

            let value = CFType::wrap_under_create_rule(value);
            assert!(value.instance_of::<CFString>());
            let s = CFString::wrap_under_get_rule(mem::transmute(value.as_CFTypeRef()));
            Some(s.to_string())
        }
    }

}

impl CTFontDescriptor {
    pub fn family_name(&self) -> String {
        unsafe {
            let value = self.get_string_attribute(kCTFontFamilyNameAttribute);
            value.expect("A font must have a non-null family name.")
        }
    }

    pub fn font_name(&self) -> String {
        unsafe {
            let value = self.get_string_attribute(kCTFontNameAttribute);
            value.expect("A font must have a non-null name.")
        }
    }

    pub fn style_name(&self) -> String {
        unsafe {
            let value = self.get_string_attribute(kCTFontStyleNameAttribute);
            value.expect("A font must have a non-null style name.")
        }
    }

    pub fn display_name(&self) -> String {
        unsafe {
            let value = self.get_string_attribute(kCTFontDisplayNameAttribute);
            value.expect("A font must have a non-null display name.")
        }
    }

    pub fn font_path(&self) -> Option<String> {
        unsafe {
            let value = CTFontDescriptorCopyAttribute(self.0, kCTFontURLAttribute);
            if value.is_null() {
                return None;
            }

            let value = CFType::wrap_under_create_rule(value);
            assert!(value.instance_of::<CFURL>());
            let url = CFURL::wrap_under_get_rule(mem::transmute(value.as_CFTypeRef()));
            let path = CFString::wrap_under_create_rule(CFURLCopyFileSystemPath(
                url.as_concrete_TypeRef(),
                kCFURLPOSIXPathStyle,
            )).to_string();
            Some(path)
        }
    }

    pub fn traits(&self) -> CTFontTraits {
        unsafe {
            let value = CTFontDescriptorCopyAttribute(self.0, kCTFontTraitsAttribute);
            assert!(!value.is_null());
            let value = CFType::wrap_under_create_rule(value);
            assert!(value.instance_of::<CFDictionary>());
            CFDictionary::wrap_under_get_rule(mem::transmute(value.as_CFTypeRef()))
        }
    }
}

pub fn new_from_attributes(attributes: &CFDictionary) -> CTFontDescriptor {
    unsafe {
        let result: CTFontDescriptorRef =
            CTFontDescriptorCreateWithAttributes(attributes.as_concrete_TypeRef());
        CTFontDescriptor::wrap_under_create_rule(result)
    }
}

pub fn debug_descriptor(desc: &CTFontDescriptor) {
    println!("family: {}", desc.family_name());
    println!("name: {}", desc.font_name());
    println!("style: {}", desc.style_name());
    println!("display: {}", desc.display_name());
    println!("path: {:?}", desc.font_path());
    desc.show();
}

extern {
    /*
     * CTFontTraits.h
     */

    // font trait constants
    pub static kCTFontSymbolicTrait: CFStringRef;
    pub static kCTFontWeightTrait: CFStringRef;
    pub static kCTFontWidthTrait: CFStringRef;
    pub static kCTFontSlantTrait: CFStringRef;

    /*
     * CTFontDescriptor.h
     */

    // font attribute constants. Note that the name-related attributes
    // here are somewhat flaky. Servo creates CTFont instances and
    // then uses CTFontCopyName to get more fine-grained names.
    pub static kCTFontURLAttribute:                  CFStringRef; // value: CFURLRef
    pub static kCTFontNameAttribute:                 CFStringRef; // value: CFStringRef
    pub static kCTFontDisplayNameAttribute:          CFStringRef; // value: CFStringRef
    pub static kCTFontFamilyNameAttribute:           CFStringRef; // value: CFStringRef
    pub static kCTFontStyleNameAttribute:            CFStringRef; // value: CFStringRef
    pub static kCTFontTraitsAttribute:               CFStringRef;
    pub static kCTFontVariationAttribute:            CFStringRef;
    pub static kCTFontSizeAttribute:                 CFStringRef;
    pub static kCTFontMatrixAttribute:               CFStringRef;
    pub static kCTFontCascadeListAttribute:          CFStringRef;
    pub static kCTFontCharacterSetAttribute:         CFStringRef;
    pub static kCTFontLanguagesAttribute:            CFStringRef;
    pub static kCTFontBaselineAdjustAttribute:       CFStringRef;
    pub static kCTFontMacintoshEncodingsAttribute:   CFStringRef;
    pub static kCTFontFeaturesAttribute:             CFStringRef;
    pub static kCTFontFeatureSettingsAttribute:      CFStringRef;
    pub static kCTFontFixedAdvanceAttribute:         CFStringRef;
    pub static kCTFontOrientationAttribute:          CFStringRef;
    pub static kCTFontFormatAttribute:               CFStringRef;
    pub static kCTFontRegistrationScopeAttribute:    CFStringRef;
    pub static kCTFontPriorityAttribute:             CFStringRef;
    pub static kCTFontEnabledAttribute:              CFStringRef;

    pub fn CTFontDescriptorCopyAttribute(descriptor: CTFontDescriptorRef,
                                         attribute: CFStringRef) -> CFTypeRef;
    pub fn CTFontDescriptorCopyAttributes(descriptor: CTFontDescriptorRef) -> CFDictionaryRef;
    pub fn CTFontDescriptorCopyLocalizedAttribute(descriptor: CTFontDescriptorRef,
                                                  attribute: CFStringRef,
                                                  language: *mut CFStringRef) -> CFTypeRef;
    pub fn CTFontDescriptorCreateCopyWithAttributes(original: CTFontDescriptorRef,
                                                    attributes: CFDictionaryRef) -> CTFontDescriptorRef;
    pub fn CTFontDescriptorCreateCopyWithFeature(original: CTFontDescriptorRef,
                                                 featureTypeIdentifier: CFNumberRef,
                                                 featureSelectorIdentifier: CFNumberRef) -> CTFontDescriptorRef;
    pub fn CTFontDescriptorCreateCopyWithVariation(original: CTFontDescriptorRef,
                                                   variationIdentifier: CFNumberRef,
                                                   variationValue: CGFloat) -> CTFontDescriptorRef;
    pub fn CTFontDescriptorCreateMatchingFontDescriptor(descriptor: CTFontDescriptorRef,
                                                        mandatoryAttributes: CFSetRef) -> CTFontDescriptorRef;
    pub fn CTFontDescriptorCreateWithAttributes(attributes: CFDictionaryRef) -> CTFontDescriptorRef;
    pub fn CTFontDescriptorCreateWithNameAndSize(name: CFStringRef, size: CGFloat) -> CTFontDescriptorRef;
    pub fn CTFontDescriptorGetTypeID() -> CFTypeID;
}

extern {
    pub fn CTFontDescriptorCreateMatchingFontDescriptors(descriptor: CTFontDescriptorRef,
                                                         mandatoryAttributes: CFSetRef) -> CFArrayRef;
}
<|endoftext|>


<commit_before>use std::io::{ self, BufRead, Write };
use std::borrow::ToOwned;
use std::iter::{ Iterator, AdditiveIterator };

use log::Event;
use format::{ Encode, Decode };

use l::LogLevel::Info;

use chrono::*;

pub struct Weechat3;

static TIME_DATE_FORMAT: &'static str = "%Y-%m-%d %H:%M:%S";

pub struct Iter<R> where R: BufRead {
    input: R,
    buffer: String
}

impl<R> Iterator for Iter<R> where R: BufRead {
    type Item = ::Result<Event>;
    fn next(&mut self) -> Option<::Result<Event>> {
        fn timestamp(date: &str, time: &str) -> i64 {
            UTC.datetime_from_str(&format!("{} {}", date, time), TIME_DATE_FORMAT).unwrap().timestamp()
        }
        fn join(s: &[&str], splits: &[&str]) -> String {
            let len = s.iter().map(|s| s.len()).sum();
            let mut out = s.iter().zip(splits.iter()).fold(String::with_capacity(len),
               |mut s, (b, split)| { s.push_str(b); s.push(split); s });
            out.pop(); out
        }
        fn mask(s: &str) -> String {
            s.trim_left_matches('(').trim_right_matches(')').to_owned()
        }

        loop {
            self.buffer.clear();
            match self.input.read_line(&mut self.buffer) {
                Ok(0) | Err(_) => return None,
                Ok(_) => ()
            }

            let mut split_tokens = Vec::new();
            let tokens = self.buffer.split(|c: char| { split_tokens.push(c); c.is_whitespace() }).collect::<Vec<_>>();
            if log_enabled!(Info) {
                info!("Original:  `{}`", self.buffer);
                info!("Parsing:   {:?}", tokens);
            }
            match tokens[..tokens.len() - 1].as_ref() {
                [date, time, "-->", nick, host, "has", "joined", channel, _..] => return Some(Ok(Event::Join {
                    nick: nick.to_owned(), channel: channel.to_owned(), mask: mask(host),
                    time: timestamp(date, time)
                })),
                [date, time, "<--", nick, host, "has", "left", channel, reason..] => return Some(Ok(Event::Part {
                    nick: nick.to_owned(), channel: channel.to_owned(), mask: mask(host),
                    reason: mask(&join(reason)), time: timestamp(date, time)
                })),
                [date, time, "--", notice, content..]
                    if notice.starts_with("Notice(")
                    => return Some(Ok(Event::Notice {
                    nick: notice["Notice(".len()..notice.len() - 2].to_owned(),
                    content: join(content),
                    time: timestamp(date, time)
                })),
                [date, time, "--", "irc:", "disconnected", "from", "server", _..] => return Some(Ok(Event::Disconnect {
                    time: timestamp(date, time)
                })),
                [date, time, sp, "*", nick, msg..]
                    if sp.is_empty()
                    => return Some(Ok(Event::Action {
                    from: nick.to_owned(), content: join(msg),
                    time: timestamp(date, time)
                })),
                [date, time, nick, msg..] => return Some(Ok(Event::Msg {
                    from: nick.to_owned(),
                    content: join(msg),
                    time: timestamp(date, time)
                })),
                _ => ()
            }
        }
    }
}

impl<R> Decode<R, Iter<R>> for Weechat3 where R: BufRead {
    fn decode(&mut self, input: R) -> Iter<R> {
        Iter {
            input: input,
            buffer: String::new()
        }
    }
}

impl<W> Encode<W> for Weechat3 where W: Write {
    fn encode(&self, mut output: W, event: &Event) -> io::Result<()> {
        fn date(t: i64) -> String {
            format!("{}", UTC.timestamp(t, 0).format(TIME_DATE_FORMAT))
        }
        match event {
            &Event::Msg { ref from, ref content, ref time } => {
                try!(writeln!(&mut output, "{}\t{}\t{}", date(*time), from, content))
            },
            &Event::Action { ref from, ref content, ref time } => {
                try!(writeln!(&mut output, "{}\t *\t{} {}", date(*time), from, content))
            },
            &Event::Join { ref nick, ref mask, ref channel, ref time } => {
                try!(writeln!(&mut output, "{}\t-->\t{} ({}) has joined {}",
                date(*time), nick, mask, channel))
            },
            &Event::Part { ref nick, ref mask, ref channel, ref time, ref reason } => {
                try!(write!(&mut output, "{}\t<--\t{} ({}) has left {}",
                date(*time), nick, mask, channel));
                if reason.len() > 0 {
                    try!(write!(&mut output, " ({})", reason));
                }
                try!(write!(&mut output, "\n"))
            },
            &Event::Quit { ref nick, ref mask, ref time, ref reason } => {
                try!(write!(&mut output, "{}\t<--\t{} ({}) has quit", date(*time), nick, mask));
                if reason.len() > 0 {
                    try!(write!(&mut output, " ({})", reason));
                }
                try!(write!(&mut output, "\n"))
            },
            &Event::Disconnect { ref time } => {
                try!(writeln!(&mut output, "{}\t--\tirc: disconnected from server", date(*time)))
            },
            &Event::Notice { ref nick, ref content, ref time } => {
                try!(writeln!(&mut output, "{}\t--\tNotice({}): {}", date(*time), nick, content))
            },
            _ => ()
        }
        Ok(())
    }
}
<commit_msg>Actually account for non-'blank' whitespace<commit_after>use std::io::{ self, BufRead, Write };
use std::borrow::ToOwned;
use std::iter::{ Iterator, AdditiveIterator };

use log::Event;
use format::{ Encode, Decode };

use l::LogLevel::Info;

use chrono::*;

pub struct Weechat3;

static TIME_DATE_FORMAT: &'static str = "%Y-%m-%d %H:%M:%S";

pub struct Iter<R> where R: BufRead {
    input: R,
    buffer: String
}

impl<R> Iterator for Iter<R> where R: BufRead {
    type Item = ::Result<Event>;
    fn next(&mut self) -> Option<::Result<Event>> {
        fn timestamp(date: &str, time: &str) -> i64 {
            UTC.datetime_from_str(&format!("{} {}", date, time), TIME_DATE_FORMAT).unwrap().timestamp()
        }
        fn join(s: &[&str], splits: &[char]) -> String {
            let len = s.iter().map(|s| s.len()).sum();
            let mut out = s.iter().zip(splits.iter()).fold(String::with_capacity(len),
               |mut s, (b, &split)| { s.push_str(b); s.push(split); s });
            out.pop(); out
        }
        fn mask(s: &str) -> String {
            s.trim_left_matches('(').trim_right_matches(')').to_owned()
        }

        loop {
            self.buffer.clear();
            match self.input.read_line(&mut self.buffer) {
                Ok(0) | Err(_) => return None,
                Ok(_) => ()
            }

            let mut split_tokens: Vec<char> = Vec::new();
            let tokens = self.buffer.split( |c: char| {
                if c.is_whitespace() { split_tokens.push(c); true } else { false }
            }).collect::<Vec<_>>();
            if log_enabled!(Info) {
                info!("Original:  `{}`", self.buffer);
                info!("Parsing:   {:?}", tokens);
            }
            match tokens[..tokens.len() - 1].as_ref() {
                [date, time, "-->", nick, host, "has", "joined", channel, _..] => return Some(Ok(Event::Join {
                    nick: nick.to_owned(), channel: channel.to_owned(), mask: mask(host),
                    time: timestamp(date, time)
                })),
                [date, time, "<--", nick, host, "has", "left", channel, reason..] => return Some(Ok(Event::Part {
                    nick: nick.to_owned(), channel: channel.to_owned(), mask: mask(host),
                    reason: mask(&join(reason, &split_tokens[8..])), time: timestamp(date, time)
                })),
                [date, time, "--", notice, content..]
                    if notice.starts_with("Notice(")
                    => return Some(Ok(Event::Notice {
                    nick: notice["Notice(".len()..notice.len() - 2].to_owned(),
                    content: join(content, &split_tokens[4..]),
                    time: timestamp(date, time)
                })),
                [date, time, "--", "irc:", "disconnected", "from", "server", _..] => return Some(Ok(Event::Disconnect {
                    time: timestamp(date, time)
                })),
                [date, time, sp, "*", nick, msg..]
                    if sp.is_empty()
                    => return Some(Ok(Event::Action {
                    from: nick.to_owned(), content: join(msg, &split_tokens[5..]),
                    time: timestamp(date, time)
                })),
                [date, time, nick, msg..] => return Some(Ok(Event::Msg {
                    from: nick.to_owned(),
                    content: join(msg, &split_tokens[3..]),
                    time: timestamp(date, time)
                })),
                _ => ()
            }
        }
    }
}

impl<R> Decode<R, Iter<R>> for Weechat3 where R: BufRead {
    fn decode(&mut self, input: R) -> Iter<R> {
        Iter {
            input: input,
            buffer: String::new()
        }
    }
}

impl<W> Encode<W> for Weechat3 where W: Write {
    fn encode(&self, mut output: W, event: &Event) -> io::Result<()> {
        fn date(t: i64) -> String {
            format!("{}", UTC.timestamp(t, 0).format(TIME_DATE_FORMAT))
        }
        match event {
            &Event::Msg { ref from, ref content, ref time } => {
                try!(writeln!(&mut output, "{}\t{}\t{}", date(*time), from, content))
            },
            &Event::Action { ref from, ref content, ref time } => {
                try!(writeln!(&mut output, "{}\t *\t{} {}", date(*time), from, content))
            },
            &Event::Join { ref nick, ref mask, ref channel, ref time } => {
                try!(writeln!(&mut output, "{}\t-->\t{} ({}) has joined {}",
                date(*time), nick, mask, channel))
            },
            &Event::Part { ref nick, ref mask, ref channel, ref time, ref reason } => {
                try!(write!(&mut output, "{}\t<--\t{} ({}) has left {}",
                date(*time), nick, mask, channel));
                if reason.len() > 0 {
                    try!(write!(&mut output, " ({})", reason));
                }
                try!(write!(&mut output, "\n"))
            },
            &Event::Quit { ref nick, ref mask, ref time, ref reason } => {
                try!(write!(&mut output, "{}\t<--\t{} ({}) has quit", date(*time), nick, mask));
                if reason.len() > 0 {
                    try!(write!(&mut output, " ({})", reason));
                }
                try!(write!(&mut output, "\n"))
            },
            &Event::Disconnect { ref time } => {
                try!(writeln!(&mut output, "{}\t--\tirc: disconnected from server", date(*time)))
            },
            &Event::Notice { ref nick, ref content, ref time } => {
                try!(writeln!(&mut output, "{}\t--\tNotice({}): {}", date(*time), nick, content))
            },
            _ => ()
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before>use env::Env;
use var::Var;
use source::{Src,Next};
use logic::LogicFn;
use def::DefBlock;

use std::collections::HashMap;

/// Creates a possible path from a dot-seperated string
///
/// Returns path, and final symbol
/// Eg: 'items.bag.coins' becomes -> (Some(Vec['items','bag']), 'coins')
pub fn as_path<'a> (lookup: &'a str) -> (Option<Vec<&'a str>>, &'a str) {
    let mut lookups: Vec<&'a str> = lookup.split_terminator('.').collect();
    let item = lookups.pop().unwrap();

    let path;
    if lookups.len() > 0 { path = Some(lookups); }
    else { path = None }

    (path,item)
}

/// Primary Evaluation trait must be implemented to run Evaluator
///
///
pub trait Eval {
    /// Get method to retrieve variable from Rust side
    fn get (&self, path: Option<Vec<&str>>, lookup: &str) -> Option<Var>;

    fn as_path<'a> (&self, lookup: &'a str) -> (Option<Vec<&'a str>>, &'a str) {
        as_path(lookup)
    }

    fn get_path (&self, lookup: &str) -> Option<Var> {
        let (path,lookup) = self.as_path(lookup);
        self.get(path, lookup)
    }

    /// Returns final ref or var, and if it is a var
    fn get_last (&self, lookup: &str) -> Option<(Var, bool)>;

    /// Expects var to be written to underlying mem/store in Rust
    fn set (&mut self, path: Option<Vec<&str>>, lookup: &str, var: Var);

    fn set_path (&mut self, lookup: &str, v: Var) {
        let (path,lookup) = self.as_path(&lookup);
        self.set(path,lookup, v);
    }

    /// A custom callable function
    ///
    /// Var represents variable to mutate
    /// Fun is the function name
    /// Vars are any additional arguments
    /// Can optionally return variable back to lichen
    fn call (&mut self, var: Var, fun: &str, vars: &Vec<Var>) -> Option<Var>;
}

pub struct Evaluator<'e> {
    env: &'e mut Env,
    node_stack: Vec<String>,
}

impl<'e> Iterator for Evaluator<'e> {

        type Item = (Vec<Var>, Option<Next>); //here we only return node name as an option to advance
        fn next(&mut self) -> Option<Self::Item> {
            if let Some(nn) = self.node_stack.pop() {
                if let Some(r) = self.run(&nn) {
                    // reset node if necessary
                    if let Some(ref next) = r.1 {
                        match next {
                            &Next::Restart(ref nn) => {
                                if let &Some(ref nn) = nn {
                                    if let Some(b) = self.env.src.get_mut(nn) {
                                        b.idx = 0;
                                    }
                                }
                            }
                            _ => {}, // we handle the rest during Run, for convenience
                        }
                    }

                    Some(r)
                }
                else { self.next() }
            }
            else { None }
        }
    }

impl<'e> Evaluator<'e> {
    /// Evaluator by default starts on the node named 'root'
    pub fn new (env: &'e mut Env) -> Evaluator<'e> {
        Evaluator {
            env: env,
            node_stack: vec!["root".to_owned()],
        }
    }

    /// Consumes Evaluator for saving state
    ///
    /// You should save the Env state as well, as it's external to the Evaluator
    pub fn save (self) -> EvaluatorState {
        EvaluatorState {
            node_stack: self.node_stack,
        }
    }

    /// Manually advances Evaluator to next node
    pub fn advance (&mut self, node: String) {
        // successful advance clears out stack
        self.node_stack.clear();
        self.node_stack.push(node);
    }

    pub fn resolve (s: &str, logic: &HashMap<String,LogicFn>, def: &HashMap<String,DefBlock>) -> Option<Var> {
        if let Some(ref lfn) = logic.get(s) {
            if let Some(val_) = lfn.run(&def, &logic) {
                return Some(val_.into())
            }
        }
        else if let Some((v,res)) = def.get_last(s) {
            if res { return Some(v) }
        }

        None
    }

    /// Manually run the Evaluator, starting at node specified
    pub fn run (&mut self, node_name: &str)
                -> Option<(Vec<Var>, Option<Next>)>
    {
        if let Some(b) = self.env.src.get_mut(node_name) {
            b.visited = true;

            if let Some(src) = b.src.get(b.idx) {
                self.node_stack.push(node_name.to_owned()); //more to iterate through?
                b.idx += 1;

                match src {
                    &Src::Or(_,_) => {
                        if !b.or_valid {
                            return None
                        }
                        else { b.or_valid = false; } //reset
                    }
                    &Src::If(_,_,_) => { b.or_valid = true; }
                    // anything else resets above or-logic
                    _ => { b.or_valid = false; },
                }

                let (mut vars, next) = src.eval(&mut b.logic,
                                                &mut self.env.def,
                                                &mut self.env.fun);
                let has_return = (vars.len() > 0) || next.is_some();

                // reset when if is successful
                if has_return { b.or_valid = false; }

                for var in vars.iter_mut() {
                    let mut val = None;

                    match var {
                        &mut Var::Sym(ref mut s) => { // resolve symbol refs
                            val = Evaluator::resolve(s, &b.logic, &self.env.def);
                            // NOTE: otherwise we silently fail
                        },
                        &mut Var::String(ref mut s) => { //format string
                            let mut fs = String::new();
                            let mut sym = String::new();
                            let mut in_sym = false;

                            for c in s.chars() {
                                if (c == ' ' || c == '`') && !sym.is_empty() {
                                    if let Some(v) = Evaluator::resolve(&sym, &b.logic, &self.env.def) {
                                        fs.push_str(&v.to_string());
                                    }
                                    else {
                                        fs.push_str(&sym); //push as non-ref sym again
                                        // NOTE: we should consider failing silently (dont push)
                                    }

                                    if c == '`' { in_sym = true; }
                                    else {
                                        in_sym = false;
                                        sym.clear();
                                        fs.push(' ');
                                    }
                                }
                                else if c == '`' { in_sym = true; }
                                else {
                                    if in_sym { sym.push(c); }
                                    else { fs.push(c); }
                                }
                            }

                            if !sym.is_empty() {
                                if let Some(v) = Evaluator::resolve(&sym, &b.logic, &self.env.def) {
                                    fs.push_str(&v.to_string());
                                }
                                else {
                                    fs.push_str(&sym);
                                }
                            }

                            *s = fs;
                        },
                        _ => {}
                    }

                    if let Some(val) = val {
                        *var = val;
                    }
                }

                if let Some(ref next) = next {
                    // NOTE: await and select clear stack on advance
                    match next {
                        &Next::Now(ref nn) => { // immediate advance clears node stack
                            self.node_stack.clear();
                            self.node_stack.push(nn.clone());
                        },
                        &Next::Call(ref nn) => { // callback nodes add to stack
                            self.node_stack.push(nn.clone());
                        },
                        &Next::Back => { self.node_stack.pop(); },
                        &Next::Restart(ref nn) => {
                            if nn.is_none() { b.idx = 0; }
                            // NOTE: see iterator for other side of this
                        },
                        &Next::Clear => {
                            self.node_stack.clear();
                            self.node_stack.push(b.name.to_owned());
                        },
                        &Next::Exit => { self.node_stack.clear(); },
                        _ => {},
                    }
                }

                if has_return {
                    return Some((vars,next))
                }
                else {
                    return None
                }
            }
            else { b.idx = 0; } //reset
        }

        None
    }
}

#[derive(Clone,Debug)]
pub struct EvaluatorState {
    node_stack: Vec<String>,
}

impl EvaluatorState {
    pub fn to_eval<'e> (self, env: &'e mut Env) -> Evaluator<'e> {
        Evaluator {
            env: env,
            node_stack: self.node_stack,
        }
    }

    pub fn as_eval<'e> (&self, env: &'e mut Env) -> Evaluator<'e> {
        self.clone().to_eval(env)
    }
}
<commit_msg>fix idx bug<commit_after>use env::Env;
use var::Var;
use source::{Src,Next};
use logic::LogicFn;
use def::DefBlock;

use std::collections::HashMap;

/// Creates a possible path from a dot-seperated string
///
/// Returns path, and final symbol
/// Eg: 'items.bag.coins' becomes -> (Some(Vec['items','bag']), 'coins')
pub fn as_path<'a> (lookup: &'a str) -> (Option<Vec<&'a str>>, &'a str) {
    let mut lookups: Vec<&'a str> = lookup.split_terminator('.').collect();
    let item = lookups.pop().unwrap();

    let path;
    if lookups.len() > 0 { path = Some(lookups); }
    else { path = None }

    (path,item)
}

/// Primary Evaluation trait must be implemented to run Evaluator
///
///
pub trait Eval {
    /// Get method to retrieve variable from Rust side
    fn get (&self, path: Option<Vec<&str>>, lookup: &str) -> Option<Var>;

    fn as_path<'a> (&self, lookup: &'a str) -> (Option<Vec<&'a str>>, &'a str) {
        as_path(lookup)
    }

    fn get_path (&self, lookup: &str) -> Option<Var> {
        let (path,lookup) = self.as_path(lookup);
        self.get(path, lookup)
    }

    /// Returns final ref or var, and if it is a var
    fn get_last (&self, lookup: &str) -> Option<(Var, bool)>;

    /// Expects var to be written to underlying mem/store in Rust
    fn set (&mut self, path: Option<Vec<&str>>, lookup: &str, var: Var);

    fn set_path (&mut self, lookup: &str, v: Var) {
        let (path,lookup) = self.as_path(&lookup);
        self.set(path,lookup, v);
    }

    /// A custom callable function
    ///
    /// Var represents variable to mutate
    /// Fun is the function name
    /// Vars are any additional arguments
    /// Can optionally return variable back to lichen
    fn call (&mut self, var: Var, fun: &str, vars: &Vec<Var>) -> Option<Var>;
}

pub struct Evaluator<'e> {
    env: &'e mut Env,
    node_stack: Vec<String>,
}

impl<'e> Iterator for Evaluator<'e> {

        type Item = (Vec<Var>, Option<Next>); //here we only return node name as an option to advance
        fn next(&mut self) -> Option<Self::Item> {
            if let Some(nn) = self.node_stack.pop() {
                if let Some(r) = self.run(&nn) {
                    // reset node if necessary
                    if let Some(ref next) = r.1 {
                        match next {
                            &Next::Restart(ref nn) => {
                                if let &Some(ref nn) = nn {
                                    if let Some(b) = self.env.src.get_mut(nn) {
                                        b.idx = 0;
                                    }
                                }
                            }
                            _ => {}, // we handle the rest during Run, for convenience
                        }
                    }

                    Some(r)
                }
                else { self.next() }
            }
            else { None }
        }
    }

impl<'e> Evaluator<'e> {
    /// Evaluator by default starts on the node named 'root'
    pub fn new (env: &'e mut Env) -> Evaluator<'e> {
        Evaluator {
            env: env,
            node_stack: vec!["root".to_owned()],
        }
    }

    /// Consumes Evaluator for saving state
    ///
    /// You should save the Env state as well, as it's external to the Evaluator
    pub fn save (self) -> EvaluatorState {
        EvaluatorState {
            node_stack: self.node_stack,
        }
    }

    /// Manually advances Evaluator to next node
    pub fn advance (&mut self, node: String) {
        // successful advance clears out stack
        self.node_stack.clear();
        self.node_stack.push(node);
    }

    pub fn resolve (s: &str, logic: &HashMap<String,LogicFn>, def: &HashMap<String,DefBlock>) -> Option<Var> {
        if let Some(ref lfn) = logic.get(s) {
            if let Some(val_) = lfn.run(&def, &logic) {
                return Some(val_.into())
            }
        }
        else if let Some((v,res)) = def.get_last(s) {
            if res { return Some(v) }
        }

        None
    }

    /// Manually run the Evaluator, starting at node specified
    pub fn run (&mut self, node_name: &str)
                -> Option<(Vec<Var>, Option<Next>)>
    {
        if let Some(b) = self.env.src.get_mut(node_name) {
            b.visited = true;

            if let Some(src) = b.src.get(b.idx) {
                self.node_stack.push(node_name.to_owned()); //more to iterate through?
                b.idx += 1;

                match src {
                    &Src::Or(_,_) => {
                        if !b.or_valid {
                            return None
                        }
                        else { b.or_valid = false; } //reset
                    }
                    &Src::If(_,_,_) => { b.or_valid = true; }
                    // anything else resets above or-logic
                    _ => { b.or_valid = false; },
                }

                let (mut vars, next) = src.eval(&mut b.logic,
                                                &mut self.env.def,
                                                &mut self.env.fun);
                let has_return = (vars.len() > 0) || next.is_some();

                // reset when if is successful
                if has_return { b.or_valid = false; }

                for var in vars.iter_mut() {
                    let mut val = None;

                    match var {
                        &mut Var::Sym(ref mut s) => { // resolve symbol refs
                            val = Evaluator::resolve(s, &b.logic, &self.env.def);
                            // NOTE: otherwise we silently fail
                        },
                        &mut Var::String(ref mut s) => { //format string
                            let mut fs = String::new();
                            let mut sym = String::new();
                            let mut in_sym = false;

                            for c in s.chars() {
                                if (c == ' ' || c == '`') && !sym.is_empty() {
                                    if let Some(v) = Evaluator::resolve(&sym, &b.logic, &self.env.def) {
                                        fs.push_str(&v.to_string());
                                    }
                                    else {
                                        fs.push_str(&sym); //push as non-ref sym again
                                        // NOTE: we should consider failing silently (dont push)
                                    }

                                    if c == '`' { in_sym = true; }
                                    else {
                                        in_sym = false;
                                        sym.clear();
                                        fs.push(' ');
                                    }
                                }
                                else if c == '`' { in_sym = true; }
                                else {
                                    if in_sym { sym.push(c); }
                                    else { fs.push(c); }
                                }
                            }

                            if !sym.is_empty() {
                                if let Some(v) = Evaluator::resolve(&sym, &b.logic, &self.env.def) {
                                    fs.push_str(&v.to_string());
                                }
                                else {
                                    fs.push_str(&sym);
                                }
                            }

                            *s = fs;
                        },
                        _ => {}
                    }

                    if let Some(val) = val {
                        *var = val;
                    }
                }

                if let Some(ref next) = next {
                    let idx = b.idx; // save for 'back'
                    b.idx = 0; // clear our place in the source evaluation
                    // NOTE: await and select clear stack on advance
                    match next {
                        &Next::Now(ref nn) => { // immediate advance clears node stack
                            self.node_stack.clear();
                            self.node_stack.push(nn.clone());
                        },
                        &Next::Call(ref nn) => { // callback nodes add to stack
                            self.node_stack.push(nn.clone());
                        },
                        &Next::Back => {
                            b.idx = idx; // reset so we can pickup afterwards
                            self.node_stack.pop();
                        },
                        &Next::Restart(ref nn) => {
                            if nn.is_none() { b.idx = 0; }
                            // NOTE: see iterator for other side of this
                        },
                        &Next::Clear => {
                            self.node_stack.clear();
                            self.node_stack.push(b.name.to_owned());
                        },
                        &Next::Exit => { self.node_stack.clear(); },
                        _ => {},
                    }
                }

                if has_return {
                    return Some((vars,next))
                }
                else {
                    return None
                }
            }
            else { b.idx = 0; } //reset
        }

        None
    }
}

#[derive(Clone,Debug)]
pub struct EvaluatorState {
    node_stack: Vec<String>,
}

impl EvaluatorState {
    pub fn to_eval<'e> (self, env: &'e mut Env) -> Evaluator<'e> {
        Evaluator {
            env: env,
            node_stack: self.node_stack,
        }
    }

    pub fn as_eval<'e> (&self, env: &'e mut Env) -> Evaluator<'e> {
        self.clone().to_eval(env)
    }
}
<|endoftext|>


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

use visitor::FmtVisitor;
use utils::*;
use lists::{write_list, ListFormatting, SeparatorTactic, ListTactic};

use syntax::{ast, ptr};
use syntax::codemap::{Span, Pos};

use MIN_STRING;

impl<'a> FmtVisitor<'a> {
    // TODO NEEDS TESTS
    fn rewrite_string_lit(&mut self, s: &str, span: Span, width: usize, offset: usize) -> String {
        // FIXME I bet this stomps unicode escapes in the source string

        // Check if there is anything to fix: we always try to fixup multi-line
        // strings, or if the string is too long for the line.
        let l_loc = self.codemap.lookup_char_pos(span.lo);
        let r_loc = self.codemap.lookup_char_pos(span.hi);
        if l_loc.line == r_loc.line && r_loc.col.to_usize() <= config!(max_width) {
            return self.snippet(span);
        }

        // TODO if lo.col > IDEAL - 10, start a new line (need cur indent for that)

        let s = s.escape_default();

        let offset = offset + 1;
        let indent = make_indent(offset);
        let indent = &indent;

        let max_chars = width - 1;

        let mut cur_start = 0;
        let mut result = String::new();
        result.push('"');
        loop {
            let mut cur_end = cur_start + max_chars;

            if cur_end >= s.len() {
                result.push_str(&s[cur_start..]);
                break;
            }

            // Make sure we're on a char boundary.
            cur_end = next_char(&s, cur_end);

            // Push cur_end left until we reach whitespace
            while !s.char_at(cur_end-1).is_whitespace() {
                cur_end = prev_char(&s, cur_end);

                if cur_end - cur_start < MIN_STRING {
                    // We can't break at whitespace, fall back to splitting
                    // anywhere that doesn't break an escape sequence
                    cur_end = next_char(&s, cur_start + max_chars);
                    while s.char_at(cur_end) == '\\' {
                        cur_end = prev_char(&s, cur_end);
                    }
                }
            }
            // Make sure there is no whitespace to the right of the break.
            while cur_end < s.len() && s.char_at(cur_end).is_whitespace() {
                cur_end = next_char(&s, cur_end+1);
            }
            result.push_str(&s[cur_start..cur_end]);
            result.push_str("\\\n");
            result.push_str(indent);

            cur_start = cur_end;
        }
        result.push('"');

        result
    }

    fn rewrite_call(&mut self,
                    callee: &ast::Expr,
                    args: &[ptr::P<ast::Expr>],
                    width: usize,
                    offset: usize)
        -> String
    {
        debug!("rewrite_call, width: {}, offset: {}", width, offset);

        // TODO using byte lens instead of char lens (and probably all over the place too)
        let callee_str = self.rewrite_expr(callee, width, offset);
        debug!("rewrite_call, callee_str: `{}`", callee_str);
        // 2 is for parens.
        let remaining_width = width - callee_str.len() - 2;
        let offset = callee_str.len() + 1 + offset;
        let arg_count = args.len();

        let args_str = if arg_count > 0 {
            let args: Vec<_> = args.iter().map(|e| (self.rewrite_expr(e,
                                                                      remaining_width,
                                                                      offset), String::new())).collect();
            // TODO move this into write_list
            let tactics = if args.iter().any(|&(ref s, _)| s.contains('\n')) {
                ListTactic::Vertical
            } else {
                ListTactic::HorizontalVertical
            };
            let fmt = ListFormatting {
                tactic: tactics,
                separator: ",",
                trailing_separator: SeparatorTactic::Never,
                indent: offset,
                h_width: remaining_width,
                v_width: remaining_width,
            };
            write_list(&args, &fmt)
        } else {
            String::new()
        };

        format!("{}({})", callee_str, args_str)
    }

    fn rewrite_paren(&mut self, subexpr: &ast::Expr, width: usize, offset: usize) -> String {
        debug!("rewrite_paren, width: {}, offset: {}", width, offset);
        // 1 is for opening paren
        let subexpr_str = self.rewrite_expr(subexpr, width-1, offset+1);
        debug!("rewrite_paren, subexpr_str: `{}`", subexpr_str);
        let mut lines = subexpr_str.rsplit('\n');
        let last_line_len = lines.next().unwrap().len();
        let last_line_offset = if lines.next().is_none() {offset+1} else {0};
        if width + offset - last_line_offset - last_line_len > 0 {
            format!("({})", subexpr_str)
        } else {
            // FIXME That's correct unless we have width < 2. Return an Optrion for such cases ?
            format!("({}\n{} )", subexpr_str, make_indent(offset))
        }
    }

    pub fn rewrite_expr(&mut self, expr: &ast::Expr, width: usize, offset: usize) -> String {
        match expr.node {
            ast::Expr_::ExprLit(ref l) => {
                match l.node {
                    ast::Lit_::LitStr(ref is, _) => {
                        let result = self.rewrite_string_lit(&is, l.span, width, offset);
                        debug!("string lit: `{}`", result);
                        return result;
                    }
                    _ => {}
                }
            }
            ast::Expr_::ExprCall(ref callee, ref args) => {
                return self.rewrite_call(callee, args, width, offset);
            }
            ast::Expr_::ExprParen(ref subexpr) => {
                return self.rewrite_paren(subexpr, width, offset);
            }
            _ => {}
        }

        let result = self.snippet(expr.span);
        result
    }
}
<commit_msg>syle correction<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use visitor::FmtVisitor;
use utils::*;
use lists::{write_list, ListFormatting, SeparatorTactic, ListTactic};

use syntax::{ast, ptr};
use syntax::codemap::{Span, Pos};

use MIN_STRING;

impl<'a> FmtVisitor<'a> {
    // TODO NEEDS TESTS
    fn rewrite_string_lit(&mut self, s: &str, span: Span, width: usize, offset: usize) -> String {
        // FIXME I bet this stomps unicode escapes in the source string

        // Check if there is anything to fix: we always try to fixup multi-line
        // strings, or if the string is too long for the line.
        let l_loc = self.codemap.lookup_char_pos(span.lo);
        let r_loc = self.codemap.lookup_char_pos(span.hi);
        if l_loc.line == r_loc.line && r_loc.col.to_usize() <= config!(max_width) {
            return self.snippet(span);
        }

        // TODO if lo.col > IDEAL - 10, start a new line (need cur indent for that)

        let s = s.escape_default();

        let offset = offset + 1;
        let indent = make_indent(offset);
        let indent = &indent;

        let max_chars = width - 1;

        let mut cur_start = 0;
        let mut result = String::new();
        result.push('"');
        loop {
            let mut cur_end = cur_start + max_chars;

            if cur_end >= s.len() {
                result.push_str(&s[cur_start..]);
                break;
            }

            // Make sure we're on a char boundary.
            cur_end = next_char(&s, cur_end);

            // Push cur_end left until we reach whitespace
            while !s.char_at(cur_end-1).is_whitespace() {
                cur_end = prev_char(&s, cur_end);

                if cur_end - cur_start < MIN_STRING {
                    // We can't break at whitespace, fall back to splitting
                    // anywhere that doesn't break an escape sequence
                    cur_end = next_char(&s, cur_start + max_chars);
                    while s.char_at(cur_end) == '\\' {
                        cur_end = prev_char(&s, cur_end);
                    }
                }
            }
            // Make sure there is no whitespace to the right of the break.
            while cur_end < s.len() && s.char_at(cur_end).is_whitespace() {
                cur_end = next_char(&s, cur_end+1);
            }
            result.push_str(&s[cur_start..cur_end]);
            result.push_str("\\\n");
            result.push_str(indent);

            cur_start = cur_end;
        }
        result.push('"');

        result
    }

    fn rewrite_call(&mut self,
                    callee: &ast::Expr,
                    args: &[ptr::P<ast::Expr>],
                    width: usize,
                    offset: usize)
        -> String
    {
        debug!("rewrite_call, width: {}, offset: {}", width, offset);

        // TODO using byte lens instead of char lens (and probably all over the place too)
        let callee_str = self.rewrite_expr(callee, width, offset);
        debug!("rewrite_call, callee_str: `{}`", callee_str);
        // 2 is for parens.
        let remaining_width = width - callee_str.len() - 2;
        let offset = callee_str.len() + 1 + offset;
        let arg_count = args.len();

        let args_str = if arg_count > 0 {
            let args: Vec<_> = args.iter().map(|e| (self.rewrite_expr(e,
                                                                      remaining_width,
                                                                      offset), String::new())).collect();
            // TODO move this into write_list
            let tactics = if args.iter().any(|&(ref s, _)| s.contains('\n')) {
                ListTactic::Vertical
            } else {
                ListTactic::HorizontalVertical
            };
            let fmt = ListFormatting {
                tactic: tactics,
                separator: ",",
                trailing_separator: SeparatorTactic::Never,
                indent: offset,
                h_width: remaining_width,
                v_width: remaining_width,
            };
            write_list(&args, &fmt)
        } else {
            String::new()
        };

        format!("{}({})", callee_str, args_str)
    }

    fn rewrite_paren(&mut self, subexpr: &ast::Expr, width: usize, offset: usize) -> String {
        debug!("rewrite_paren, width: {}, offset: {}", width, offset);
        // 1 is for opening paren
        let subexpr_str = self.rewrite_expr(subexpr, width-1, offset+1);
        debug!("rewrite_paren, subexpr_str: `{}`", subexpr_str);
        let mut lines = subexpr_str.rsplit('\n');
        let last_line_len = lines.next().unwrap().len();
        let last_line_offset = match lines.next() {
            None => offset+1,
            Some(_) => 0,
        };
        if width + offset - last_line_offset - last_line_len > 0 {
            format!("({})", subexpr_str)
        } else {
            // FIXME That's correct unless we have width < 2. Return an Option for such cases ?
            format!("({}\n{} )", subexpr_str, make_indent(offset))
        }
    }

    pub fn rewrite_expr(&mut self, expr: &ast::Expr, width: usize, offset: usize) -> String {
        match expr.node {
            ast::Expr_::ExprLit(ref l) => {
                match l.node {
                    ast::Lit_::LitStr(ref is, _) => {
                        let result = self.rewrite_string_lit(&is, l.span, width, offset);
                        debug!("string lit: `{}`", result);
                        return result;
                    }
                    _ => {}
                }
            }
            ast::Expr_::ExprCall(ref callee, ref args) => {
                return self.rewrite_call(callee, args, width, offset);
            }
            ast::Expr_::ExprParen(ref subexpr) => {
                return self.rewrite_paren(subexpr, width, offset);
            }
            _ => {}
        }

        let result = self.snippet(expr.span);
        result
    }
}
<|endoftext|>


<commit_before>use std::sync::mpsc;
use std::thread;
use std::path::PathBuf;
use std::fs::{File, OpenOptions};
use std::io::{Seek, SeekFrom, Write, Read};

pub struct Writer {
    thread: thread::JoinHandle<()>,
    tx: mpsc::SyncSender<WriteMsg>,
}

pub struct Reader {
    pub rx: mpsc::Receiver<ReadMsg>,
}

pub enum WriteMsg {
    Seek(SeekFrom),
    Write(Vec<u8>),
    Finish,
}

pub enum ReadMsg {
    Read(Vec<u8>),
    Finish,
    Error,
}

impl Writer {
    pub fn new(filename: PathBuf) -> Writer {
        let (tx, rx) = mpsc::sync_channel(1024*10);
        let builder = thread::Builder::new().name("file_writer".into());
        let t = builder.spawn(move || {
            let mut f = OpenOptions::new()
                .write(true)
                .create(true)
                .truncate(false)
                .open(filename)
                .unwrap();

            loop {
                match rx.recv().unwrap() {
                    WriteMsg::Seek(from) => {
                        f.seek(from).unwrap();
                    }
                    WriteMsg::Write(buf) => {
                        f.write_all(&buf[..]).unwrap();
                    }
                    WriteMsg::Finish => {
                        break;
                    }
                }
            }
        }).unwrap();
        Writer {
            thread: t,
            tx: tx,
        }
    }

    pub fn seek(&self, from: SeekFrom) {
        self.tx.send(WriteMsg::Seek(from)).unwrap();
    }

    pub fn write_all(&self, buf: Vec<u8>) {
        match self.tx.try_send(WriteMsg::Write(buf)) {
            Err(mpsc::TrySendError::Full(d)) => {
                warn!("file write buffer full.");
                self.tx.send(d).unwrap();
            }
            Err(_) => {
                panic!("disconnect");
            }
            Ok(_) => {}
        }
    }

    pub fn close(self) {
        self.tx.send(WriteMsg::Finish).unwrap_or_else(|e| error!("{:?}", e));
        if let Err(e) = self.thread.join() {
            error!("\n\n{:?}\n\n", e);
        }
    }
}

impl Reader {
    pub fn new(filename: String) -> Reader {
        let (tx, rx) = mpsc::sync_channel(1024*10);
        let builder = thread::Builder::new().name("file_reader".into());
        let _ = builder.spawn(move || {
            let mut f = File::open(filename).unwrap();
            let mut payload = vec![0; 1350];
            f.seek(SeekFrom::Start(0)).unwrap();
            loop {
                match f.read(&mut payload) {
                    Ok(0) => {
                        tx.send(ReadMsg::Finish).unwrap();
                        break;
                    }
                    Ok(read) => {
                        let mut owned = Vec::with_capacity(read);
                        owned.extend_from_slice(&payload[..read]);
                        match tx.try_send(ReadMsg::Read(owned)) {
                            Err(mpsc::TrySendError::Full(d)) => {
                                warn!("file read buffer full.");
                                tx.send(d).unwrap();
                            }
                            Err(_) => {
                                panic!("disconnect");
                            }
                            Ok(_) => {}
                        }
                    }
                    Err(_) => {
                        tx.send(ReadMsg::Error).unwrap();
                        panic!("ruh roh");
                    }
                }
            }
        });

        Reader {
            rx: rx,
        }
    }
}

<commit_msg>a little less crazy on the buffer fill warnings<commit_after>use std::sync::mpsc;
use std::thread;
use std::path::PathBuf;
use std::fs::{File, OpenOptions};
use std::io::{Seek, SeekFrom, Write, Read};
use std::time::{Duration, Instant};

pub struct Writer {
    thread: thread::JoinHandle<()>,
    tx: mpsc::SyncSender<WriteMsg>,
}

pub struct Reader {
    pub rx: mpsc::Receiver<ReadMsg>,
}

pub enum WriteMsg {
    Seek(SeekFrom),
    Write(Vec<u8>),
    Finish,
}

pub enum ReadMsg {
    Read(Vec<u8>),
    Finish,
    Error,
}

impl Writer {
    pub fn new(filename: PathBuf) -> Writer {
        let (tx, rx) = mpsc::sync_channel(1024*10);
        let builder = thread::Builder::new().name("file_writer".into());
        let t = builder.spawn(move || {
            let mut f = OpenOptions::new()
                .write(true)
                .create(true)
                .truncate(false)
                .open(filename)
                .unwrap();

            loop {
                match rx.recv().unwrap() {
                    WriteMsg::Seek(from) => {
                        f.seek(from).unwrap();
                    }
                    WriteMsg::Write(buf) => {
                        f.write_all(&buf[..]).unwrap();
                    }
                    WriteMsg::Finish => {
                        break;
                    }
                }
            }
        }).unwrap();
        Writer {
            thread: t,
            tx: tx,
        }
    }

    pub fn seek(&self, from: SeekFrom) {
        self.tx.send(WriteMsg::Seek(from)).unwrap();
    }

    pub fn write_all(&self, buf: Vec<u8>) {
        match self.tx.try_send(WriteMsg::Write(buf)) {
            Err(mpsc::TrySendError::Full(d)) => {
                warn!("file write buffer full.");
                self.tx.send(d).unwrap();
            }
            Err(_) => {
                panic!("disconnect");
            }
            Ok(_) => {}
        }
    }

    pub fn close(self) {
        self.tx.send(WriteMsg::Finish).unwrap_or_else(|e| error!("{:?}", e));
        if let Err(e) = self.thread.join() {
            error!("\n\n{:?}\n\n", e);
        }
    }
}

impl Reader {
    pub fn new(filename: String) -> Reader {
        let (tx, rx) = mpsc::sync_channel(1024*10);
        let builder = thread::Builder::new().name("file_reader".into());
        let _ = builder.spawn(move || {
            let mut f = File::open(filename).unwrap();
            let mut payload = vec![0; 1350];
            let mut last_buffer_warn = Instant::now();
            f.seek(SeekFrom::Start(0)).unwrap();
            loop {
                match f.read(&mut payload) {
                    Ok(0) => {
                        tx.send(ReadMsg::Finish).unwrap();
                        break;
                    }
                    Ok(read) => {
                        let mut owned = Vec::with_capacity(read);
                        owned.extend_from_slice(&payload[..read]);
                        match tx.try_send(ReadMsg::Read(owned)) {
                            Err(mpsc::TrySendError::Full(d)) => {
                                if last_buffer_warn.elapsed() > Duration::from_secs(3) {
                                    warn!("file read buffer full.");
                                    last_buffer_warn = Instant::now();
                                }
                                tx.send(d).unwrap();
                            }
                            Err(_) => {
                                panic!("disconnect");
                            }
                            Ok(_) => {}
                        }
                    }
                    Err(_) => {
                        tx.send(ReadMsg::Error).unwrap();
                        panic!("ruh roh");
                    }
                }
            }
        });

        Reader {
            rx: rx,
        }
    }
}

<|endoftext|>


<commit_before>
use std::ops::{Not};

#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub enum PlayerColor {
    White,
    Black
}

// This implementation allows PlayerColor::White == !PlayerColor::Black.
impl Not for PlayerColor {
    type Output = PlayerColor;

    fn not(self) -> PlayerColor {
        match self {
            PlayerColor::White => PlayerColor::Black,
            PlayerColor::Black => PlayerColor::White
        }
    }
}

#[allow(dead_code)] // Dead code is allowed, because I want x, y, z even if I don't use it.
#[derive(Debug)]
pub struct Point {
    pub x : i8,
    pub y : i8,
    pub z : i8,
    pub flat_coordinate : i8,
    pub lines : Vec<i8> // This vector stores the IDs of all lines through it.
}

impl Point {
    // constructor, by convention
    pub fn new(x:i8, y:i8, z:i8) -> Point {
        Point {x:x, y:y, z:z, flat_coordinate:flatten(x, y, z), lines:Vec::new()} //, lines : Vec::new()}
    }
}

pub fn flatten(x:i8, y:i8, z:i8) -> i8 {
    return x + 4*y + 16*z
}

#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub enum Action {
    Play {x : i8, y : i8},
    Surrender
}

impl Action {
    pub fn new(x : i8, y : i8) -> Action {
        Action::Play {x:x, y:y}
    }
}

pub struct Line {
    points : [Point; 4]
}

impl Line {
    // constructor, by convention
    pub fn new(x:i8, y:i8, z:i8, dx:i8, dy:i8, dz:i8) -> Line {
        let point1 = Point::new(x, y, z);
        let point2 = Point::new(x+1*dx, y+1*dy, z+1*dz);
        let point3 = Point::new(x+2*dx, y+2*dy, z+2*dz);
        let point4 = Point::new(x+3*dx, y+3*dy, z+3*dz);
        Line { points : [point1, point2, point3, point4]}
    }
}

#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub enum PointState {
    Piece(PlayerColor),
    Empty
}

#[derive(PartialEq, Eq, Debug, Clone, Copy)]
pub enum LineState {
    Empty,
    Pure { color: PlayerColor, count: i8 },
    Mixed,
    Win(PlayerColor),
}

impl LineState {
    fn add_ball_functional(line_state : LineState, new_color : PlayerColor) -> LineState {
        match line_state {
            LineState::Empty => LineState::Pure { color : new_color, count : 1},
            LineState::Pure { color : current_color, count : old_count} =>
                if current_color != new_color { LineState::Mixed } else {
                    if old_count == 3 {LineState::Win(current_color)}
                    else {LineState::Pure {color : current_color, count : old_count+1}}
                },
            LineState::Mixed => LineState::Mixed,
            LineState::Win(_) => panic!("A filled line can't accept any more balls.")
        }
    }
}

#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub enum VictoryState {
    Undecided,
    Win(PlayerColor),
    Draw
}

#[derive(Debug, Copy, Clone)]
pub struct VictoryStats {
    pub white : i32,
    pub black : i32,
    pub draws : i32,
}

impl VictoryStats {
    pub fn new() -> VictoryStats {
        VictoryStats { white : 0, black : 0, draws : 0}
    }
}

// TODO: Recomputing this and passing it around gets boring at some point.
// Once we abstract over several different game structures
// this should be moved to a macro and be created once at compile time.
pub struct GameStructure {
    pub points : Vec<Point>,
    pub lines  : Vec<Line>
}

impl GameStructure {
    pub fn new() -> GameStructure {
        // Initialize a vector of all Points.
        let mut point_box = Vec::new();
        for z in 0..4 {
            for y in 0..4 {
                for x in 0..4 {
                    point_box.push(Point::new(x, y, z));
                }
            }
        }
        // Check if this really produced the right amount of points.
        // Assert the property, that point_box[i].flat_coordinate = i.
        assert_eq!(point_box.len(), 64);
        let mut i = 0;
        for p in &point_box {
            assert_eq!(p.flat_coordinate, i);
            i += 1;
        }

        // Initialize a vector of all Lines.
        let mut line_box = Vec::new();
        for a in 0..4 {
            for b in 0..4 {
                line_box.push(Line::new(a, b, 0, 0, 0, 1));
                line_box.push(Line::new(0, a, b, 1, 0, 0));
                line_box.push(Line::new(b, 0, a, 0, 1, 0))
            }
            // Diagonals in two spacial directions
            line_box.push(Line::new(a, 0, 0, 0, 1, 1));
            line_box.push(Line::new(0, a, 0, 1, 0, 1));
            line_box.push(Line::new(0, 0, a, 1, 1, 0));
            line_box.push(Line::new(a, 3, 0, 0,-1, 1));
            line_box.push(Line::new(0, a, 3, 1, 0,-1));
            line_box.push(Line::new(3, 0, a,-1, 1, 0));
        }
        // Diagonals in all three directions at once
        line_box.push(Line::new(0, 0, 0, 1, 1, 1));
        line_box.push(Line::new(3, 0, 0,-1, 1, 1));
        line_box.push(Line::new(3, 3, 0,-1,-1, 1));
        line_box.push(Line::new(0, 3, 0, 1,-1, 1));

        // Verify if the line_box has the right length.
        // TODO: This shoud be a test, not part of the normal code.
        assert_eq!(line_box.len(), 76);

        // Refenence the line ID in the points.
        let mut i = 0;
        for line in &line_box {
            for point in line.points.iter() {
                point_box[point.flat_coordinate as usize].lines.push(i);
            }
            i += 1;
        }
        GameStructure { points : point_box, lines : line_box }
    }
}

// fn expand(index:i8) -> (i8, i8, i8) {
//     return (index % 4, (index / 4) % 4, index / 16)
// }

//#[derive(Clone)]
pub struct GameState {
    pub points : [PointState; 64], // Maybe we want to change this into a Vector.
    pub lines  : [LineState; 76],
    pub current_color : PlayerColor,
    pub victory_state : VictoryState,
    pub age : i8, // how many balls were played?
    pub legal_actions : Vec<Action>,
}

impl Clone for GameState {
    fn clone(&self) -> GameState {
        let mut point_clone = [PointState::Empty; 64];
        let mut line_clone  = [LineState::Empty; 76];
        for i in 0..64 {
            point_clone[i] = self.points[i].clone();
        }
        for i in 0..76 {
            line_clone[i] = self.lines[i].clone();
        }
        GameState {
            points : point_clone,
            lines  : line_clone,
            current_color : self.current_color.clone(),
            victory_state : self.victory_state.clone(),
            age : self.age.clone(),
            legal_actions : self.legal_actions.clone(),
        }
    }
}

impl GameState {
    pub fn new() -> GameState {
        // The board is empty and all actions are legal
        let mut legal = Vec::new();
        for x in 0..4 {
            for y in 0..4 {
                legal.push(Action::new(x,y));
            }
        }
        GameState {
            points : [PointState::Empty; 64],
            lines : [LineState::Empty; 76],
            current_color : PlayerColor::White,
            victory_state : VictoryState::Undecided,
            age : 0,
            legal_actions : legal,
        }
    }

    pub fn execute_action(&mut self, structure : &GameStructure, play : &Action) {
        match play {
            &Action::Surrender   => self.victory_state = VictoryState::Win(!self.current_color),
            &Action::Play {x, y} => self.play_at(structure, x, y),
        }
    }
    pub fn execute_action_functional(&self, structure : &GameStructure, play : &Action) -> GameState {
        let mut result = self.clone();
        result.execute_action(structure, &play);
        return result;
    }

    // Idea: This could be cached inside each gamestate.
    fn z_value(&self, x : i8, y : i8) -> Option<i8> {
        for z in 0..4 {
            if self.points[flatten(x, y, z) as usize] == PointState::Empty {
                return Some(z)
            }
        }
        return None
    }

    fn play_at(&mut self, structure : &GameStructure, x:i8, y:i8) {
        let z = self.z_value(x, y);
        let flat_coordinate = match z {
            Some(z) => flatten(x, y, z),
            None => panic!("Added a ball at ({}, {}), which is already full.", x, y)
        };
        // Place a colored piece at the coordinate
        self.points[flat_coordinate as usize] = PointState::Piece(self.current_color);
        // Update the legal actions, if the z-coordinate is 3
        // I make use of the fact that the z coordinate is occupying the top two bits.
        if flat_coordinate >= 4*4*3 {
            // As the legal actions will be mixed up during play, we need to search through all.
            for i in 0..self.legal_actions.len() {
                // TODO: Don't construct a new Action object just to compare.
                if self.legal_actions[i] == Action::new(x, y) {
                    println!("removed something.");
                    self.legal_actions.swap_remove(i);
                    // Removes an element from anywhere in the vector and return it, replacing it with the last element.
                    // This does not preserve ordering, but is O(1).
                    break;
                }
            }
        }
        for line in structure.points[flat_coordinate as usize].lines.clone() {
            let line_state = LineState::add_ball_functional(self.lines[line as usize], self.current_color);
            match line_state {
                LineState::Win(color) => self.victory_state = VictoryState::Win(color),
                _ => (),
            }
            self.lines[line as usize] = line_state;
        }
        self.age += 1;
        if self.age == 64 && self.victory_state == VictoryState::Undecided {
            self.victory_state = VictoryState::Draw;
        }
        self.current_color = !self.current_color;
    }
}
<commit_msg>Move the GameStructure verification code into my first test case.<commit_after>
use std::ops::{Not};

#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub enum PlayerColor {
    White,
    Black
}

// This implementation allows PlayerColor::White == !PlayerColor::Black.
impl Not for PlayerColor {
    type Output = PlayerColor;

    fn not(self) -> PlayerColor {
        match self {
            PlayerColor::White => PlayerColor::Black,
            PlayerColor::Black => PlayerColor::White
        }
    }
}

#[allow(dead_code)] // Dead code is allowed, because I want x, y, z even if I don't use it.
#[derive(Debug)]
pub struct Point {
    pub x : i8,
    pub y : i8,
    pub z : i8,
    pub flat_coordinate : i8,
    pub lines : Vec<i8> // This vector stores the IDs of all lines through it.
}

impl Point {
    // constructor, by convention
    pub fn new(x:i8, y:i8, z:i8) -> Point {
        Point {x:x, y:y, z:z, flat_coordinate:flatten(x, y, z), lines:Vec::new()} //, lines : Vec::new()}
    }
}

pub fn flatten(x:i8, y:i8, z:i8) -> i8 {
    return x + 4*y + 16*z
}

#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub enum Action {
    Play {x : i8, y : i8},
    Surrender
}

impl Action {
    pub fn new(x : i8, y : i8) -> Action {
        Action::Play {x:x, y:y}
    }
}

pub struct Line {
    points : [Point; 4]
}

impl Line {
    // constructor, by convention
    pub fn new(x:i8, y:i8, z:i8, dx:i8, dy:i8, dz:i8) -> Line {
        let point1 = Point::new(x, y, z);
        let point2 = Point::new(x+1*dx, y+1*dy, z+1*dz);
        let point3 = Point::new(x+2*dx, y+2*dy, z+2*dz);
        let point4 = Point::new(x+3*dx, y+3*dy, z+3*dz);
        Line { points : [point1, point2, point3, point4]}
    }
}

#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub enum PointState {
    Piece(PlayerColor),
    Empty
}

#[derive(PartialEq, Eq, Debug, Clone, Copy)]
pub enum LineState {
    Empty,
    Pure { color: PlayerColor, count: i8 },
    Mixed,
    Win(PlayerColor),
}

impl LineState {
    fn add_ball_functional(line_state : LineState, new_color : PlayerColor) -> LineState {
        match line_state {
            LineState::Empty => LineState::Pure { color : new_color, count : 1},
            LineState::Pure { color : current_color, count : old_count} =>
                if current_color != new_color { LineState::Mixed } else {
                    if old_count == 3 {LineState::Win(current_color)}
                    else {LineState::Pure {color : current_color, count : old_count+1}}
                },
            LineState::Mixed => LineState::Mixed,
            LineState::Win(_) => panic!("A filled line can't accept any more balls.")
        }
    }
}

#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub enum VictoryState {
    Undecided,
    Win(PlayerColor),
    Draw
}

#[derive(Debug, Copy, Clone)]
pub struct VictoryStats {
    pub white : i32,
    pub black : i32,
    pub draws : i32,
}

impl VictoryStats {
    pub fn new() -> VictoryStats {
        VictoryStats { white : 0, black : 0, draws : 0}
    }
}

// TODO: Recomputing this and passing it around gets boring at some point.
// Once we abstract over several different game structures
// this should be moved to a macro and be created once at compile time.
pub struct GameStructure {
    pub points : Vec<Point>,
    pub lines  : Vec<Line>
}

impl GameStructure {
    pub fn new() -> GameStructure {
        // Initialize a vector of all Points.
        let mut point_box = Vec::new();
        for z in 0..4 {
            for y in 0..4 {
                for x in 0..4 {
                    point_box.push(Point::new(x, y, z));
                }
            }
        }

        // Initialize a vector of all Lines.
        let mut line_box = Vec::new();
        for a in 0..4 {
            for b in 0..4 {
                line_box.push(Line::new(a, b, 0, 0, 0, 1));
                line_box.push(Line::new(0, a, b, 1, 0, 0));
                line_box.push(Line::new(b, 0, a, 0, 1, 0))
            }
            // Diagonals in two spacial directions
            line_box.push(Line::new(a, 0, 0, 0, 1, 1));
            line_box.push(Line::new(0, a, 0, 1, 0, 1));
            line_box.push(Line::new(0, 0, a, 1, 1, 0));
            line_box.push(Line::new(a, 3, 0, 0,-1, 1));
            line_box.push(Line::new(0, a, 3, 1, 0,-1));
            line_box.push(Line::new(3, 0, a,-1, 1, 0));
        }
        // Diagonals in all three directions at once
        line_box.push(Line::new(0, 0, 0, 1, 1, 1));
        line_box.push(Line::new(3, 0, 0,-1, 1, 1));
        line_box.push(Line::new(3, 3, 0,-1,-1, 1));
        line_box.push(Line::new(0, 3, 0, 1,-1, 1));

        // Refenence the line ID in the points.
        let mut i = 0;
        for line in &line_box {
            for point in line.points.iter() {
                point_box[point.flat_coordinate as usize].lines.push(i);
            }
            i += 1;
        }
        GameStructure { points : point_box, lines : line_box }
    }
}

#[test]
fn test_game_structure_size() {
    let structure = GameStructure::new();
    assert_eq!(structure.points.len(), 4*4*4);
    let mut i = 0;
    for p in &structure.points {
        assert_eq!(p.flat_coordinate, i);
        i += 1;
    }
    assert_eq!(structure.lines.len(), 76);
}

// fn expand(index:i8) -> (i8, i8, i8) {
//     return (index % 4, (index / 4) % 4, index / 16)
// }

//#[derive(Clone)]
pub struct GameState {
    pub points : [PointState; 64], // Maybe we want to change this into a Vector.
    pub lines  : [LineState; 76],
    pub current_color : PlayerColor,
    pub victory_state : VictoryState,
    pub age : i8, // how many balls were played?
    pub legal_actions : Vec<Action>,
}

impl Clone for GameState {
    fn clone(&self) -> GameState {
        let mut point_clone = [PointState::Empty; 64];
        let mut line_clone  = [LineState::Empty; 76];
        for i in 0..64 {
            point_clone[i] = self.points[i].clone();
        }
        for i in 0..76 {
            line_clone[i] = self.lines[i].clone();
        }
        GameState {
            points : point_clone,
            lines  : line_clone,
            current_color : self.current_color.clone(),
            victory_state : self.victory_state.clone(),
            age : self.age.clone(),
            legal_actions : self.legal_actions.clone(),
        }
    }
}

impl GameState {
    pub fn new() -> GameState {
        // The board is empty and all actions are legal
        let mut legal = Vec::new();
        for x in 0..4 {
            for y in 0..4 {
                legal.push(Action::new(x,y));
            }
        }
        GameState {
            points : [PointState::Empty; 64],
            lines : [LineState::Empty; 76],
            current_color : PlayerColor::White,
            victory_state : VictoryState::Undecided,
            age : 0,
            legal_actions : legal,
        }
    }

    pub fn execute_action(&mut self, structure : &GameStructure, play : &Action) {
        match play {
            &Action::Surrender   => self.victory_state = VictoryState::Win(!self.current_color),
            &Action::Play {x, y} => self.play_at(structure, x, y),
        }
    }
    pub fn execute_action_functional(&self, structure : &GameStructure, play : &Action) -> GameState {
        let mut result = self.clone();
        result.execute_action(structure, &play);
        return result;
    }

    // Idea: This could be cached inside each gamestate.
    fn z_value(&self, x : i8, y : i8) -> Option<i8> {
        for z in 0..4 {
            if self.points[flatten(x, y, z) as usize] == PointState::Empty {
                return Some(z)
            }
        }
        return None
    }

    fn play_at(&mut self, structure : &GameStructure, x:i8, y:i8) {
        let z = self.z_value(x, y);
        let flat_coordinate = match z {
            Some(z) => flatten(x, y, z),
            None => panic!("Added a ball at ({}, {}), which is already full.", x, y)
        };
        // Place a colored piece at the coordinate
        self.points[flat_coordinate as usize] = PointState::Piece(self.current_color);
        // Update the legal actions, if the z-coordinate is 3
        // I make use of the fact that the z coordinate is occupying the top two bits.
        if flat_coordinate >= 4*4*3 {
            // As the legal actions will be mixed up during play, we need to search through all.
            for i in 0..self.legal_actions.len() {
                // TODO: Don't construct a new Action object just to compare.
                if self.legal_actions[i] == Action::new(x, y) {
                    println!("removed something.");
                    self.legal_actions.swap_remove(i);
                    // Removes an element from anywhere in the vector and return it, replacing it with the last element.
                    // This does not preserve ordering, but is O(1).
                    break;
                }
            }
        }
        for line in structure.points[flat_coordinate as usize].lines.clone() {
            let line_state = LineState::add_ball_functional(self.lines[line as usize], self.current_color);
            match line_state {
                LineState::Win(color) => self.victory_state = VictoryState::Win(color),
                _ => (),
            }
            self.lines[line as usize] = line_state;
        }
        self.age += 1;
        if self.age == 64 && self.victory_state == VictoryState::Undecided {
            self.victory_state = VictoryState::Draw;
        }
        self.current_color = !self.current_color;
    }
}
<|endoftext|>