<commit_before>#![allow(unused_mut, unused_variables)]

use truetype::GlyphID;

use {Result, Tape, Value, Walue};
use glyph_substitution::{
    AlternateSet,
    ChainClassRuleSet,
    ChainRuleSet,
    ClassRuleSet,
    LigatureSet,
    RuleSet,
    Sequence,
    Substitution,
};
use layout::{Class, Coverage};

/// A table.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Table {
    SingleSubstitution(SingleSubstitution),
    MultipleSubstitution(MultipleSubstitution),
    AlternateSubstitution(AlternateSubstitution),
    LigatureSubstitution(LigatureSubstitution),
    ContextSubstitution(ContextSubstitution),
    ChainContextSubstitution(ChainContextSubstitution),
    ExtensionSubstitution(ExtensionSubstitution),
    ReverseChainContextSubstitution(ReverseChainContextSubstitution),
}

/// A table for substituting one glyph with one glyph.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum SingleSubstitution {
    /// Format 1.
    Format1(SingleSubstitution1),
    /// Format 2.
    Format2(SingleSubstitution2),
}

table! {
    @position
    #[doc = "A table for substituting one glyph with one glyph in format 1."]
    pub SingleSubstitution1 { // SingleSubstFormat1
        format          (u16) = { 1 }, // SubstFormat
        coverage_offset (u16), // Coverage
        delta_glyph_id  (i16), // DeltaGlyphID

        coverage (Coverage) |this, tape, position| {
            jump_take!(tape, position, this.coverage_offset)
        },
    }
}

table! {
    @position
    #[doc = "A table for substituting one glyph with one glyph in format 2."]
    pub SingleSubstitution2 { // SingleSubstFormat2
        format          (u16) = { 2 }, // SubstFormat
        coverage_offset (u16), // Coverage
        glyph_count     (u16), // GlyphCount

        glyph_ids (Vec<GlyphID>) |this, tape, _| { // Substitute
            tape.take_given(this.glyph_count as usize)
        },

        coverage (Coverage) |this, tape, position| {
            jump_take!(tape, position, this.coverage_offset)
        },
    }
}

table! {
    @position
    #[doc = "A table for substituting one glyph with more than one glyph."]
    pub MultipleSubstitution { // MultipleSubstFormat1
        format           (u16) = { 1 }, // SubstFormat
        coverage_offset  (u16), // Coverage
        sequence_count   (u16), // SequenceCount

        sequence_offsets (Vec<u16>) |this, tape, _| { // Sequence
            tape.take_given(this.sequence_count as usize)
        },

        coverage (Coverage) |this, tape, position| {
            jump_take!(tape, position, this.coverage_offset)
        },

        sequences (Vec<Sequence>) |this, tape, position| {
            jump_take!(tape, position, this.sequence_count, this.sequence_offsets)
        },
    }
}

table! {
    @position
    #[doc = "A table for substituting one glyph with one of many glyphs."]
    pub AlternateSubstitution { // AlternateSubstFormat1
        format          (u16) = { 1 }, // SubstFormat
        coverage_offset (u16), // Coverage
        set_count       (u16), // AlternateSetCount

        set_offsets (Vec<u16>) |this, tape, _| { // AlternateSet
            tape.take_given(this.set_count as usize)
        },

        coverage (Coverage) |this, tape, position| {
            jump_take!(tape, position, this.coverage_offset)
        },

        sets (Vec<AlternateSet>) |this, tape, position| {
            jump_take!(tape, position, this.set_count, this.set_offsets)
        },
    }
}

table! {
    @position
    #[doc = "A table for substituting multiple glyphs with one glyph."]
    pub LigatureSubstitution { // LigatureSubstFormat1
        format          (u16) = { 1 }, // SubstFormat
        coverage_offset (u16), // Coverage
        set_count       (u16), // LigSetCount

        set_offsets (Vec<u16>) |this, tape, _| { // LigatureSet
            tape.take_given(this.set_count as usize)
        },

        coverage (Coverage) |this, tape, position| {
            jump_take!(tape, position, this.coverage_offset)
        },

        sets (Vec<LigatureSet>) |this, tape, position| {
            jump_take!(tape, position, this.set_count, this.set_offsets)
        },
    }
}

/// A table for substituting glyphs in a context.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ContextSubstitution {
    /// Format 1.
    Format1(ContextSubstitution1),
    /// Format 2.
    Format2(ContextSubstitution2),
    /// Format 3.
    Format3(ContextSubstitution3),
}

table! {
    @position
    #[doc = "A table for substituting glyphs in a context in format 1."]
    pub ContextSubstitution1 { // ContextSubstFormat1
        format          (u16) = { 1 }, // SubstFormat
        coverage_offset (u16), // Coverage
        set_count       (u16), // SubRuleSetCount

        set_offsets (Vec<u16>) |this, tape, position| { // SubRuleSet
            tape.take_given(this.set_count as usize)
        },

        coverage (Coverage) |this, tape, position| {
            jump_take!(tape, position, this.coverage_offset)
        },

        sets (Vec<RuleSet>) |this, tape, position| {
            jump_take!(tape, position, this.set_count, this.set_offsets)
        },
    }
}

table! {
    @position
    #[doc = "A table for substituting glyphs in a context in format 2."]
    pub ContextSubstitution2 { // ContextSubstFormat2
        format          (u16) = { 2 }, // SubstFormat
        coverage_offset (u16), // Coverage
        class_offset    (u16), // ClassDef
        set_count       (u16), // SubClassSetCnt

        set_offsets (Vec<u16>) |this, tape, _| { // SubClassSet
            tape.take_given(this.set_count as usize)
        },

        coverage (Coverage) |this, tape, position| {
            jump_take!(tape, position, this.coverage_offset)
        },

        sets (Vec<ClassRuleSet>) |this, tape, position| {
            jump_take!(tape, position, this.set_count, this.set_offsets)
        },
    }
}

table! {
    @position
    #[doc = "A table for substituting glyphs in a context in format 3."]
    pub ContextSubstitution3 { // ContextSubstFormat3
        format             (u16) = { 3 }, // SubstFormat
        glyph_count        (u16), // GlyphCount
        substitution_count (u16), // SubstCount

        coverage_offsets (Vec<u16>) |this, tape, _| { // Coverage
            tape.take_given(this.glyph_count as usize)
        },

        substitutions (Vec<Substitution>) |this, tape, _| { // SubstLookupRecord
            tape.take_given(this.substitution_count as usize)
        },

        coverages (Vec<Coverage>) |this, tape, position| {
            jump_take!(tape, position, this.glyph_count, this.coverage_offsets)
        },
    }
}

/// A table for substituting glyphs in a chaining context.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ChainContextSubstitution {
    /// Format 1.
    Format1(ChainContextSubstitution1),
    /// Format 2.
    Format2(ChainContextSubstitution2),
    /// Format 3.
    Format3(ChainContextSubstitution3),
}

table! {
    @position
    #[doc = "A table for substituting glyphs in a chaining context in format 1."]
    pub ChainContextSubstitution1 { // ChainContextSubstFormat1
        format          (u16) = { 1 }, // SubstFormat
        coverage_offset (u16), // Coverage
        set_count       (u16), // ChainSubRuleSetCount

        set_offsets (Vec<u16>) |this, tape, _| { // ChainSubRuleSet
            tape.take_given(this.set_count as usize)
        },

        coverage (Coverage) |this, tape, position| {
            jump_take!(tape, position, this.coverage_offset)
        },

        sets (Vec<ChainRuleSet>) |this, tape, position| {
            jump_take!(tape, position, this.set_count, this.set_offsets)
        },
    }
}

table! {
    @position
    #[doc = "A table for substituting glyphs in a chaining context in format 2."]
    pub ChainContextSubstitution2 { // ChainContextSubstFormat2
        format                (u16) = { 2 }, // SubstFormat
        coverage_offset       (u16), // Coverage
        backward_class_offset (u16), // BacktrackClassDef
        input_class_offset    (u16), // InputClassDef
        forward_class_offset  (u16), // LookaheadClassDef
        set_count             (u16), // ChainSubClassSetCnt

        set_offsets (Vec<u16>) |this, tape, _| { // ChainSubClassSet
            tape.take_given(this.set_count as usize)
        },

        coverage (Coverage) |this, tape, position| {
            jump_take!(tape, position, this.coverage_offset)
        },

        backward_class (Class) |this, tape, position| {
            jump_take!(tape, position, this.backward_class_offset)
        },

        input_class (Class) |this, tape, position| {
            jump_take!(tape, position, this.input_class_offset)
        },

        forward_class (Class) |this, tape, position| {
            jump_take!(tape, position, this.forward_class_offset)
        },

        sets (Vec<ChainClassRuleSet>) |this, tape, position| {
            jump_take!(tape, position, this.set_count, this.set_offsets)
        },
    }
}

table! {
    #[doc = "A table for substituting glyphs in a chaining context in format 3."]
    pub ChainContextSubstitution3 { // ChainContextSubstFormat3
        format (u16) = { 3 }, // SubstFormat
    }
}

table! {
    #[doc = "A table for other types of substitution."]
    pub ExtensionSubstitution { // ExtensionSubstFormat1
        format (u16) = { 1 }, // SubstFormat
        kind   (u16), // ExtensionLookupType
        offset (u32), // ExtensionOffset
    }
}

table! {
    #[doc = "A table for substituting glyphs in reverse order in a chaining context."]
    pub ReverseChainContextSubstitution { // ReverseChainSingleSubstFormat1
    }
}

impl Walue<u16> for Table {
    fn read<T: Tape>(tape: &mut T, kind: u16) -> Result<Self> {
        Ok(match kind {
            1 => Table::SingleSubstitution(try!(tape.take())),
            2 => Table::MultipleSubstitution(try!(tape.take())),
            3 => Table::AlternateSubstitution(try!(tape.take())),
            4 => Table::LigatureSubstitution(try!(tape.take())),
            5 => Table::ContextSubstitution(try!(tape.take())),
            6 => Table::ChainContextSubstitution(try!(tape.take())),
            7 => Table::ExtensionSubstitution(try!(tape.take())),
            8 => Table::ReverseChainContextSubstitution(try!(tape.take())),
            _ => raise!("found an unknown glyph-substitution type"),
        })
    }
}

impl Value for SingleSubstitution {
    fn read<T: Tape>(tape: &mut T) -> Result<Self> {
        Ok(match try!(tape.peek::<u16>()) {
            1 => SingleSubstitution::Format1(try!(tape.take())),
            2 => SingleSubstitution::Format2(try!(tape.take())),
            _ => raise!("found an unknown format of the single-substitution table"),
        })
    }
}

impl Value for ContextSubstitution {
    fn read<T: Tape>(tape: &mut T) -> Result<Self> {
        Ok(match try!(tape.peek::<u16>()) {
            1 => ContextSubstitution::Format1(try!(tape.take())),
            2 => ContextSubstitution::Format2(try!(tape.take())),
            3 => ContextSubstitution::Format3(try!(tape.take())),
            _ => raise!("found an unknown format of the context-substitution table"),
        })
    }
}

impl Value for ChainContextSubstitution {
    fn read<T: Tape>(tape: &mut T) -> Result<Self> {
        Ok(match try!(tape.peek::<u16>()) {
            1 => ChainContextSubstitution::Format1(try!(tape.take())),
            2 => ChainContextSubstitution::Format2(try!(tape.take())),
            3 => ChainContextSubstitution::Format3(try!(tape.take())),
            _ => raise!("found an unknown format of the chaining-context-substitution table"),
        })
    }
}
<commit_msg>Read the chaining-context-substitution table in format 3<commit_after>#![allow(unused_mut, unused_variables)]

use truetype::GlyphID;

use {Result, Tape, Value, Walue};
use glyph_substitution::{
    AlternateSet,
    ChainClassRuleSet,
    ChainRuleSet,
    ClassRuleSet,
    LigatureSet,
    RuleSet,
    Sequence,
    Substitution,
};
use layout::{Class, Coverage};

/// A table.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Table {
    SingleSubstitution(SingleSubstitution),
    MultipleSubstitution(MultipleSubstitution),
    AlternateSubstitution(AlternateSubstitution),
    LigatureSubstitution(LigatureSubstitution),
    ContextSubstitution(ContextSubstitution),
    ChainContextSubstitution(ChainContextSubstitution),
    ExtensionSubstitution(ExtensionSubstitution),
    ReverseChainContextSubstitution(ReverseChainContextSubstitution),
}

/// A table for substituting one glyph with one glyph.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum SingleSubstitution {
    /// Format 1.
    Format1(SingleSubstitution1),
    /// Format 2.
    Format2(SingleSubstitution2),
}

table! {
    @position
    #[doc = "A table for substituting one glyph with one glyph in format 1."]
    pub SingleSubstitution1 { // SingleSubstFormat1
        format          (u16) = { 1 }, // SubstFormat
        coverage_offset (u16), // Coverage
        delta_glyph_id  (i16), // DeltaGlyphID

        coverage (Coverage) |this, tape, position| {
            jump_take!(tape, position, this.coverage_offset)
        },
    }
}

table! {
    @position
    #[doc = "A table for substituting one glyph with one glyph in format 2."]
    pub SingleSubstitution2 { // SingleSubstFormat2
        format          (u16) = { 2 }, // SubstFormat
        coverage_offset (u16), // Coverage
        glyph_count     (u16), // GlyphCount

        glyph_ids (Vec<GlyphID>) |this, tape, _| { // Substitute
            tape.take_given(this.glyph_count as usize)
        },

        coverage (Coverage) |this, tape, position| {
            jump_take!(tape, position, this.coverage_offset)
        },
    }
}

table! {
    @position
    #[doc = "A table for substituting one glyph with more than one glyph."]
    pub MultipleSubstitution { // MultipleSubstFormat1
        format           (u16) = { 1 }, // SubstFormat
        coverage_offset  (u16), // Coverage
        sequence_count   (u16), // SequenceCount

        sequence_offsets (Vec<u16>) |this, tape, _| { // Sequence
            tape.take_given(this.sequence_count as usize)
        },

        coverage (Coverage) |this, tape, position| {
            jump_take!(tape, position, this.coverage_offset)
        },

        sequences (Vec<Sequence>) |this, tape, position| {
            jump_take!(tape, position, this.sequence_count, this.sequence_offsets)
        },
    }
}

table! {
    @position
    #[doc = "A table for substituting one glyph with one of many glyphs."]
    pub AlternateSubstitution { // AlternateSubstFormat1
        format          (u16) = { 1 }, // SubstFormat
        coverage_offset (u16), // Coverage
        set_count       (u16), // AlternateSetCount

        set_offsets (Vec<u16>) |this, tape, _| { // AlternateSet
            tape.take_given(this.set_count as usize)
        },

        coverage (Coverage) |this, tape, position| {
            jump_take!(tape, position, this.coverage_offset)
        },

        sets (Vec<AlternateSet>) |this, tape, position| {
            jump_take!(tape, position, this.set_count, this.set_offsets)
        },
    }
}

table! {
    @position
    #[doc = "A table for substituting multiple glyphs with one glyph."]
    pub LigatureSubstitution { // LigatureSubstFormat1
        format          (u16) = { 1 }, // SubstFormat
        coverage_offset (u16), // Coverage
        set_count       (u16), // LigSetCount

        set_offsets (Vec<u16>) |this, tape, _| { // LigatureSet
            tape.take_given(this.set_count as usize)
        },

        coverage (Coverage) |this, tape, position| {
            jump_take!(tape, position, this.coverage_offset)
        },

        sets (Vec<LigatureSet>) |this, tape, position| {
            jump_take!(tape, position, this.set_count, this.set_offsets)
        },
    }
}

/// A table for substituting glyphs in a context.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ContextSubstitution {
    /// Format 1.
    Format1(ContextSubstitution1),
    /// Format 2.
    Format2(ContextSubstitution2),
    /// Format 3.
    Format3(ContextSubstitution3),
}

table! {
    @position
    #[doc = "A table for substituting glyphs in a context in format 1."]
    pub ContextSubstitution1 { // ContextSubstFormat1
        format          (u16) = { 1 }, // SubstFormat
        coverage_offset (u16), // Coverage
        set_count       (u16), // SubRuleSetCount

        set_offsets (Vec<u16>) |this, tape, position| { // SubRuleSet
            tape.take_given(this.set_count as usize)
        },

        coverage (Coverage) |this, tape, position| {
            jump_take!(tape, position, this.coverage_offset)
        },

        sets (Vec<RuleSet>) |this, tape, position| {
            jump_take!(tape, position, this.set_count, this.set_offsets)
        },
    }
}

table! {
    @position
    #[doc = "A table for substituting glyphs in a context in format 2."]
    pub ContextSubstitution2 { // ContextSubstFormat2
        format          (u16) = { 2 }, // SubstFormat
        coverage_offset (u16), // Coverage
        class_offset    (u16), // ClassDef
        set_count       (u16), // SubClassSetCnt

        set_offsets (Vec<u16>) |this, tape, _| { // SubClassSet
            tape.take_given(this.set_count as usize)
        },

        coverage (Coverage) |this, tape, position| {
            jump_take!(tape, position, this.coverage_offset)
        },

        sets (Vec<ClassRuleSet>) |this, tape, position| {
            jump_take!(tape, position, this.set_count, this.set_offsets)
        },
    }
}

table! {
    @position
    #[doc = "A table for substituting glyphs in a context in format 3."]
    pub ContextSubstitution3 { // ContextSubstFormat3
        format             (u16) = { 3 }, // SubstFormat
        glyph_count        (u16), // GlyphCount
        substitution_count (u16), // SubstCount

        coverage_offsets (Vec<u16>) |this, tape, _| { // Coverage
            tape.take_given(this.glyph_count as usize)
        },

        substitutions (Vec<Substitution>) |this, tape, _| { // SubstLookupRecord
            tape.take_given(this.substitution_count as usize)
        },

        coverages (Vec<Coverage>) |this, tape, position| {
            jump_take!(tape, position, this.glyph_count, this.coverage_offsets)
        },
    }
}

/// A table for substituting glyphs in a chaining context.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ChainContextSubstitution {
    /// Format 1.
    Format1(ChainContextSubstitution1),
    /// Format 2.
    Format2(ChainContextSubstitution2),
    /// Format 3.
    Format3(ChainContextSubstitution3),
}

table! {
    @position
    #[doc = "A table for substituting glyphs in a chaining context in format 1."]
    pub ChainContextSubstitution1 { // ChainContextSubstFormat1
        format          (u16) = { 1 }, // SubstFormat
        coverage_offset (u16), // Coverage
        set_count       (u16), // ChainSubRuleSetCount

        set_offsets (Vec<u16>) |this, tape, _| { // ChainSubRuleSet
            tape.take_given(this.set_count as usize)
        },

        coverage (Coverage) |this, tape, position| {
            jump_take!(tape, position, this.coverage_offset)
        },

        sets (Vec<ChainRuleSet>) |this, tape, position| {
            jump_take!(tape, position, this.set_count, this.set_offsets)
        },
    }
}

table! {
    @position
    #[doc = "A table for substituting glyphs in a chaining context in format 2."]
    pub ChainContextSubstitution2 { // ChainContextSubstFormat2
        format                (u16) = { 2 }, // SubstFormat
        coverage_offset       (u16), // Coverage
        backward_class_offset (u16), // BacktrackClassDef
        input_class_offset    (u16), // InputClassDef
        forward_class_offset  (u16), // LookaheadClassDef
        set_count             (u16), // ChainSubClassSetCnt

        set_offsets (Vec<u16>) |this, tape, _| { // ChainSubClassSet
            tape.take_given(this.set_count as usize)
        },

        coverage (Coverage) |this, tape, position| {
            jump_take!(tape, position, this.coverage_offset)
        },

        backward_class (Class) |this, tape, position| {
            jump_take!(tape, position, this.backward_class_offset)
        },

        input_class (Class) |this, tape, position| {
            jump_take!(tape, position, this.input_class_offset)
        },

        forward_class (Class) |this, tape, position| {
            jump_take!(tape, position, this.forward_class_offset)
        },

        sets (Vec<ChainClassRuleSet>) |this, tape, position| {
            jump_take!(tape, position, this.set_count, this.set_offsets)
        },
    }
}

table! {
    @position
    #[doc = "A table for substituting glyphs in a chaining context in format 3."]
    pub ChainContextSubstitution3 { // ChainContextSubstFormat3
        format               (u16) = { 3 }, // SubstFormat
        backward_glyph_count (u16), // BacktrackGlyphCount

        backward_coverage_offsets (Vec<u16>) |this, tape, _| { // Coverage
            tape.take_given(this.backward_glyph_count as usize)
        },

        input_glyph_count (u16), // InputGlyphCount

        input_coverage_offsets (Vec<u16>) |this, tape, _| { // Coverage
            tape.take_given(this.input_glyph_count as usize)
        },

        forward_glyph_count (u16), // LookaheadGlyphCount

        forward_coverage_offsets (Vec<u16>) |this, tape, _| { // Coverage
            tape.take_given(this.forward_glyph_count as usize)
        },

        substitution_count (u16), // SubstCount

        substitutions (Vec<Substitution>) |this, tape, _| { // SubstLookupRecord
            tape.take_given(this.substitution_count as usize)
        },

        backward_coverages (Vec<Coverage>) |this, tape, position| {
            jump_take!(tape, position, this.backward_glyph_count, this.backward_coverage_offsets)
        },

        input_coverages (Vec<Coverage>) |this, tape, position| {
            jump_take!(tape, position, this.input_glyph_count, this.input_coverage_offsets)
        },

        forward_coverages (Vec<Coverage>) |this, tape, position| {
            jump_take!(tape, position, this.forward_glyph_count, this.forward_coverage_offsets)
        },
    }
}

table! {
    #[doc = "A table for other types of substitution."]
    pub ExtensionSubstitution { // ExtensionSubstFormat1
        format (u16) = { 1 }, // SubstFormat
        kind   (u16), // ExtensionLookupType
        offset (u32), // ExtensionOffset
    }
}

table! {
    #[doc = "A table for substituting glyphs in reverse order in a chaining context."]
    pub ReverseChainContextSubstitution { // ReverseChainSingleSubstFormat1
    }
}

impl Walue<u16> for Table {
    fn read<T: Tape>(tape: &mut T, kind: u16) -> Result<Self> {
        Ok(match kind {
            1 => Table::SingleSubstitution(try!(tape.take())),
            2 => Table::MultipleSubstitution(try!(tape.take())),
            3 => Table::AlternateSubstitution(try!(tape.take())),
            4 => Table::LigatureSubstitution(try!(tape.take())),
            5 => Table::ContextSubstitution(try!(tape.take())),
            6 => Table::ChainContextSubstitution(try!(tape.take())),
            7 => Table::ExtensionSubstitution(try!(tape.take())),
            8 => Table::ReverseChainContextSubstitution(try!(tape.take())),
            _ => raise!("found an unknown glyph-substitution type"),
        })
    }
}

impl Value for SingleSubstitution {
    fn read<T: Tape>(tape: &mut T) -> Result<Self> {
        Ok(match try!(tape.peek::<u16>()) {
            1 => SingleSubstitution::Format1(try!(tape.take())),
            2 => SingleSubstitution::Format2(try!(tape.take())),
            _ => raise!("found an unknown format of the single-substitution table"),
        })
    }
}

impl Value for ContextSubstitution {
    fn read<T: Tape>(tape: &mut T) -> Result<Self> {
        Ok(match try!(tape.peek::<u16>()) {
            1 => ContextSubstitution::Format1(try!(tape.take())),
            2 => ContextSubstitution::Format2(try!(tape.take())),
            3 => ContextSubstitution::Format3(try!(tape.take())),
            _ => raise!("found an unknown format of the context-substitution table"),
        })
    }
}

impl Value for ChainContextSubstitution {
    fn read<T: Tape>(tape: &mut T) -> Result<Self> {
        Ok(match try!(tape.peek::<u16>()) {
            1 => ChainContextSubstitution::Format1(try!(tape.take())),
            2 => ChainContextSubstitution::Format2(try!(tape.take())),
            3 => ChainContextSubstitution::Format3(try!(tape.take())),
            _ => raise!("found an unknown format of the chaining-context-substitution table"),
        })
    }
}
<|endoftext|>


<commit_before>#[derive(Clone)]
pub struct VecMap<K, V> {
    vec: Vec<(K, V)>,
}

impl<K: PartialEq, V> VecMap<K, V> {
    #[inline]
    pub fn with_capacity(cap: usize) -> VecMap<K, V> {
        VecMap {
            vec: Vec::with_capacity(cap)
        }
    }

    #[inline]
    pub fn insert(&mut self, key: K, value: V) {
        match self.find(&key) {
            Some(pos) => self.vec[pos] = (key, value),
            None => self.vec.push((key, value))
        }
    }

    #[inline]
    pub fn entry(&mut self, key: K) -> Entry<K, V> {
        match self.find(&key) {
            Some(pos) => Entry::Occupied(OccupiedEntry {
                vec: self,
                pos: pos,
            }),
            None => Entry::Vacant(VacantEntry {
                vec: self,
                key: key,
            })
        }
    }

    #[inline]
    pub fn get<K2>(&self, key: &K2) -> Option<&V>
    where K2: PartialEq<K> + ?Sized {
        self.find(key).map(move |pos| &self.vec[pos].1)
    }

    #[inline]
    pub fn get_mut<K2>(&mut self, key: &K2) -> Option<&mut V>
    where K2: PartialEq<K> {
        self.find(key).map(move |pos| &mut self.vec[pos].1)
    }

    #[inline]
    pub fn contains_key<K2>(&self, key: &K2) -> bool
    where K2: PartialEq<K> {
        self.find(key).is_some()
    }

    #[inline]
    pub fn len(&self) -> usize { self.vec.len() }

    #[inline]
    pub fn is_empty(&self) -> bool { self.vec.is_empty() }

    #[inline]
    pub fn iter(&self) -> ::std::slice::Iter<(K, V)> {
        self.vec.iter()
    }
    #[inline]
    pub fn remove<K2>(&mut self, key: &K2) -> Option<V>
    where K2: PartialEq<K> + ?Sized {
        self.find(key).map(|pos| self.vec.remove(pos)).map(|(_, v)| v)
    }
    #[inline]
    pub fn clear(&mut self) {
        self.vec.clear();
    }

    #[inline]
    fn find<K2>(&self, key: &K2) -> Option<usize>
    where K2: PartialEq<K> + ?Sized {
        self.vec.iter().position(|entry| key == &entry.0)
    }
}

pub enum Entry<'a, K: 'a, V: 'a> {
    Vacant(VacantEntry<'a, K, V>),
    Occupied(OccupiedEntry<'a, K, V>)
}

pub struct VacantEntry<'a, K: 'a, V: 'a> {
    vec: &'a mut VecMap<K, V>,
    key: K,
}

impl<'a, K, V> VacantEntry<'a, K, V> {
    pub fn insert(self, val: V) -> &'a mut V {
        let mut vec = self.vec;
        vec.vec.push((self.key, val));
        let pos = vec.vec.len() - 1;
        &mut vec.vec[pos].1
    }
}

pub struct OccupiedEntry<'a, K: 'a, V: 'a> {
    vec: &'a mut VecMap<K, V>,
    pos: usize,
}

impl<'a, K, V> OccupiedEntry<'a, K, V> {
    pub fn into_mut(self) -> &'a mut V {
        &mut self.vec.vec[self.pos].1
    }
}
<commit_msg>refactor(header): remove unused VecMap.is_empty<commit_after>#[derive(Clone)]
pub struct VecMap<K, V> {
    vec: Vec<(K, V)>,
}

impl<K: PartialEq, V> VecMap<K, V> {
    #[inline]
    pub fn with_capacity(cap: usize) -> VecMap<K, V> {
        VecMap {
            vec: Vec::with_capacity(cap)
        }
    }

    #[inline]
    pub fn insert(&mut self, key: K, value: V) {
        match self.find(&key) {
            Some(pos) => self.vec[pos] = (key, value),
            None => self.vec.push((key, value))
        }
    }

    #[inline]
    pub fn entry(&mut self, key: K) -> Entry<K, V> {
        match self.find(&key) {
            Some(pos) => Entry::Occupied(OccupiedEntry {
                vec: self,
                pos: pos,
            }),
            None => Entry::Vacant(VacantEntry {
                vec: self,
                key: key,
            })
        }
    }

    #[inline]
    pub fn get<K2>(&self, key: &K2) -> Option<&V>
    where K2: PartialEq<K> + ?Sized {
        self.find(key).map(move |pos| &self.vec[pos].1)
    }

    #[inline]
    pub fn get_mut<K2>(&mut self, key: &K2) -> Option<&mut V>
    where K2: PartialEq<K> {
        self.find(key).map(move |pos| &mut self.vec[pos].1)
    }

    #[inline]
    pub fn contains_key<K2>(&self, key: &K2) -> bool
    where K2: PartialEq<K> {
        self.find(key).is_some()
    }

    #[inline]
    pub fn len(&self) -> usize { self.vec.len() }

    #[inline]
    pub fn iter(&self) -> ::std::slice::Iter<(K, V)> {
        self.vec.iter()
    }
    #[inline]
    pub fn remove<K2>(&mut self, key: &K2) -> Option<V>
    where K2: PartialEq<K> + ?Sized {
        self.find(key).map(|pos| self.vec.remove(pos)).map(|(_, v)| v)
    }
    #[inline]
    pub fn clear(&mut self) {
        self.vec.clear();
    }

    #[inline]
    fn find<K2>(&self, key: &K2) -> Option<usize>
    where K2: PartialEq<K> + ?Sized {
        self.vec.iter().position(|entry| key == &entry.0)
    }
}

pub enum Entry<'a, K: 'a, V: 'a> {
    Vacant(VacantEntry<'a, K, V>),
    Occupied(OccupiedEntry<'a, K, V>)
}

pub struct VacantEntry<'a, K: 'a, V: 'a> {
    vec: &'a mut VecMap<K, V>,
    key: K,
}

impl<'a, K, V> VacantEntry<'a, K, V> {
    pub fn insert(self, val: V) -> &'a mut V {
        let mut vec = self.vec;
        vec.vec.push((self.key, val));
        let pos = vec.vec.len() - 1;
        &mut vec.vec[pos].1
    }
}

pub struct OccupiedEntry<'a, K: 'a, V: 'a> {
    vec: &'a mut VecMap<K, V>,
    pos: usize,
}

impl<'a, K, V> OccupiedEntry<'a, K, V> {
    pub fn into_mut(self) -> &'a mut V {
        &mut self.vec.vec[self.pos].1
    }
}
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::borrow::Cow;
use std::cell::RefCell;
use std::fmt::Display;
use std::path::Path;
use std::rc::Rc;
use std::io::ErrorKind;
use std::collections::BTreeMap;
use dbus::Connection;
use dbus::BusType;
use dbus::Message;
use dbus::MessageItem;
use dbus::NameFlag;
use dbus::tree::Factory;
use dbus::tree::MethodErr;
use dbus::tree::MethodFn;
use dbus::tree::MethodResult;
use dbus::tree::Tree;

use dbus_consts::*;

use engine::Engine;
use types::{StratisResult, StratisError};

#[derive(Debug, Clone)]
pub struct DbusContext {
    pub next_index: i32,
    pub pools: BTreeMap<String, String>,
}

impl DbusContext {
    pub fn new() -> DbusContext {
        DbusContext {
            next_index: 0,
            pools: BTreeMap::new(),
        }
    }
    pub fn get_next_id(&mut self) {
        self.next_index += 1;
    }
}

fn internal_to_dbus_err(err: &StratisError) -> StratisErrorEnum {
    match *err {
        StratisError::Stratis(_) => StratisErrorEnum::STRATIS_ERROR,
        StratisError::Io(ref err) => {
            match err.kind() {
                ErrorKind::NotFound => StratisErrorEnum::STRATIS_NOTFOUND,
                ErrorKind::AlreadyExists => StratisErrorEnum::STRATIS_ALREADY_EXISTS,
                _ => StratisErrorEnum::STRATIS_ERROR,
            }
        }
        _ => StratisErrorEnum::STRATIS_ERROR,
    }
}

fn list_pools(m: &Message,
              dbus_context: &Rc<RefCell<DbusContext>>,
              engine: &Rc<RefCell<Engine>>)
              -> MethodResult {

    let result = engine.borrow().list_pools();

    let mut msg_vec = Vec::new();

    // TODO: deal with failure
    let pool_tree = result.unwrap();

    for (name, pool) in pool_tree {
        let entry = vec![MessageItem::Str(format!("{}", name)),
                         MessageItem::UInt16(0),
                         MessageItem::Str(String::from("Ok"))];
        msg_vec.push(MessageItem::Struct(entry));
    }

    let item_array = MessageItem::Array(msg_vec, Cow::Borrowed("(sqs)"));

    Ok(vec![m.method_return().append1(item_array)])
}

fn create_pool(m: &Message,
               dbus_context: &Rc<RefCell<DbusContext>>,
               engine: &Rc<RefCell<Engine>>)
               -> MethodResult {

    let message: (Option<MessageItem>, Option<MessageItem>, Option<MessageItem>) = m.get3();

    let item0: MessageItem = try!(message.0.ok_or_else(MethodErr::no_arg));
    let name: &String = try!(item0.inner().map_err(|_| MethodErr::invalid_arg(&item0)));

    let item1: MessageItem = try!(message.1.ok_or_else(MethodErr::no_arg));
    let devs: &Vec<MessageItem> = try!(item1.inner().map_err(|_| MethodErr::invalid_arg(&item1)));

    let item2: MessageItem = try!(message.2.ok_or_else(MethodErr::no_arg));
    let raid_level: u16 = try!(item2.inner().map_err(|_| MethodErr::invalid_arg(&item2)));

    let blockdevs = devs.iter()
        .map(|x| Path::new(x.inner::<&String>().unwrap()))
        .collect::<Vec<&Path>>();

    match engine.borrow_mut().create_pool(&name, &blockdevs, raid_level) {
        Ok(_) => Ok(vec![m.method_return().append3("/dbus/newpool/path", 0, "Ok")]),
        Err(x) => {
            let dbus_err = internal_to_dbus_err(&x);
            Ok(vec![m.method_return()
                        .append3("", dbus_err.get_error_int(), dbus_err.get_error_string())])
        }
    }
}

fn destroy_pool(m: &Message,
                dbus_context: &Rc<RefCell<DbusContext>>,
                engine: &Rc<RefCell<Engine>>)
                -> MethodResult {

    let item: MessageItem = try!(m.get1().ok_or_else(MethodErr::no_arg));

    let name: &String = try!(item.inner().map_err(|_| MethodErr::invalid_arg(&item)));

    let result = engine.borrow_mut().destroy_pool(&name);

    let message = m.method_return();

    let msg = match result {
        Ok(_) => {
            message.append2(MessageItem::UInt16(0),
                            MessageItem::Str(format!("{}", "Ok")))
        }
        Err(err) => {
            message.append2(MessageItem::UInt16(0),
                            MessageItem::Str(format!("{}", "Ok")))
        }
    };

    Ok(vec![msg])

}

fn get_pool_object_path(m: &Message, dbus_context: &Rc<RefCell<DbusContext>>) -> MethodResult {

    Ok(vec![m.method_return().append3("/dbus/pool/path", 0, "Ok")])
}

fn get_volume_object_path(m: &Message, dbus_context: &Rc<RefCell<DbusContext>>) -> MethodResult {
    Ok(vec![m.method_return().append3("/dbus/volume/path", 0, "Ok")])
}

fn get_dev_object_path(m: &Message, dbus_context: &Rc<RefCell<DbusContext>>) -> MethodResult {
    Ok(vec![m.method_return().append3("/dbus/dev/path", 0, "Ok")])
}

fn get_cache_object_path(m: &Message, dbus_context: &Rc<RefCell<DbusContext>>) -> MethodResult {
    Ok(vec![m.method_return().append3("/dbus/cache/path", 0, "Ok")])
}

fn get_list_items<T, I>(m: &Message, iter: I) -> MethodResult
    where T: HasCodes + Display,
          I: Iterator<Item = T>
{
    let msg_vec = iter.map(|item| {
            MessageItem::Struct(vec![MessageItem::Str(format!("{}", item)),
                                     MessageItem::UInt16(item.get_error_int()),
                                     MessageItem::Str(format!("{}", item.get_error_string()))])
        })
        .collect::<Vec<MessageItem>>();

    let item_array = MessageItem::Array(msg_vec, Cow::Borrowed("(sqs)"));
    Ok(vec![m.method_return().append1(item_array)])
}

fn get_error_codes(m: &Message) -> MethodResult {
    get_list_items(m, StratisErrorEnum::iter_variants())
}

fn get_raid_levels(m: &Message) -> MethodResult {
    get_list_items(m, StratisRaidType::iter_variants())
}

fn get_dev_types(m: &Message) -> MethodResult {
    let mut items = m.get_items();
    if items.len() < 1 {
        return Err(MethodErr::no_arg());
    }

    let _name = try!(items.pop()
        .ok_or_else(MethodErr::no_arg)
        .and_then(|i| {
            i.inner::<&str>()
                .map_err(|_| MethodErr::invalid_arg(&i))
                .map(|i| i.to_owned())
        }));

    println!("method called");

    Ok(vec![m.method_return()])
}

pub fn get_base_tree<'a>(dbus_context: Rc<RefCell<DbusContext>>,
                         engine: Rc<RefCell<Engine>>)
                         -> StratisResult<Tree<MethodFn<'a>>> {

    let f = Factory::new_fn();

    let base_tree = f.tree();

    let dbus_context_clone = dbus_context.clone();
    let engine_clone = engine.clone();

    let createpool_method = f.method(CREATE_POOL,
                move |m, _, _| create_pool(m, &dbus_context_clone, &engine_clone))
        .in_arg(("pool_name", "s"))
        .in_arg(("dev_list", "as"))
        .in_arg(("raid_type", "q"))
        .out_arg(("object_path", "o"))
        .out_arg(("return_code", "q"))
        .out_arg(("return_string", "s"));

    let engine_clone = engine.clone();
    let dbus_context_clone = dbus_context.clone();

    let destroypool_method = f.method(DESTROY_POOL,
                move |m, _, _| destroy_pool(m, &dbus_context_clone, &engine_clone))
        .in_arg(("pool_name", "s"))
        .out_arg(("return_code", "q"))
        .out_arg(("return_string", "s"));

    let engine_clone = engine.clone();
    let dbus_context_clone = dbus_context.clone();

    let listpools_method = f.method(LIST_POOLS,
                move |m, _, _| list_pools(m, &dbus_context_clone, &engine_clone))
        .out_arg(("pool_names", "as"))
        .out_arg(("return_code", "q"))
        .out_arg(("return_string", "s"));

    let dbus_context_clone = dbus_context.clone();

    let getpoolobjectpath_method = f.method(GET_POOL_OBJECT_PATH,
                move |m, _, _| get_pool_object_path(m, &dbus_context_clone))
        .in_arg(("pool_name", "s"))
        .out_arg(("object_path", "o"))
        .out_arg(("return_code", "q"))
        .out_arg(("return_string", "s"));

    let dbus_context_clone = dbus_context.clone();

    let getvolumeobjectpath_method = f.method(GET_VOLUME_OBJECT_PATH,
                move |m, _, _| get_volume_object_path(m, &dbus_context_clone))
        .in_arg(("pool_name", "s"))
        .in_arg(("volume_name", "s"))
        .out_arg(("object_path", "o"))
        .out_arg(("return_code", "q"))
        .out_arg(("return_string", "s"));

    let dbus_context_clone = dbus_context.clone();

    let getdevobjectpath_method = f.method(GET_DEV_OBJECT_PATH,
                move |m, _, _| get_dev_object_path(m, &dbus_context_clone))
        .in_arg(("dev_name", "s"))
        .out_arg(("object_path", "o"))
        .out_arg(("return_code", "q"))
        .out_arg(("return_string", "s"));

    let dbus_context_clone = dbus_context.clone();

    let getcacheobjectpath_method = f.method(GET_CACHE_OBJECT_PATH,
                move |m, _, _| get_cache_object_path(m, &dbus_context_clone))
        .in_arg(("cache_dev_name", "s"))
        .out_arg(("object_path", "o"))
        .out_arg(("return_code", "q"))
        .out_arg(("return_string", "s"));

    let geterrorcodes_method = f.method(GET_ERROR_CODES, move |m, _, _| get_error_codes(m))
        .out_arg(("error_codes", "a(sqs)"));

    let getraidlevels_method = f.method(GET_RAID_LEVELS, move |m, _, _| get_raid_levels(m))
        .out_arg(("error_codes", "a(sqs)"));

    let getdevtypes_method = f.method(GET_DEV_TYPES, move |m, _, _| get_dev_types(m));

    let obj_path = f.object_path(STRATIS_BASE_PATH)
        .introspectable()
        .add(f.interface(STRATIS_MANAGER_INTERFACE)
            .add_m(listpools_method)
            .add_m(createpool_method)
            .add_m(destroypool_method)
            .add_m(getpoolobjectpath_method)
            .add_m(getvolumeobjectpath_method)
            .add_m(getdevobjectpath_method)
            .add_m(getcacheobjectpath_method)
            .add_m(geterrorcodes_method)
            .add_m(getraidlevels_method)
            .add_m(getdevtypes_method));

    let base_tree = base_tree.add(obj_path);

    Ok(base_tree)
}

pub fn run(engine: Rc<RefCell<Engine>>) -> StratisResult<()> {
    let dbus_context = Rc::new(RefCell::new(DbusContext::new()));
    let tree = get_base_tree(dbus_context, engine).unwrap();

    // Setup DBus connection
    let c = try!(Connection::get_private(BusType::Session));
    c.register_name(STRATIS_BASE_SERVICE, NameFlag::ReplaceExisting as u32).unwrap();
    try!(tree.set_registered(&c, true));

    // ...and serve incoming requests.
    for _ in tree.run(&c, c.iter(1000)) {
    }

    Ok(())
}
<commit_msg>Also check the element type of the array.<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::borrow::Cow;
use std::cell::RefCell;
use std::fmt::Display;
use std::path::Path;
use std::rc::Rc;
use std::io::ErrorKind;
use std::collections::BTreeMap;
use dbus::Connection;
use dbus::BusType;
use dbus::Message;
use dbus::MessageItem;
use dbus::NameFlag;
use dbus::tree::Factory;
use dbus::tree::MethodErr;
use dbus::tree::MethodFn;
use dbus::tree::MethodResult;
use dbus::tree::Tree;

use dbus_consts::*;

use engine::Engine;
use types::{StratisResult, StratisError};

#[derive(Debug, Clone)]
pub struct DbusContext {
    pub next_index: i32,
    pub pools: BTreeMap<String, String>,
}

impl DbusContext {
    pub fn new() -> DbusContext {
        DbusContext {
            next_index: 0,
            pools: BTreeMap::new(),
        }
    }
    pub fn get_next_id(&mut self) {
        self.next_index += 1;
    }
}

fn internal_to_dbus_err(err: &StratisError) -> StratisErrorEnum {
    match *err {
        StratisError::Stratis(_) => StratisErrorEnum::STRATIS_ERROR,
        StratisError::Io(ref err) => {
            match err.kind() {
                ErrorKind::NotFound => StratisErrorEnum::STRATIS_NOTFOUND,
                ErrorKind::AlreadyExists => StratisErrorEnum::STRATIS_ALREADY_EXISTS,
                _ => StratisErrorEnum::STRATIS_ERROR,
            }
        }
        _ => StratisErrorEnum::STRATIS_ERROR,
    }
}

fn list_pools(m: &Message,
              dbus_context: &Rc<RefCell<DbusContext>>,
              engine: &Rc<RefCell<Engine>>)
              -> MethodResult {

    let result = engine.borrow().list_pools();

    let mut msg_vec = Vec::new();

    // TODO: deal with failure
    let pool_tree = result.unwrap();

    for (name, pool) in pool_tree {
        let entry = vec![MessageItem::Str(format!("{}", name)),
                         MessageItem::UInt16(0),
                         MessageItem::Str(String::from("Ok"))];
        msg_vec.push(MessageItem::Struct(entry));
    }

    let item_array = MessageItem::Array(msg_vec, Cow::Borrowed("(sqs)"));

    Ok(vec![m.method_return().append1(item_array)])
}

fn create_pool(m: &Message,
               dbus_context: &Rc<RefCell<DbusContext>>,
               engine: &Rc<RefCell<Engine>>)
               -> MethodResult {

    let message: (Option<MessageItem>, Option<MessageItem>, Option<MessageItem>) = m.get3();

    let item0: MessageItem = try!(message.0.ok_or_else(MethodErr::no_arg));
    let name: &String = try!(item0.inner().map_err(|_| MethodErr::invalid_arg(&item0)));

    let item1: MessageItem = try!(message.1.ok_or_else(MethodErr::no_arg));
    let devs: &Vec<MessageItem> = try!(item1.inner().map_err(|_| MethodErr::invalid_arg(&item1)));
    let mut devstrings = devs.iter().map(|x| x.inner::<&String>());
    if devstrings.any(|x| x.is_err()) {
        return Err(MethodErr::invalid_arg(&item1));
    }
    let blockdevs = devstrings.map(|x| Path::new(x.unwrap())).collect::<Vec<&Path>>();

    let item2: MessageItem = try!(message.2.ok_or_else(MethodErr::no_arg));
    let raid_level: u16 = try!(item2.inner().map_err(|_| MethodErr::invalid_arg(&item2)));

    match engine.borrow_mut().create_pool(&name, &blockdevs, raid_level) {
        Ok(_) => Ok(vec![m.method_return().append3("/dbus/newpool/path", 0, "Ok")]),
        Err(x) => {
            let dbus_err = internal_to_dbus_err(&x);
            Ok(vec![m.method_return()
                        .append3("", dbus_err.get_error_int(), dbus_err.get_error_string())])
        }
    }
}

fn destroy_pool(m: &Message,
                dbus_context: &Rc<RefCell<DbusContext>>,
                engine: &Rc<RefCell<Engine>>)
                -> MethodResult {

    let item: MessageItem = try!(m.get1().ok_or_else(MethodErr::no_arg));

    let name: &String = try!(item.inner().map_err(|_| MethodErr::invalid_arg(&item)));

    let result = engine.borrow_mut().destroy_pool(&name);

    let message = m.method_return();

    let msg = match result {
        Ok(_) => {
            message.append2(MessageItem::UInt16(0),
                            MessageItem::Str(format!("{}", "Ok")))
        }
        Err(err) => {
            message.append2(MessageItem::UInt16(0),
                            MessageItem::Str(format!("{}", "Ok")))
        }
    };

    Ok(vec![msg])

}

fn get_pool_object_path(m: &Message, dbus_context: &Rc<RefCell<DbusContext>>) -> MethodResult {

    Ok(vec![m.method_return().append3("/dbus/pool/path", 0, "Ok")])
}

fn get_volume_object_path(m: &Message, dbus_context: &Rc<RefCell<DbusContext>>) -> MethodResult {
    Ok(vec![m.method_return().append3("/dbus/volume/path", 0, "Ok")])
}

fn get_dev_object_path(m: &Message, dbus_context: &Rc<RefCell<DbusContext>>) -> MethodResult {
    Ok(vec![m.method_return().append3("/dbus/dev/path", 0, "Ok")])
}

fn get_cache_object_path(m: &Message, dbus_context: &Rc<RefCell<DbusContext>>) -> MethodResult {
    Ok(vec![m.method_return().append3("/dbus/cache/path", 0, "Ok")])
}

fn get_list_items<T, I>(m: &Message, iter: I) -> MethodResult
    where T: HasCodes + Display,
          I: Iterator<Item = T>
{
    let msg_vec = iter.map(|item| {
            MessageItem::Struct(vec![MessageItem::Str(format!("{}", item)),
                                     MessageItem::UInt16(item.get_error_int()),
                                     MessageItem::Str(format!("{}", item.get_error_string()))])
        })
        .collect::<Vec<MessageItem>>();

    let item_array = MessageItem::Array(msg_vec, Cow::Borrowed("(sqs)"));
    Ok(vec![m.method_return().append1(item_array)])
}

fn get_error_codes(m: &Message) -> MethodResult {
    get_list_items(m, StratisErrorEnum::iter_variants())
}

fn get_raid_levels(m: &Message) -> MethodResult {
    get_list_items(m, StratisRaidType::iter_variants())
}

fn get_dev_types(m: &Message) -> MethodResult {
    let mut items = m.get_items();
    if items.len() < 1 {
        return Err(MethodErr::no_arg());
    }

    let _name = try!(items.pop()
        .ok_or_else(MethodErr::no_arg)
        .and_then(|i| {
            i.inner::<&str>()
                .map_err(|_| MethodErr::invalid_arg(&i))
                .map(|i| i.to_owned())
        }));

    println!("method called");

    Ok(vec![m.method_return()])
}

pub fn get_base_tree<'a>(dbus_context: Rc<RefCell<DbusContext>>,
                         engine: Rc<RefCell<Engine>>)
                         -> StratisResult<Tree<MethodFn<'a>>> {

    let f = Factory::new_fn();

    let base_tree = f.tree();

    let dbus_context_clone = dbus_context.clone();
    let engine_clone = engine.clone();

    let createpool_method = f.method(CREATE_POOL,
                move |m, _, _| create_pool(m, &dbus_context_clone, &engine_clone))
        .in_arg(("pool_name", "s"))
        .in_arg(("dev_list", "as"))
        .in_arg(("raid_type", "q"))
        .out_arg(("object_path", "o"))
        .out_arg(("return_code", "q"))
        .out_arg(("return_string", "s"));

    let engine_clone = engine.clone();
    let dbus_context_clone = dbus_context.clone();

    let destroypool_method = f.method(DESTROY_POOL,
                move |m, _, _| destroy_pool(m, &dbus_context_clone, &engine_clone))
        .in_arg(("pool_name", "s"))
        .out_arg(("return_code", "q"))
        .out_arg(("return_string", "s"));

    let engine_clone = engine.clone();
    let dbus_context_clone = dbus_context.clone();

    let listpools_method = f.method(LIST_POOLS,
                move |m, _, _| list_pools(m, &dbus_context_clone, &engine_clone))
        .out_arg(("pool_names", "as"))
        .out_arg(("return_code", "q"))
        .out_arg(("return_string", "s"));

    let dbus_context_clone = dbus_context.clone();

    let getpoolobjectpath_method = f.method(GET_POOL_OBJECT_PATH,
                move |m, _, _| get_pool_object_path(m, &dbus_context_clone))
        .in_arg(("pool_name", "s"))
        .out_arg(("object_path", "o"))
        .out_arg(("return_code", "q"))
        .out_arg(("return_string", "s"));

    let dbus_context_clone = dbus_context.clone();

    let getvolumeobjectpath_method = f.method(GET_VOLUME_OBJECT_PATH,
                move |m, _, _| get_volume_object_path(m, &dbus_context_clone))
        .in_arg(("pool_name", "s"))
        .in_arg(("volume_name", "s"))
        .out_arg(("object_path", "o"))
        .out_arg(("return_code", "q"))
        .out_arg(("return_string", "s"));

    let dbus_context_clone = dbus_context.clone();

    let getdevobjectpath_method = f.method(GET_DEV_OBJECT_PATH,
                move |m, _, _| get_dev_object_path(m, &dbus_context_clone))
        .in_arg(("dev_name", "s"))
        .out_arg(("object_path", "o"))
        .out_arg(("return_code", "q"))
        .out_arg(("return_string", "s"));

    let dbus_context_clone = dbus_context.clone();

    let getcacheobjectpath_method = f.method(GET_CACHE_OBJECT_PATH,
                move |m, _, _| get_cache_object_path(m, &dbus_context_clone))
        .in_arg(("cache_dev_name", "s"))
        .out_arg(("object_path", "o"))
        .out_arg(("return_code", "q"))
        .out_arg(("return_string", "s"));

    let geterrorcodes_method = f.method(GET_ERROR_CODES, move |m, _, _| get_error_codes(m))
        .out_arg(("error_codes", "a(sqs)"));

    let getraidlevels_method = f.method(GET_RAID_LEVELS, move |m, _, _| get_raid_levels(m))
        .out_arg(("error_codes", "a(sqs)"));

    let getdevtypes_method = f.method(GET_DEV_TYPES, move |m, _, _| get_dev_types(m));

    let obj_path = f.object_path(STRATIS_BASE_PATH)
        .introspectable()
        .add(f.interface(STRATIS_MANAGER_INTERFACE)
            .add_m(listpools_method)
            .add_m(createpool_method)
            .add_m(destroypool_method)
            .add_m(getpoolobjectpath_method)
            .add_m(getvolumeobjectpath_method)
            .add_m(getdevobjectpath_method)
            .add_m(getcacheobjectpath_method)
            .add_m(geterrorcodes_method)
            .add_m(getraidlevels_method)
            .add_m(getdevtypes_method));

    let base_tree = base_tree.add(obj_path);

    Ok(base_tree)
}

pub fn run(engine: Rc<RefCell<Engine>>) -> StratisResult<()> {
    let dbus_context = Rc::new(RefCell::new(DbusContext::new()));
    let tree = get_base_tree(dbus_context, engine).unwrap();

    // Setup DBus connection
    let c = try!(Connection::get_private(BusType::Session));
    c.register_name(STRATIS_BASE_SERVICE, NameFlag::ReplaceExisting as u32).unwrap();
    try!(tree.set_registered(&c, true));

    // ...and serve incoming requests.
    for _ in tree.run(&c, c.iter(1000)) {
    }

    Ok(())
}
<|endoftext|>


<commit_before><commit_msg>doc: Add missing spaces<commit_after><|endoftext|>


<commit_before>use super::super::parse_ast as past;
use super::nodes::*;
use super::scope::{ClosureScope, FuncScope, ModuleScope, Scope, ScopeLike};
use super::typ::{Generic, Type, Variable};
use super::{unify, Builtins, Closable, RecursionTracker, TypeError, TypeResult};

pub fn translate_module(pmodule: past::Module) -> TypeResult<Module> {
    let scope = ModuleScope::new().into_scope();

    let mut statements = vec![];
    for pstatement in pmodule.statements.into_iter() {
        let statement = match pstatement {
            past::ModuleStatement::Func(pfunc) => {
                ModuleStatement::Func(translate_func(&pfunc, scope.clone())?)
            }
            _ => unreachable!(),
        };
        // NOTE: Closing should already be done within the translation of every
        //   module-level statement:
        statements.push(statement.close(&mut RecursionTracker::new(), scope.clone())?);
    }

    // Now that we've translated the whole module we can close all of the
    // statements to ensure all types are bound.
    // let mut closed_statements = vec![];
    // for statement in statements.into_iter() {
    //     closed_statements.push(statement.close(&mut RecursionTracker::new())?);
    // }

    Ok(Module { statements })
}

fn translate_func(pfunc: &past::Func, scope: Scope) -> TypeResult<Func> {
    let name = pfunc.name.name.clone();
    // The scope that the function's arguments and body will be evaluated in.
    let func_scope = FuncScope::new(Some(scope.clone())).into_scope();

    // Build the `FuncArgument` nodes ahead of time so that they have types
    // in place.
    let arguments_nodes = pfunc
        .arguments
        .iter()
        .map(|argument| FuncArgument {
            name: argument.name.clone(),
            // TODO: Support argument type definitions.
            typ: Type::new_unbound(func_scope.clone()),
        })
        .collect::<Vec<_>>();

    for argument_node in arguments_nodes.iter() {
        func_scope.add_local(&argument_node.name, argument_node.typ.clone())?;
    }
    // Build a forward declaration for the recursive call.
    let retrn = Type::new_unbound(func_scope.clone());
    let typ = Type::new_func(
        Some(name.clone()),
        arguments_nodes
            .iter()
            .map(|argument| argument.typ.clone())
            .collect::<Vec<_>>(),
        retrn.clone(),
        scope.clone(),
    );
    // Add the function to its defining scope.
    // TODO: This should be an `add_static` once functions are static.
    scope.add_local(&name, typ.clone())?;

    let body = match &pfunc.body {
        past::FuncBody::Block(block) => {
            FuncBody::Block(translate_block(block, func_scope.clone())?)
        }
    };

    let implicit_retrn = body.typ();
    unify(&retrn, &implicit_retrn, func_scope.clone())?;

    Ok(Func {
        name: name.clone(),
        arguments: arguments_nodes,
        body,
        scope: func_scope.clone(),
        typ,
    })
}

fn translate_block(pblock: &past::Block, scope: Scope) -> TypeResult<Block> {
    let mut statements = vec![];
    for pstatement in &pblock.statements {
        let statement = match pstatement {
            past::BlockStatement::CommentLine(_) => continue,
            past::BlockStatement::Func(pfunc) => {
                BlockStatement::Func(translate_func(pfunc, scope.clone())?)
            }
            past::BlockStatement::Expression(pexpression) => {
                BlockStatement::Expression(translate_expression(&pexpression, scope.clone())?)
            }
            past::BlockStatement::Var(pvar) => {
                BlockStatement::Var(translate_var(pvar, scope.clone())?)
            }
        };
        statements.push(statement);
    }
    let typ = if statements.is_empty() {
        Type::new_empty_tuple(scope)
    } else {
        statements.last().unwrap().typ()
    };
    Ok(Block {
        statements,
        span: pblock.span.clone(),
        typ,
    })
}

fn translate_var(pvar: &past::Var, scope: Scope) -> TypeResult<Var> {
    let typ = Type::new_unbound(scope.clone());
    scope.add_local(&pvar.name.name, typ.clone())?;
    let initializer = match &pvar.initializer {
        Some(expression) => {
            let initializer = translate_expression(expression, scope.clone())?;
            unify(&typ, initializer.typ(), scope)?;
            Some(initializer)
        }
        None => None,
    };
    Ok(Var {
        name: pvar.name.clone(),
        initializer,
        typ,
    })
}

fn translate_expression(pexpression: &past::Expression, scope: Scope) -> TypeResult<Expression> {
    Ok(match pexpression {
        past::Expression::Closure(pclosure) => {
            Expression::Closure(translate_closure(pclosure, scope)?)
        }
        past::Expression::Identifier(pidentifier) => {
            let name = pidentifier.name.clone();
            let resolution = scope
                .get_local(&name.name)
                .map_err(|err| err.with_span(pidentifier.name.span.clone()))?;
            let typ = resolution.typ();
            Expression::Identifier(Identifier {
                name,
                resolution,
                typ,
            })
        }
        past::Expression::Infix(pinfix) => {
            let lhs = translate_expression(&*pinfix.lhs, scope.clone())?;
            let rhs = translate_expression(&*pinfix.rhs, scope.clone())?;
            // Left- and right-hand sides must be the same in an infix operation.
            unify(lhs.typ(), rhs.typ(), scope)?;
            let typ = rhs.typ().clone();
            Expression::Infix(Infix {
                lhs: Box::new(lhs),
                op: pinfix.op.clone(),
                rhs: Box::new(rhs),
                typ,
            })
        }
        past::Expression::LiteralInt(pliteral) => {
            let class = Builtins::get("Int");
            Expression::LiteralInt(LiteralInt {
                value: pliteral.value,
                typ: Type::new_object(class, scope),
            })
        }
        past::Expression::PostfixCall(pcall) => {
            Expression::PostfixCall(translate_postfix_call(pcall, scope)?)
        }
        past::Expression::PostfixProperty(pproperty) => {
            Expression::PostfixProperty(translate_postfix_property(pproperty, scope)?)
        }
        _ => unreachable!("Cannot translate {:?}", pexpression),
    })
}

fn translate_closure(pclosure: &past::Closure, scope: Scope) -> TypeResult<Closure> {
    let closure_scope = ClosureScope::new(Some(scope.clone())).into_scope();

    let arguments_nodes = pclosure
        .arguments
        .iter()
        .map(|argument| FuncArgument {
            name: argument.name.clone(),
            typ: Type::new_unbound(closure_scope.clone()),
        })
        .collect::<Vec<_>>();

    let retrn = Type::new_unbound(closure_scope.clone());

    for argument_node in arguments_nodes.iter() {
        closure_scope.add_local(&argument_node.name, argument_node.typ.clone())?;
    }

    let body = match &*pclosure.body {
        past::ClosureBody::Block(block) => {
            ClosureBody::Block(translate_block(block, closure_scope.clone())?)
        }
        past::ClosureBody::Expression(expression) => {
            ClosureBody::Expression(translate_expression(expression, closure_scope.clone())?)
        }
    };

    let implicit_retrn = body.typ();
    unify(&retrn, implicit_retrn, closure_scope.clone())?;

    let typ = Type::new_func(
        None,
        arguments_nodes
            .iter()
            .map(|argument| argument.typ.clone())
            .collect::<Vec<_>>(),
        retrn.clone(),
        scope.clone(),
    );

    Ok(Closure {
        arguments: arguments_nodes,
        body: Box::new(body),
        scope: closure_scope.clone(),
        typ,
    })
}

fn translate_postfix_call(pcall: &past::PostfixCall, scope: Scope) -> TypeResult<PostfixCall> {
    let target = translate_expression(&*pcall.target, scope.clone())?;

    let mut arguments = vec![];
    for argument in pcall.arguments.iter() {
        arguments.push(translate_expression(argument, scope.clone())?);
    }

    let retrn = {
        // If the target is already a callable then unify directly instead of
        // through a constraint.
        if let Some((target_arguments, target_retrn)) = target.typ().maybe_callable() {
            let call_arguments = arguments
                .iter()
                .map(|argument| argument.typ().clone())
                .collect::<Vec<_>>();
            if target_arguments.len() != call_arguments.len() {
                return Err(TypeError::ArgumentLengthMismatch {
                    expected: target_arguments,
                    got: call_arguments,
                });
            }
            let mut tracker = RecursionTracker::new();
            for (target_argument, call_argument) in
                target_arguments.iter().zip(call_arguments.iter())
            {
                let open_target_argument =
                    target_argument.open_duplicate(&mut tracker, scope.clone());
                unify(call_argument, &open_target_argument, scope.clone())?;
            }
            let call_retrn = Type::new_unbound(scope.clone());
            let open_target_retrn = target_retrn.open_duplicate(&mut tracker, scope.clone());
            unify(&call_retrn, &open_target_retrn, scope)?;
            call_retrn

        // Otherwise build a callable generic constraint as an intermediary
        // and unify through that.
        } else {
            // The return type of the callable.
            let retrn = Type::new_unbound(scope.clone());

            let mut generic = Generic::new(scope.clone());
            generic.add_callable_constraint(
                arguments
                    .iter()
                    .map(|argument| argument.typ().clone())
                    .collect::<Vec<_>>(),
                retrn.clone(),
            );
            // Unify to ensure target supports being called with the arguments and
            // return types.
            let intermediary = Type::new_variable(Variable::Generic {
                scope: scope.clone(),
                generic,
            });
            unify(target.typ(), &intermediary, scope)
                .map_err(|err| err.with_span(pcall.span.clone()))?;
            retrn
        }
    };

    Ok(PostfixCall {
        target: Box::new(target),
        arguments,
        typ: retrn,
    })
}

fn translate_postfix_property(
    pproperty: &past::PostfixProperty,
    scope: Scope,
) -> TypeResult<PostfixProperty> {
    let target = translate_expression(&*pproperty.target, scope.clone())?;

    // The ultimate type of getting the target's property.
    let typ = Type::new_unbound(scope.clone());

    // Set up a variable generic with a property constraint.
    let mut generic = Generic::new(scope.clone());
    generic.add_property_constraint(pproperty.property.name.clone(), typ.clone());
    // Apply unification to ensure the target supports having the
    // given property.
    let intermediary = Type::new_variable(Variable::Generic {
        scope: scope.clone(),
        generic,
    });
    unify(target.typ(), &intermediary, scope)
        .map_err(|err| err.with_span(pproperty.span.clone()))?;

    Ok(PostfixProperty {
        target: Box::new(target),
        property: pproperty.property.clone(),
        typ,
    })
}

#[cfg(test)]
mod tests {
    use super::super::super::parse_ast as past;
    use super::super::super::parser::{Location, Span, Token, Word};
    use super::super::scope::{ClosureScope, ScopeLike};
    use super::super::{Builtins, Type, TypeError, Variable};
    use super::{translate_expression, translate_func};

    fn word<S: AsRef<str>>(name: S) -> Word {
        Word {
            name: name.as_ref().to_string(),
            span: Span::unknown(),
        }
    }

    #[test]
    fn test_translate_infix() -> Result<(), TypeError> {
        // Test simple addition of two integer literals.
        let pexpression = past::Expression::Infix(past::Infix {
            lhs: Box::new(past::Expression::LiteralInt(past::LiteralInt {
                value: 1,
                span: Span::unknown(),
            })),
            op: Token::Plus(Location::unknown()),
            rhs: Box::new(past::Expression::LiteralInt(past::LiteralInt {
                value: 2,
                span: Span::unknown(),
            })),
        });
        translate_expression(&pexpression, ClosureScope::new(None).into_scope()).map(|_| ())?;

        // Add an unbound variable with an integer literal; check that the
        // variable is substituted to an integer.
        let pexpression = past::Expression::Infix(past::Infix {
            lhs: Box::new(past::Expression::Identifier(past::Identifier {
                name: word("foo"),
            })),
            op: Token::Plus(Location::unknown()),
            rhs: Box::new(past::Expression::LiteralInt(past::LiteralInt {
                value: 2,
                span: Span::unknown(),
            })),
        });
        let scope = ClosureScope::new(None).into_scope();
        let foo = Type::new_unbound(scope.clone());
        scope.add_local("foo", foo.clone())?;
        translate_expression(&pexpression, scope.clone()).map(|_| ())?;
        assert_eq!(
            foo,
            Type::new_substitute(Type::new_object(Builtins::get("Int"), scope.clone()), scope)
        );

        Ok(())
    }

    #[test]
    fn test_translate_func() -> Result<(), TypeError> {
        let pclosure = past::Closure {
            arguments: vec![],
            body: Box::new(past::ClosureBody::Block(past::Block {
                // statements: vec![past::BlockStatement::Expression(past::Expression::Infix(
                //     past::Infix {
                //         lhs: Box::new(past::Expression::Identifier(past::Identifier {
                //             name: word("bar"),
                //         })),
                //         op: Token::Plus(Location::unknown()),
                //         rhs: Box::new(past::Expression::LiteralInt(past::LiteralInt {
                //             value: 1,
                //             span: Span::unknown(),
                //         })),
                //     },
                // ))],
                statements: vec![past::BlockStatement::Expression(
                    past::Expression::Identifier(past::Identifier { name: word("bar") }),
                )],
                span: Span::unknown(),
            })),
            span: Span::unknown(),
        };

        let pfunc = past::Func {
            name: word("foo"),
            arguments: vec![word("bar")],
            body: past::FuncBody::Block(past::Block {
                // statements: vec![past::BlockStatement::Expression(past::Expression::Infix(
                //     past::Infix {
                //         lhs: Box::new(past::Expression::Identifier(past::Identifier {
                //             name: word("bar"),
                //         })),
                //         op: Token::Plus(Location::unknown()),
                //         rhs: Box::new(past::Expression::LiteralInt(past::LiteralInt {
                //             value: 1,
                //             span: Span::unknown(),
                //         })),
                //     },
                // ))],
                statements: vec![
                    past::BlockStatement::Expression(past::Expression::Closure(pclosure)),
                    past::BlockStatement::Expression(past::Expression::Infix(past::Infix {
                        lhs: Box::new(past::Expression::Identifier(past::Identifier {
                            name: word("bar"),
                        })),
                        op: Token::Plus(Location::unknown()),
                        rhs: Box::new(past::Expression::LiteralInt(past::LiteralInt {
                            value: 1,
                            span: Span::unknown(),
                        })),
                    })),
                ],
                span: Span::unknown(),
            }),
            span: Span::unknown(),
        };

        let scope = ClosureScope::new(None).into_scope();
        let func = translate_func(&pfunc, scope)?;

        println!("{:?}", func);

        Ok(())
    }
}
<commit_msg>Close callables (functions and closures) immediately upon translation<commit_after>use super::super::parse_ast as past;
use super::nodes::*;
use super::scope::{ClosureScope, FuncScope, ModuleScope, Scope, ScopeLike};
use super::typ::{Generic, Type, Variable};
use super::{unify, Builtins, Closable, RecursionTracker, TypeError, TypeResult};

pub fn translate_module(pmodule: past::Module) -> TypeResult<Module> {
    let scope = ModuleScope::new().into_scope();

    let mut statements = vec![];
    for pstatement in pmodule.statements.into_iter() {
        let statement = match pstatement {
            past::ModuleStatement::Func(pfunc) => {
                ModuleStatement::Func(translate_func(&pfunc, scope.clone())?)
            }
            _ => unreachable!(),
        };
        statements.push(statement);
        // NOTE: Closing should already be done within the translation of every
        //   module-level statement:
        // statements.push(statement.close(&mut RecursionTracker::new(), scope.clone())?);
    }

    // Now that we've translated the whole module we can close all of the
    // statements to ensure all types are bound.
    // let mut closed_statements = vec![];
    // for statement in statements.into_iter() {
    //     closed_statements.push(statement.close(&mut RecursionTracker::new())?);
    // }

    Ok(Module { statements })
}

fn translate_func(pfunc: &past::Func, scope: Scope) -> TypeResult<Func> {
    let name = pfunc.name.name.clone();
    // The scope that the function's arguments and body will be evaluated in.
    let func_scope = FuncScope::new(Some(scope.clone())).into_scope();

    // Build the `FuncArgument` nodes ahead of time so that they have types
    // in place.
    let arguments_nodes = pfunc
        .arguments
        .iter()
        .map(|argument| FuncArgument {
            name: argument.name.clone(),
            // TODO: Support argument type definitions.
            typ: Type::new_unbound(func_scope.clone()),
        })
        .collect::<Vec<_>>();

    for argument_node in arguments_nodes.iter() {
        func_scope.add_local(&argument_node.name, argument_node.typ.clone())?;
    }
    // Build a forward declaration for the recursive call.
    let retrn = Type::new_unbound(func_scope.clone());
    let typ = Type::new_func(
        Some(name.clone()),
        arguments_nodes
            .iter()
            .map(|argument| argument.typ.clone())
            .collect::<Vec<_>>(),
        retrn.clone(),
        scope.clone(),
    );
    // Add the function to its defining scope.
    // TODO: This should be an `add_static` once functions are static.
    scope.add_local(&name, typ.clone())?;

    let body = match &pfunc.body {
        past::FuncBody::Block(block) => {
            FuncBody::Block(translate_block(block, func_scope.clone())?)
        }
    };

    let implicit_retrn = body.typ();
    unify(&retrn, &implicit_retrn, func_scope.clone())?;

    Ok(Func {
        name: name.clone(),
        arguments: arguments_nodes,
        body,
        scope: func_scope.clone(),
        typ,
    }.close(&mut RecursionTracker::new(), func_scope)?)
}

fn translate_block(pblock: &past::Block, scope: Scope) -> TypeResult<Block> {
    let mut statements = vec![];
    for pstatement in &pblock.statements {
        let statement = match pstatement {
            past::BlockStatement::CommentLine(_) => continue,
            past::BlockStatement::Func(pfunc) => {
                BlockStatement::Func(translate_func(pfunc, scope.clone())?)
            }
            past::BlockStatement::Expression(pexpression) => {
                BlockStatement::Expression(translate_expression(&pexpression, scope.clone())?)
            }
            past::BlockStatement::Var(pvar) => {
                BlockStatement::Var(translate_var(pvar, scope.clone())?)
            }
        };
        statements.push(statement);
    }
    let typ = if statements.is_empty() {
        Type::new_empty_tuple(scope)
    } else {
        statements.last().unwrap().typ()
    };
    Ok(Block {
        statements,
        span: pblock.span.clone(),
        typ,
    })
}

fn translate_var(pvar: &past::Var, scope: Scope) -> TypeResult<Var> {
    let typ = Type::new_unbound(scope.clone());
    scope.add_local(&pvar.name.name, typ.clone())?;
    let initializer = match &pvar.initializer {
        Some(expression) => {
            let initializer = translate_expression(expression, scope.clone())?;
            unify(&typ, initializer.typ(), scope)?;
            Some(initializer)
        }
        None => None,
    };
    Ok(Var {
        name: pvar.name.clone(),
        initializer,
        typ,
    })
}

fn translate_expression(pexpression: &past::Expression, scope: Scope) -> TypeResult<Expression> {
    Ok(match pexpression {
        past::Expression::Closure(pclosure) => {
            Expression::Closure(translate_closure(pclosure, scope)?)
        }
        past::Expression::Identifier(pidentifier) => {
            let name = pidentifier.name.clone();
            let resolution = scope
                .get_local(&name.name)
                .map_err(|err| err.with_span(pidentifier.name.span.clone()))?;
            let typ = resolution.typ();
            Expression::Identifier(Identifier {
                name,
                resolution,
                typ,
            })
        }
        past::Expression::Infix(pinfix) => {
            let lhs = translate_expression(&*pinfix.lhs, scope.clone())?;
            let rhs = translate_expression(&*pinfix.rhs, scope.clone())?;
            // Left- and right-hand sides must be the same in an infix operation.
            unify(lhs.typ(), rhs.typ(), scope)?;
            let typ = rhs.typ().clone();
            Expression::Infix(Infix {
                lhs: Box::new(lhs),
                op: pinfix.op.clone(),
                rhs: Box::new(rhs),
                typ,
            })
        }
        past::Expression::LiteralInt(pliteral) => {
            let class = Builtins::get("Int");
            Expression::LiteralInt(LiteralInt {
                value: pliteral.value,
                typ: Type::new_object(class, scope),
            })
        }
        past::Expression::PostfixCall(pcall) => {
            Expression::PostfixCall(translate_postfix_call(pcall, scope)?)
        }
        past::Expression::PostfixProperty(pproperty) => {
            Expression::PostfixProperty(translate_postfix_property(pproperty, scope)?)
        }
        _ => unreachable!("Cannot translate {:?}", pexpression),
    })
}

fn translate_closure(pclosure: &past::Closure, scope: Scope) -> TypeResult<Closure> {
    let closure_scope = ClosureScope::new(Some(scope.clone())).into_scope();

    let arguments_nodes = pclosure
        .arguments
        .iter()
        .map(|argument| FuncArgument {
            name: argument.name.clone(),
            typ: Type::new_unbound(closure_scope.clone()),
        })
        .collect::<Vec<_>>();

    let retrn = Type::new_unbound(closure_scope.clone());

    for argument_node in arguments_nodes.iter() {
        closure_scope.add_local(&argument_node.name, argument_node.typ.clone())?;
    }

    let body = match &*pclosure.body {
        past::ClosureBody::Block(block) => {
            ClosureBody::Block(translate_block(block, closure_scope.clone())?)
        }
        past::ClosureBody::Expression(expression) => {
            ClosureBody::Expression(translate_expression(expression, closure_scope.clone())?)
        }
    };

    let implicit_retrn = body.typ();
    unify(&retrn, implicit_retrn, closure_scope.clone())?;

    let typ = Type::new_func(
        None,
        arguments_nodes
            .iter()
            .map(|argument| argument.typ.clone())
            .collect::<Vec<_>>(),
        retrn.clone(),
        scope.clone(),
    );

    Ok(Closure {
        arguments: arguments_nodes,
        body: Box::new(body),
        scope: closure_scope.clone(),
        typ,
    }
    .close(&mut RecursionTracker::new(), closure_scope)?)
}

fn translate_postfix_call(pcall: &past::PostfixCall, scope: Scope) -> TypeResult<PostfixCall> {
    let target = translate_expression(&*pcall.target, scope.clone())?;

    let mut arguments = vec![];
    for argument in pcall.arguments.iter() {
        arguments.push(translate_expression(argument, scope.clone())?);
    }

    let retrn = {
        // If the target is already a callable then unify directly instead of
        // through a constraint.
        if let Some((target_arguments, target_retrn)) = target.typ().maybe_callable() {
            let call_arguments = arguments
                .iter()
                .map(|argument| argument.typ().clone())
                .collect::<Vec<_>>();
            if target_arguments.len() != call_arguments.len() {
                return Err(TypeError::ArgumentLengthMismatch {
                    expected: target_arguments,
                    got: call_arguments,
                });
            }
            let mut tracker = RecursionTracker::new();
            for (target_argument, call_argument) in
                target_arguments.iter().zip(call_arguments.iter())
            {
                let open_target_argument =
                    target_argument.open_duplicate(&mut tracker, scope.clone());
                unify(call_argument, &open_target_argument, scope.clone())?;
            }
            let call_retrn = Type::new_unbound(scope.clone());
            let open_target_retrn = target_retrn.open_duplicate(&mut tracker, scope.clone());
            unify(&call_retrn, &open_target_retrn, scope)?;
            call_retrn

        // Otherwise build a callable generic constraint as an intermediary
        // and unify through that.
        } else {
            // The return type of the callable.
            let retrn = Type::new_unbound(scope.clone());

            let mut generic = Generic::new(scope.clone());
            generic.add_callable_constraint(
                arguments
                    .iter()
                    .map(|argument| argument.typ().clone())
                    .collect::<Vec<_>>(),
                retrn.clone(),
            );
            // Unify to ensure target supports being called with the arguments and
            // return types.
            let intermediary = Type::new_variable(Variable::Generic {
                scope: scope.clone(),
                generic,
            });
            unify(target.typ(), &intermediary, scope)
                .map_err(|err| err.with_span(pcall.span.clone()))?;
            retrn
        }
    };

    Ok(PostfixCall {
        target: Box::new(target),
        arguments,
        typ: retrn,
    })
}

fn translate_postfix_property(
    pproperty: &past::PostfixProperty,
    scope: Scope,
) -> TypeResult<PostfixProperty> {
    let target = translate_expression(&*pproperty.target, scope.clone())?;

    // The ultimate type of getting the target's property.
    let typ = Type::new_unbound(scope.clone());

    // Set up a variable generic with a property constraint.
    let mut generic = Generic::new(scope.clone());
    generic.add_property_constraint(pproperty.property.name.clone(), typ.clone());
    // Apply unification to ensure the target supports having the
    // given property.
    let intermediary = Type::new_variable(Variable::Generic {
        scope: scope.clone(),
        generic,
    });
    unify(target.typ(), &intermediary, scope)
        .map_err(|err| err.with_span(pproperty.span.clone()))?;

    Ok(PostfixProperty {
        target: Box::new(target),
        property: pproperty.property.clone(),
        typ,
    })
}

#[cfg(test)]
mod tests {
    use super::super::super::parse_ast as past;
    use super::super::super::parser::{Location, Span, Token, Word};
    use super::super::scope::{ClosureScope, ScopeLike};
    use super::super::{Builtins, Type, TypeError, Variable};
    use super::{translate_expression, translate_func};

    fn word<S: AsRef<str>>(name: S) -> Word {
        Word {
            name: name.as_ref().to_string(),
            span: Span::unknown(),
        }
    }

    #[test]
    fn test_translate_infix() -> Result<(), TypeError> {
        // Test simple addition of two integer literals.
        let pexpression = past::Expression::Infix(past::Infix {
            lhs: Box::new(past::Expression::LiteralInt(past::LiteralInt {
                value: 1,
                span: Span::unknown(),
            })),
            op: Token::Plus(Location::unknown()),
            rhs: Box::new(past::Expression::LiteralInt(past::LiteralInt {
                value: 2,
                span: Span::unknown(),
            })),
        });
        translate_expression(&pexpression, ClosureScope::new(None).into_scope()).map(|_| ())?;

        // Add an unbound variable with an integer literal; check that the
        // variable is substituted to an integer.
        let pexpression = past::Expression::Infix(past::Infix {
            lhs: Box::new(past::Expression::Identifier(past::Identifier {
                name: word("foo"),
            })),
            op: Token::Plus(Location::unknown()),
            rhs: Box::new(past::Expression::LiteralInt(past::LiteralInt {
                value: 2,
                span: Span::unknown(),
            })),
        });
        let scope = ClosureScope::new(None).into_scope();
        let foo = Type::new_unbound(scope.clone());
        scope.add_local("foo", foo.clone())?;
        translate_expression(&pexpression, scope.clone()).map(|_| ())?;
        assert_eq!(
            foo,
            Type::new_substitute(Type::new_object(Builtins::get("Int"), scope.clone()), scope)
        );

        Ok(())
    }

    #[test]
    fn test_translate_func() -> Result<(), TypeError> {
        let pclosure = past::Closure {
            arguments: vec![],
            body: Box::new(past::ClosureBody::Block(past::Block {
                // statements: vec![past::BlockStatement::Expression(past::Expression::Infix(
                //     past::Infix {
                //         lhs: Box::new(past::Expression::Identifier(past::Identifier {
                //             name: word("bar"),
                //         })),
                //         op: Token::Plus(Location::unknown()),
                //         rhs: Box::new(past::Expression::LiteralInt(past::LiteralInt {
                //             value: 1,
                //             span: Span::unknown(),
                //         })),
                //     },
                // ))],
                statements: vec![past::BlockStatement::Expression(
                    past::Expression::Identifier(past::Identifier { name: word("bar") }),
                )],
                span: Span::unknown(),
            })),
            span: Span::unknown(),
        };

        let pfunc = past::Func {
            name: word("foo"),
            arguments: vec![word("bar")],
            body: past::FuncBody::Block(past::Block {
                // statements: vec![past::BlockStatement::Expression(past::Expression::Infix(
                //     past::Infix {
                //         lhs: Box::new(past::Expression::Identifier(past::Identifier {
                //             name: word("bar"),
                //         })),
                //         op: Token::Plus(Location::unknown()),
                //         rhs: Box::new(past::Expression::LiteralInt(past::LiteralInt {
                //             value: 1,
                //             span: Span::unknown(),
                //         })),
                //     },
                // ))],
                statements: vec![
                    past::BlockStatement::Expression(past::Expression::Closure(pclosure)),
                    past::BlockStatement::Expression(past::Expression::Infix(past::Infix {
                        lhs: Box::new(past::Expression::Identifier(past::Identifier {
                            name: word("bar"),
                        })),
                        op: Token::Plus(Location::unknown()),
                        rhs: Box::new(past::Expression::LiteralInt(past::LiteralInt {
                            value: 1,
                            span: Span::unknown(),
                        })),
                    })),
                ],
                span: Span::unknown(),
            }),
            span: Span::unknown(),
        };

        let scope = ClosureScope::new(None).into_scope();
        let func = translate_func(&pfunc, scope)?;

        println!("{:?}", func);

        Ok(())
    }
}
<|endoftext|>