<commit_before>#[macro_use(azip)]
extern crate ndarray;

#[cfg(feature = "serde")]
extern crate serde;

#[cfg(feature = "serde")]
#[macro_use]
extern crate serde_derive;

use ndarray::prelude::*;
use ndarray::Data;
use ndarray::linalg::{
    general_mat_mul,
    general_mat_vec_mul,
};

/// The parameters of recursive least squares algorithm.
///
/// This struct contains all parameters involved in a recursive
/// least squares algorithm with exponential forgetting. For a review see
/// [Haykin's Adaptive Filter Theory][http://www.isbnsearch.org/isbn/9780132671453].
/// The implementation here does not implicitly take time into account. By making a choice of the
/// forgetting factor λ < 1 and shifting down old values of the input vector manually, the user can
/// get this algorithm to behave accordingly.
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Rls<F> {

    /// The inverse forgetting factor λ^{-1}.
    inv_forgetting_factor: F,

    /// The gain vector used during the update of the inverse correlation matrix P(i) and the
    /// (tap) weight vector w(i).
    gain: Array1<F>,

    /// The *inverse correlation matrix*, initialized as P = δ^{-1} · 𝟙, with 𝟙 the unit matrix and
    /// δ an initialization factor recommended to be less than ~ 0.01 σ^2, the variance of the data
    /// sample to be analyzed (cf. [Haykin's Adaptive Filter Theory]).
    inverse_correlation: Array2<F>,

    /// The *(tap) weight vector* w(i) at time i used to produce the transversal filter's output
    /// y(i) = w(i) · u(i), where u(i) is the *(tap) input vector* at time i.
    weight: Array1<F>,

    /// The prior error used during the udpate of the inverse correlation matrix P(i) and the
    /// (tap) weight vector w(i). The prior error is calculated as the difference between the
    /// desired output and the filter output before an update.
    prior_error: F,

    // Two scratch matrices used for for update of the inverse correlation matrix.
    temp_a: Array2<F>,
    temp_b: Array2<F>,
}

impl<F: NdFloat> Rls<F> {

    /// Constructs a new Rls object with initialization factor δ and a weight vector of length n.
    pub fn new(initialization_factor: F, forgetting_factor: F, n: usize) -> Self {
        let weight = Array1::zeros(n);

        Rls::with_weight(initialization_factor, forgetting_factor, weight)
    }

    /// Constructs a new Rls object with initialization factor δ and pre-defined weight w.
    pub fn with_weight(initialization_factor: F, forgetting_factor: F, weight: Array1<F>) -> Self {
        let one = F::one();
        let zero = F::zero();

        let n = weight.len();

        let inv_forgetting_factor = one / forgetting_factor;

        let gain = Array1::zeros(n);
        let prior_error = zero;

        let mut inverse_correlation = Array2::eye(n);
        inverse_correlation *= one/initialization_factor;

        let temp_a = Array2::zeros([n,n]);
        let temp_b = Array2::zeros([n,n]);

        Rls {
            inv_forgetting_factor,
            gain,
            inverse_correlation,
            weight,
            prior_error,
            temp_a,
            temp_b,
        }
    }

    /// Performs a recursive update of inverse correlation matrix and weight vector.
    pub fn update<S>(&mut self, input: &ArrayBase<S, Ix1>, target: F)
        where S: Data<Elem = F>
    {
        let one = F::one();
        let zero = F::zero();

        // Update the gain vector.
        general_mat_vec_mul(
            one,
            &self.inverse_correlation,
            input,
            zero,
            &mut self.gain
        );
        let c = self.inv_forgetting_factor + self.gain.dot(&self.gain);
        self.gain /= c;

        // Calculate the prior error using the not yet updated tap weight.
        self.prior_error = target - self.weight.dot(&input);

        // Update the tap weight.
        self.weight.scaled_add(self.prior_error, &self.gain);

        azip!(mut row (self.temp_a.genrows_mut()), gain (&self.gain) in {
            azip!(mut row, input (input) in {
                *row = gain * input;
        })});

        general_mat_mul(one, &self.temp_a, &self.inverse_correlation, zero, &mut self.temp_b);
        self.inverse_correlation -= &self.temp_b;
        self.inverse_correlation *= self.inv_forgetting_factor;
    }
}

impl<T> Rls<T> {

    /// Returns a reference to the gain vector.
    pub fn gain_ref(&self) -> &Array1<T> {
        &self.gain
    }

    /// Returns a reference to the inverse correlation matrix.
    pub fn inverse_correlation_ref(&self) -> &Array2<T> {
        &self.inverse_correlation
    }

    /// Returns a reference to the inverse forgetting factor.
    pub fn inv_forgetting_factor_ref(&self) -> &T {
        &self.inv_forgetting_factor
    }

    /// Returns a reference to the (tap) weight vector.
    pub fn weight_ref(&self) -> &Array1<T> {
        &self.weight
    }

    /// Returns a refernce to the prior error.
    pub fn prior_error_ref(&self) -> &T {
        &self.prior_error
    }
}
<commit_msg>Derive Debug and Clone on Rls<commit_after>#[macro_use(azip)]
extern crate ndarray;

#[cfg(feature = "serde")]
extern crate serde;

#[cfg(feature = "serde")]
#[macro_use]
extern crate serde_derive;

use ndarray::prelude::*;
use ndarray::Data;
use ndarray::linalg::{
    general_mat_mul,
    general_mat_vec_mul,
};

/// The parameters of recursive least squares algorithm.
///
/// This struct contains all parameters involved in a recursive
/// least squares algorithm with exponential forgetting. For a review see
/// [Haykin's Adaptive Filter Theory][http://www.isbnsearch.org/isbn/9780132671453].
/// The implementation here does not implicitly take time into account. By making a choice of the
/// forgetting factor λ < 1 and shifting down old values of the input vector manually, the user can
/// get this algorithm to behave accordingly.
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone,Debug)]
pub struct Rls<F> {

    /// The inverse forgetting factor λ^{-1}.
    inv_forgetting_factor: F,

    /// The gain vector used during the update of the inverse correlation matrix P(i) and the
    /// (tap) weight vector w(i).
    gain: Array1<F>,

    /// The *inverse correlation matrix*, initialized as P = δ^{-1} · 𝟙, with 𝟙 the unit matrix and
    /// δ an initialization factor recommended to be less than ~ 0.01 σ^2, the variance of the data
    /// sample to be analyzed (cf. [Haykin's Adaptive Filter Theory]).
    inverse_correlation: Array2<F>,

    /// The *(tap) weight vector* w(i) at time i used to produce the transversal filter's output
    /// y(i) = w(i) · u(i), where u(i) is the *(tap) input vector* at time i.
    weight: Array1<F>,

    /// The prior error used during the udpate of the inverse correlation matrix P(i) and the
    /// (tap) weight vector w(i). The prior error is calculated as the difference between the
    /// desired output and the filter output before an update.
    prior_error: F,

    // Two scratch matrices used for for update of the inverse correlation matrix.
    temp_a: Array2<F>,
    temp_b: Array2<F>,
}

impl<F: NdFloat> Rls<F> {

    /// Constructs a new Rls object with initialization factor δ and a weight vector of length n.
    pub fn new(initialization_factor: F, forgetting_factor: F, n: usize) -> Self {
        let weight = Array1::zeros(n);

        Rls::with_weight(initialization_factor, forgetting_factor, weight)
    }

    /// Constructs a new Rls object with initialization factor δ and pre-defined weight w.
    pub fn with_weight(initialization_factor: F, forgetting_factor: F, weight: Array1<F>) -> Self {
        let one = F::one();
        let zero = F::zero();

        let n = weight.len();

        let inv_forgetting_factor = one / forgetting_factor;

        let gain = Array1::zeros(n);
        let prior_error = zero;

        let mut inverse_correlation = Array2::eye(n);
        inverse_correlation *= one/initialization_factor;

        let temp_a = Array2::zeros([n,n]);
        let temp_b = Array2::zeros([n,n]);

        Rls {
            inv_forgetting_factor,
            gain,
            inverse_correlation,
            weight,
            prior_error,
            temp_a,
            temp_b,
        }
    }

    /// Performs a recursive update of inverse correlation matrix and weight vector.
    pub fn update<S>(&mut self, input: &ArrayBase<S, Ix1>, target: F)
        where S: Data<Elem = F>
    {
        let one = F::one();
        let zero = F::zero();

        // Update the gain vector.
        general_mat_vec_mul(
            one,
            &self.inverse_correlation,
            input,
            zero,
            &mut self.gain
        );
        let c = self.inv_forgetting_factor + self.gain.dot(&self.gain);
        self.gain /= c;

        // Calculate the prior error using the not yet updated tap weight.
        self.prior_error = target - self.weight.dot(&input);

        // Update the tap weight.
        self.weight.scaled_add(self.prior_error, &self.gain);

        azip!(mut row (self.temp_a.genrows_mut()), gain (&self.gain) in {
            azip!(mut row, input (input) in {
                *row = gain * input;
        })});

        general_mat_mul(one, &self.temp_a, &self.inverse_correlation, zero, &mut self.temp_b);
        self.inverse_correlation -= &self.temp_b;
        self.inverse_correlation *= self.inv_forgetting_factor;
    }
}

impl<T> Rls<T> {

    /// Returns a reference to the gain vector.
    pub fn gain_ref(&self) -> &Array1<T> {
        &self.gain
    }

    /// Returns a reference to the inverse correlation matrix.
    pub fn inverse_correlation_ref(&self) -> &Array2<T> {
        &self.inverse_correlation
    }

    /// Returns a reference to the inverse forgetting factor.
    pub fn inv_forgetting_factor_ref(&self) -> &T {
        &self.inv_forgetting_factor
    }

    /// Returns a reference to the (tap) weight vector.
    pub fn weight_ref(&self) -> &Array1<T> {
        &self.weight
    }

    /// Returns a refernce to the prior error.
    pub fn prior_error_ref(&self) -> &T {
        &self.prior_error
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate pest;
#[macro_use]
extern crate pest_derive;

#[cfg(debug_assertions)]
const _GRAMMER: &'static str = include_str!("grammer.pest");

#[derive(Parser)]
#[grammar = "grammer.pest"]
struct TurtleParser;

#[test]
fn predicate_test() {
    parses_to! {
        parser: TurtleParser,
        input: "<http://www.example.com>",
        rule: Rule::predicate,
        tokens: [
            predicate(0, 24, [
                iri(0, 24, [
                    iriref(0, 24)
                ])
            ])
        ]
    };
}

#[test]
fn object_test() {
    parses_to! {
        parser: TurtleParser,
        input: "<http://www.example.com>",
        rule: Rule::object,
        tokens: [
            object(0, 24, [
                iri(0, 24, [
                    iriref(0, 24)
                ])
            ])
        ]
    };

    parses_to! {
        parser: TurtleParser,
        input: "_:blank",
        rule: Rule::object,
        tokens: [
            object(0, 7, [
                blank_node(0, 7, [
                    blank_node_label(0, 7)
                ])
            ])
        ]
    };

    parses_to! {
        parser: TurtleParser,
        input: "(<http://www.example.com> ([ ]))",
        rule: Rule::object,
        tokens: [
            object(0, 32, [
                collection(0, 32, [
                    object(1, 25, [
                        iri(1, 25, [ iriref(1, 25) ])
                    ]),
                    object(26, 31, [
                        collection(26, 31, [
                            object(27, 30, [ blank_node(27, 30, [ anon(27, 30) ]) ])
                        ])
                    ])
                ])
            ])
        ]
    };

    parses_to! {
        parser: TurtleParser,
        input: "[<http://verb> <http://object1>, <http://object2>; a <http://object3>]",
        rule: Rule::object,
        tokens: [
            object(0, 70, [
                blank_node_property_list(0, 70, [
                    predicate_object_list(1, 69, [
                        verb(1, 14, [ predicate(1, 14, [ iri(1, 14, [ iriref(1, 14)]) ]) ]),
                        object_list(15, 49, [
                            object(15, 31, [ iri(15, 31, [ iriref(15, 31)]) ]),
                            object(33, 49, [ iri(33, 49, [ iriref(33, 49)]) ])
                        ]),
                        verb(51, 52),
                        object_list(53, 69, [
                            object(53, 69, [ iri(53, 69, [ iriref(53, 69)]) ])
                        ])
                    ])
                ])
            ])
        ]
    };

    parses_to! {
        parser: TurtleParser,
        input: r#""test"@en"#,
        rule: Rule::object,
        tokens: [
            object(0, 9, [
                literal(0, 9, [
                    rdf_literal(0, 9, [
                        string(0, 6, [
                            string_literal_quote(0, 6)
                        ]),
                        langtag(6, 9)
                    ])
                ])
            ])
        ]
    };
}

#[test]
fn literal_test() {
    parses_to! {
        parser: TurtleParser,
        input: r#""test"@en"#,
        rule: Rule::literal,
        tokens: [
            literal(0, 9, [
                rdf_literal(0, 9, [
                    string(0, 6, [
                        string_literal_quote(0, 6)
                    ]),
                    langtag(6, 9)
                ])
            ])
        ]
    };

    parses_to! {
        parser: TurtleParser,
        input: "+10",
        rule: Rule::literal,
        tokens: [
            literal(0, 3, [
                numeric_literal(0, 3, [
                    integer(0, 3)
                ])
            ])
        ]
    };

    parses_to! {
        parser: TurtleParser,
        input: "true",
        rule: Rule::literal,
        tokens: [
            literal(0, 4, [
                boolean_literal(0, 4)
            ])
        ]
    };
}

#[test]
fn blank_node_property_list_test() {
    parses_to! {
        parser: TurtleParser,
        input: "[<http://verb> <http://object1>, <http://object2>; a <http://object3>]",
        rule: Rule::blank_node_property_list,
        tokens: [
            blank_node_property_list(0, 70, [
                predicate_object_list(1, 69, [
                    verb(1, 14, [ predicate(1, 14, [ iri(1, 14, [ iriref(1, 14)]) ]) ]),
                    object_list(15, 49, [
                        object(15, 31, [ iri(15, 31, [ iriref(15, 31)]) ]),
                        object(33, 49, [ iri(33, 49, [ iriref(33, 49)]) ])
                    ]),
                    verb(51, 52),
                    object_list(53, 69, [
                        object(53, 69, [ iri(53, 69, [ iriref(53, 69)]) ])
                    ])
                ])
            ])
        ]
    };
}

#[test]
fn collection_test() {
    parses_to! {
        parser: TurtleParser,
        input: "(<http://www.example.com> ([ ]))",
        rule: Rule::collection,
        tokens: [
            collection(0, 32, [
                object(1, 25, [
                    iri(1, 25, [ iriref(1, 25) ])
                ]),
                object(26, 31, [
                    collection(26, 31, [
                        object(27, 30, [ blank_node(27, 30, [ anon(27, 30) ]) ])
                    ])
                ])
            ])
        ]
    };
}

#[test]
fn numeric_literal_test() {
    parses_to! {
        parser: TurtleParser,
        input: "+10",
        rule: Rule::numeric_literal,
        tokens: [
            numeric_literal(0, 3, [
                integer(0, 3)
            ])
        ]
    };

    parses_to! {
        parser: TurtleParser,
        input: "+1.0",
        rule: Rule::numeric_literal,
        tokens: [
            numeric_literal(0, 4, [
                decimal(0, 4)
            ])
        ]
    };

    parses_to! {
        parser: TurtleParser,
        input: "+1.0e-9",
        rule: Rule::numeric_literal,
        tokens: [
            numeric_literal(0, 7, [
                double(0, 7)
            ])
        ]
    };
}

#[test]
fn rdf_literal_test() {
    parses_to! {
        parser: TurtleParser,
        input: r#""test"@en"#,
        rule: Rule::rdf_literal,
        tokens: [
            rdf_literal(0, 9, [
                string(0, 6, [
                    string_literal_quote(0, 6)
                ]),
                langtag(6, 9)
            ])
        ]
    };

    parses_to! {
        parser: TurtleParser,
        input: r#""test"^^<http://www.example.com>"#,
        rule: Rule::rdf_literal,
        tokens: [
            rdf_literal(0, 32, [
                string(0, 6, [
                    string_literal_quote(0, 6)
                ]),
                iri(8, 32, [
                    iriref(8, 32)
                ])
            ])
        ]
    };
}

#[test]
fn boolean_literal_test() {
    parses_to! {
        parser: TurtleParser,
        input: "true",
        rule: Rule::boolean_literal,
        tokens: [
            boolean_literal(0, 4)
        ]
    };

    parses_to! {
        parser: TurtleParser,
        input: "false",
        rule: Rule::boolean_literal,
        tokens: [
            boolean_literal(0, 5)
        ]
    };
}

#[test]
fn string_test() {
    parses_to! {
        parser: TurtleParser,
        input: r#""test""#,
        rule: Rule::string,
        tokens: [
            string(0, 6, [
                string_literal_quote(0, 6)
            ])
        ]
    };

    parses_to! {
        parser: TurtleParser,
        input: r#"'test'"#,
        rule: Rule::string,
        tokens: [
            string(0, 6, [
                string_literal_single_quote(0, 6)
            ])
        ]
    };

    parses_to! {
        parser: TurtleParser,
        input: r#""""test"test""""#,
        rule: Rule::string,
        tokens: [
            string(0, 15, [
                string_literal_long_quote(0, 15)
            ])
        ]
    };

    parses_to! {
        parser: TurtleParser,
        input: r#"'''test'test'''"#,
        rule: Rule::string,
        tokens: [
            string(0, 15, [
                string_literal_long_single_quote(0, 15)
            ])
        ]
    };
}

#[test]
fn iri_test() {
    parses_to! {
        parser: TurtleParser,
        input: "<http://www.example.com>",
        rule: Rule::iri,
        tokens: [
            iri(0, 24, [
                iriref(0, 24)
            ])
        ]
    };

    parses_to! {
        parser: TurtleParser,
        input: "foaf:Person",
        rule: Rule::iri,
        tokens: [
            iri(0, 11, [
                prefixed_name(0, 11, [
                    pname_ln(0, 11)
                ])
            ])
        ]
    };
}

#[test]
fn prefixd_name_test() {
    parses_to! {
        parser: TurtleParser,
        input: "foaf:",
        rule: Rule::prefixed_name,
        tokens: [
            prefixed_name(0, 5, [
                pname_ns(0, 5)
            ])
        ]
    };

    parses_to! {
        parser: TurtleParser,
        input: "foaf:Person",
        rule: Rule::prefixed_name,
        tokens: [
            prefixed_name(0, 11, [
                pname_ln(0, 11)
            ])
        ]
    };
}

#[test]
fn blank_node_test() {
    parses_to! {
        parser: TurtleParser,
        input: "_:blank",
        rule: Rule::blank_node,
        tokens: [
            blank_node(0, 7, [
                blank_node_label(0, 7)
            ])
        ]
    };

    parses_to! {
        parser: TurtleParser,
        input: "[     ]",
        rule: Rule::blank_node,
        tokens: [
            blank_node(0, 7, [
                anon(0, 7)
            ])
        ]
    };
}
<commit_msg>Add turtle_doc_test, subject_test, verb_test, predicate_test<commit_after>#[macro_use]
extern crate pest;
#[macro_use]
extern crate pest_derive;

#[cfg(debug_assertions)]
const _GRAMMER: &'static str = include_str!("grammer.pest");

#[derive(Parser)]
#[grammar = "grammer.pest"]
struct TurtleParser;

#[test]
fn turtle_doc_test() {
    parses_to! {
        parser: TurtleParser,
        input: "@base <http://example.org/> .
                @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
                @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
                @prefix foaf: <http://xmlns.com/foaf/0.1/> .
                @prefix rel: <http://www.perceive.net/schemas/relationship/> .

                <#green-goblin>
                    rel:enemyOf <#spiderman> ;
                    a foaf:Person ;    # in the context of the Marvel universe
                    foaf:name \"Green Goblin\" .
                <#spiderman>
                    rel:enemyOf <#green-goblin> ;
                    a foaf:Person ;
                    foaf:name \"Spiderman\", \"Человек-паук\"@ru .",
        rule: Rule::turtle_doc,
        tokens: [
            turtle_doc(0, 713, [
                statement(0, 29, [
                    directive(0, 29, [
                        base(0, 29, [
                            iriref(6, 27)
                        ])
                    ])
                ]),
                statement(46, 106, [
                    directive(46, 106, [
                        prefix_id(46, 106, [
                            pname_ns(54, 58),
                            iriref(59, 104)
                        ])
                    ])
                ]),
                statement(123, 178, [
                    directive(123, 178, [
                        prefix_id(123, 178, [
                            pname_ns(131, 136),
                            iriref(137, 176)
                        ])
                    ])
                ]),
                statement(195, 239, [
                    directive(195, 239, [
                        prefix_id(195, 239, [
                            pname_ns(203, 208),
                            iriref(209, 237)
                        ])
                    ])
                ]),
                statement(256, 318, [
                    directive(256, 318, [
                        prefix_id(256, 318, [
                            pname_ns(264, 268),
                            iriref(269, 316)
                        ])
                    ])
                ]),
                statement(336, 524, [
                    triples(336, 523, [
                        subject(336, 351, [
                            iri(336, 351, [
                                iriref(336, 351)
                            ])
                        ]),
                        predicate_object_list(372, 523, [
                            verb(372, 383, [
                                predicate(372, 383, [
                                    iri(372, 383, [
                                        prefixed_name(372, 383, [
                                            pname_ln(372, 383)
                                        ])
                                    ])
                                ])
                            ]),
                            object_list(384, 397, [
                                object(384, 396, [
                                    iri(384, 396, [
                                        iriref(384, 396)
                                    ])
                                ])
                            ]),
                            verb(419, 420),
                            object_list(421, 433, [
                                object(421, 432, [
                                    iri(421, 432, [
                                        prefixed_name(421, 432, [
                                            pname_ln(421, 432)
                                        ])
                                    ])
                                ])
                            ]),
                            verb(498, 507, [
                                predicate(498, 507, [
                                    iri(498, 507, [
                                        prefixed_name(498, 507, [
                                            pname_ln(498, 507)
                                        ])
                                    ])
                                ])
                            ]),
                            object_list(508, 523, [
                                object(508, 523, [
                                    literal(508, 523, [
                                        rdf_literal(508, 523, [
                                            string(508, 522, [
                                                string_literal_quote(508, 522)
                                            ])
                                        ])
                                    ])
                                ])
                            ])
                        ])
                    ])
                ]),
                statement(541, 713, [
                    triples(541, 711, [
                        subject(541, 553, [
                            iri(541, 553, [
                                iriref(541, 553)
                            ])
                        ]),
                        predicate_object_list(574, 711, [
                            verb(574, 585, [
                                predicate(574, 585, [
                                    iri(574, 585, [
                                        prefixed_name(574, 585, [
                                            pname_ln(574, 585)
                                        ])
                                    ])
                                ])
                            ]),
                            object_list(586, 602, [
                                object(586, 601, [
                                    iri(586, 601, [
                                        iriref(586, 601)
                                    ])
                                ])
                            ]),
                            verb(624, 625),
                            object_list(626, 638, [
                                object(626, 637, [
                                    iri(626, 637, [
                                        prefixed_name(626, 637, [
                                            pname_ln(626, 637)
                                        ])
                                    ])
                                ])
                            ]),
                            verb(660, 669, [
                                predicate(660, 669, [
                                    iri(660, 669, [
                                        prefixed_name(660, 669, [
                                            pname_ln(660, 669)
                                        ])
                                    ])
                                ])
                            ]),
                            object_list(670, 711, [
                                object(670, 681, [
                                    literal(670, 681, [
                                        rdf_literal(670, 681, [
                                            string(670, 681, [
                                                string_literal_quote(670, 681)
                                            ])
                                        ])
                                    ])
                                ]),
                                object(683, 711, [
                                    literal(683, 711, [
                                        rdf_literal(683, 711, [
                                            string(683, 708, [
                                                string_literal_quote(683, 708)
                                            ]),
                                            langtag(708, 711)
                                        ])
                                    ])
                                ])
                            ])
                        ])
                    ])
                ])
            ])
        ]
    };
}

#[test]
fn verb_test() {
    parses_to! {
        parser: TurtleParser,
        input: "<http://www.example.com>",
        rule: Rule::verb,
        tokens: [
            verb(0, 24, [
                predicate(0, 24, [
                    iri(0, 24, [
                        iriref(0, 24)
                    ])
                ])
            ])
        ]
    };
}

#[test]
fn subject_test() {
    parses_to! {
        parser: TurtleParser,
        input: "<http://www.example.com>",
        rule: Rule::subject,
        tokens: [
            subject(0, 24, [
                iri(0, 24, [
                    iriref(0, 24)
                ])
            ])
        ]
    };

    parses_to! {
        parser: TurtleParser,
        input: "_:blank",
        rule: Rule::subject,
        tokens: [
            subject(0, 7, [
                blank_node(0, 7, [
                    blank_node_label(0, 7)
                ])
            ])
        ]
    };

    parses_to! {
        parser: TurtleParser,
        input: "(<http://www.example.com> ([ ]))",
        rule: Rule::subject,
        tokens: [
            subject(0, 32, [
                collection(0, 32, [
                    object(1, 25, [
                        iri(1, 25, [ iriref(1, 25) ])
                    ]),
                    object(26, 31, [
                        collection(26, 31, [
                            object(27, 30, [ blank_node(27, 30, [ anon(27, 30) ]) ])
                        ])
                    ])
                ])
            ])
        ]
    };
}

#[test]
fn predicate_test() {
    parses_to! {
        parser: TurtleParser,
        input: "<http://www.example.com>",
        rule: Rule::predicate,
        tokens: [
            predicate(0, 24, [
                iri(0, 24, [
                    iriref(0, 24)
                ])
            ])
        ]
    };
}

#[test]
fn object_test() {
    parses_to! {
        parser: TurtleParser,
        input: "<http://www.example.com>",
        rule: Rule::object,
        tokens: [
            object(0, 24, [
                iri(0, 24, [
                    iriref(0, 24)
                ])
            ])
        ]
    };

    parses_to! {
        parser: TurtleParser,
        input: "_:blank",
        rule: Rule::object,
        tokens: [
            object(0, 7, [
                blank_node(0, 7, [
                    blank_node_label(0, 7)
                ])
            ])
        ]
    };

    parses_to! {
        parser: TurtleParser,
        input: "(<http://www.example.com> ([ ]))",
        rule: Rule::object,
        tokens: [
            object(0, 32, [
                collection(0, 32, [
                    object(1, 25, [
                        iri(1, 25, [ iriref(1, 25) ])
                    ]),
                    object(26, 31, [
                        collection(26, 31, [
                            object(27, 30, [ blank_node(27, 30, [ anon(27, 30) ]) ])
                        ])
                    ])
                ])
            ])
        ]
    };

    parses_to! {
        parser: TurtleParser,
        input: "[<http://verb> <http://object1>, <http://object2>; a <http://object3>]",
        rule: Rule::object,
        tokens: [
            object(0, 70, [
                blank_node_property_list(0, 70, [
                    predicate_object_list(1, 69, [
                        verb(1, 14, [ predicate(1, 14, [ iri(1, 14, [ iriref(1, 14)]) ]) ]),
                        object_list(15, 49, [
                            object(15, 31, [ iri(15, 31, [ iriref(15, 31)]) ]),
                            object(33, 49, [ iri(33, 49, [ iriref(33, 49)]) ])
                        ]),
                        verb(51, 52),
                        object_list(53, 69, [
                            object(53, 69, [ iri(53, 69, [ iriref(53, 69)]) ])
                        ])
                    ])
                ])
            ])
        ]
    };

    parses_to! {
        parser: TurtleParser,
        input: r#""test"@en"#,
        rule: Rule::object,
        tokens: [
            object(0, 9, [
                literal(0, 9, [
                    rdf_literal(0, 9, [
                        string(0, 6, [
                            string_literal_quote(0, 6)
                        ]),
                        langtag(6, 9)
                    ])
                ])
            ])
        ]
    };
}

#[test]
fn literal_test() {
    parses_to! {
        parser: TurtleParser,
        input: r#""test"@en"#,
        rule: Rule::literal,
        tokens: [
            literal(0, 9, [
                rdf_literal(0, 9, [
                    string(0, 6, [
                        string_literal_quote(0, 6)
                    ]),
                    langtag(6, 9)
                ])
            ])
        ]
    };

    parses_to! {
        parser: TurtleParser,
        input: "+10",
        rule: Rule::literal,
        tokens: [
            literal(0, 3, [
                numeric_literal(0, 3, [
                    integer(0, 3)
                ])
            ])
        ]
    };

    parses_to! {
        parser: TurtleParser,
        input: "true",
        rule: Rule::literal,
        tokens: [
            literal(0, 4, [
                boolean_literal(0, 4)
            ])
        ]
    };
}

#[test]
fn blank_node_property_list_test() {
    parses_to! {
        parser: TurtleParser,
        input: "[<http://verb> <http://object1>, <http://object2>; a <http://object3>]",
        rule: Rule::blank_node_property_list,
        tokens: [
            blank_node_property_list(0, 70, [
                predicate_object_list(1, 69, [
                    verb(1, 14, [ predicate(1, 14, [ iri(1, 14, [ iriref(1, 14)]) ]) ]),
                    object_list(15, 49, [
                        object(15, 31, [ iri(15, 31, [ iriref(15, 31)]) ]),
                        object(33, 49, [ iri(33, 49, [ iriref(33, 49)]) ])
                    ]),
                    verb(51, 52),
                    object_list(53, 69, [
                        object(53, 69, [ iri(53, 69, [ iriref(53, 69)]) ])
                    ])
                ])
            ])
        ]
    };
}

#[test]
fn collection_test() {
    parses_to! {
        parser: TurtleParser,
        input: "(<http://www.example.com> ([ ]))",
        rule: Rule::collection,
        tokens: [
            collection(0, 32, [
                object(1, 25, [
                    iri(1, 25, [ iriref(1, 25) ])
                ]),
                object(26, 31, [
                    collection(26, 31, [
                        object(27, 30, [ blank_node(27, 30, [ anon(27, 30) ]) ])
                    ])
                ])
            ])
        ]
    };
}

#[test]
fn numeric_literal_test() {
    parses_to! {
        parser: TurtleParser,
        input: "+10",
        rule: Rule::numeric_literal,
        tokens: [
            numeric_literal(0, 3, [
                integer(0, 3)
            ])
        ]
    };

    parses_to! {
        parser: TurtleParser,
        input: "+1.0",
        rule: Rule::numeric_literal,
        tokens: [
            numeric_literal(0, 4, [
                decimal(0, 4)
            ])
        ]
    };

    parses_to! {
        parser: TurtleParser,
        input: "+1.0e-9",
        rule: Rule::numeric_literal,
        tokens: [
            numeric_literal(0, 7, [
                double(0, 7)
            ])
        ]
    };
}

#[test]
fn rdf_literal_test() {
    parses_to! {
        parser: TurtleParser,
        input: r#""test"@en"#,
        rule: Rule::rdf_literal,
        tokens: [
            rdf_literal(0, 9, [
                string(0, 6, [
                    string_literal_quote(0, 6)
                ]),
                langtag(6, 9)
            ])
        ]
    };

    parses_to! {
        parser: TurtleParser,
        input: r#""test"^^<http://www.example.com>"#,
        rule: Rule::rdf_literal,
        tokens: [
            rdf_literal(0, 32, [
                string(0, 6, [
                    string_literal_quote(0, 6)
                ]),
                iri(8, 32, [
                    iriref(8, 32)
                ])
            ])
        ]
    };
}

#[test]
fn boolean_literal_test() {
    parses_to! {
        parser: TurtleParser,
        input: "true",
        rule: Rule::boolean_literal,
        tokens: [
            boolean_literal(0, 4)
        ]
    };

    parses_to! {
        parser: TurtleParser,
        input: "false",
        rule: Rule::boolean_literal,
        tokens: [
            boolean_literal(0, 5)
        ]
    };
}

#[test]
fn string_test() {
    parses_to! {
        parser: TurtleParser,
        input: r#""test""#,
        rule: Rule::string,
        tokens: [
            string(0, 6, [
                string_literal_quote(0, 6)
            ])
        ]
    };

    parses_to! {
        parser: TurtleParser,
        input: r#"'test'"#,
        rule: Rule::string,
        tokens: [
            string(0, 6, [
                string_literal_single_quote(0, 6)
            ])
        ]
    };

    parses_to! {
        parser: TurtleParser,
        input: r#""""test"test""""#,
        rule: Rule::string,
        tokens: [
            string(0, 15, [
                string_literal_long_quote(0, 15)
            ])
        ]
    };

    parses_to! {
        parser: TurtleParser,
        input: r#"'''test'test'''"#,
        rule: Rule::string,
        tokens: [
            string(0, 15, [
                string_literal_long_single_quote(0, 15)
            ])
        ]
    };
}

#[test]
fn iri_test() {
    parses_to! {
        parser: TurtleParser,
        input: "<http://www.example.com>",
        rule: Rule::iri,
        tokens: [
            iri(0, 24, [
                iriref(0, 24)
            ])
        ]
    };

    parses_to! {
        parser: TurtleParser,
        input: "foaf:Person",
        rule: Rule::iri,
        tokens: [
            iri(0, 11, [
                prefixed_name(0, 11, [
                    pname_ln(0, 11)
                ])
            ])
        ]
    };
}

#[test]
fn prefixd_name_test() {
    parses_to! {
        parser: TurtleParser,
        input: "foaf:",
        rule: Rule::prefixed_name,
        tokens: [
            prefixed_name(0, 5, [
                pname_ns(0, 5)
            ])
        ]
    };

    parses_to! {
        parser: TurtleParser,
        input: "foaf:Person",
        rule: Rule::prefixed_name,
        tokens: [
            prefixed_name(0, 11, [
                pname_ln(0, 11)
            ])
        ]
    };
}

#[test]
fn blank_node_test() {
    parses_to! {
        parser: TurtleParser,
        input: "_:blank",
        rule: Rule::blank_node,
        tokens: [
            blank_node(0, 7, [
                blank_node_label(0, 7)
            ])
        ]
    };

    parses_to! {
        parser: TurtleParser,
        input: "[     ]",
        rule: Rule::blank_node,
        tokens: [
            blank_node(0, 7, [
                anon(0, 7)
            ])
        ]
    };
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate string_cache;
extern crate html5ever;
extern crate unicode_width;

use std::io;
use std::io::Write;
use std::cmp::max;
use html5ever::{parse_document};
use html5ever::driver::ParseOpts;
use html5ever::tree_builder::TreeBuilderOpts;
use html5ever::rcdom::{RcDom,Handle,Text,Element,Document,Comment};
use html5ever::tendril::TendrilSink;
use unicode_width::{UnicodeWidthStr,UnicodeWidthChar};

/// A dummy writer which does nothing
struct Discard {}
impl Write for Discard {
    fn write(&mut self, bytes: &[u8]) -> std::result::Result<usize, io::Error> { Ok(bytes.len()) }
    fn flush(&mut self) -> std::result::Result<(), io::Error> { Ok(()) }
}

fn wrap_text(text: &str, width: usize) -> String {
    let mut result = String::new();
    let mut xpos = 0usize;
    for word in text.split_whitespace() {
        println!("First word: <<{:?}>>", word);
        if width <= (xpos + 1) {
            result.push('\n');
            xpos = 0;
        }
        let space_left = width - xpos - 1;
        let word_width = UnicodeWidthStr::width(word);
        if word_width <= space_left {
            /* It fits; no problem.  Add a space if not at the
             * start of line.*/
            if xpos > 0 {
                result.push(' ');
                xpos += 1;
            }
            result.push_str(word);
            xpos += word_width;
            continue;
        }

        /* It doesn't fit.  If we're not at the start of the line,
         * then go to a new line. */
        if xpos > 0 {
            result.push('\n');
            xpos = 0;
        }

        /* We're now at the start of a line. */
        if word_width > width {
            /* It doesn't fit at all on the line, so break it. */
            for c in word.chars() {
                let c_width = UnicodeWidthChar::width(c).unwrap();
                if c_width + xpos > width {
                    /* Break here */
                    result.push('\n');
                    xpos = 0;
                }
                /* This might happen with really narrow spaces... */
                assert!(c_width <= width);

                result.push(c);
                xpos += c_width;
            }
        } else {
            result.push_str(word);
            xpos += word_width;
        }
    }
    /* Terminate with a newline if needed. */
    if xpos > 0 {
        result.push('\n');
    }
    result
}

fn get_text(handle: Handle) -> String {
    let node = handle.borrow();
    let mut result = String::new();
    if let Text(ref tstr) = node.node {
        result.push_str(&tstr);
    } else {
        for child in node.children.iter() {
            result.push_str(&get_text(child.clone()));
        }
    }
    result
}

fn get_wrapped_text(handle: Handle, width: usize) -> String {
    let text = get_text(handle);
    wrap_text(&text, width)
}

fn dom_to_string<T:Write>(handle: Handle, err_out: &mut T, width: usize) -> String {
    let node = handle.borrow();
    let mut result = String::new();
    match node.node {
        Document => {},
        Element(ref name, _, _) => {
            match *name {
                qualname!(html, "html") |
                qualname!(html, "div") |
                qualname!(html, "span") |
                qualname!(html, "body") => {
                    /* process children, but don't add anything */
                },
                qualname!(html, "link") |
                qualname!(html, "meta") |
                qualname!(html, "hr") |
                qualname!(html, "head") => {
                    /* Ignore the head and its children */
                    return result;
                },
                qualname!(html, "h1") |
                qualname!(html, "h2") |
                qualname!(html, "h3") |
                qualname!(html, "h4") |
                qualname!(html, "p") => {
                    return get_wrapped_text(handle.clone(), width) + "\n";
                },
                qualname!(html, "br") => {
                    result.push('\n');
                }
                qualname!(html, "table") => return table_to_string(handle.clone(), err_out, width),
                _ => {
                    write!(err_out, "Unhandled element: {:?}\n", name.local).unwrap();
                },
            }
          },
        Text(ref tstr) => {
            return wrap_text(tstr, width);
        }
        Comment(_) => {},
        _ => { write!(err_out, "Unhandled: {:?}\n", node).unwrap(); },
    }
    for child in node.children.iter() {
        result.push_str(&dom_to_string(child.clone(), err_out, width));
    }
    result
}

#[derive(Debug)]
struct TableCell {
    colspan: usize,
    content: Handle,
}

#[derive(Debug)]
struct TableRow {
    cells: Vec<TableCell>,
}

#[derive(Debug)]
struct Table {
    rows: Vec<TableRow>,
}

impl Table {
    pub fn new() -> Table {
        Table{ rows: Vec::new() }
    }
    pub fn push(&mut self, row: TableRow) {
        self.rows.push(row);
    }
    pub fn rows(&self) -> std::slice::Iter<TableRow> {
        self.rows.iter()
    }
}

impl TableRow {
    pub fn new() -> TableRow {
        TableRow{ cells: Vec::new() }
    }
    pub fn push(&mut self, cell: TableCell) {
        self.cells.push(cell);
    }
    pub fn cells(&self) -> std::slice::Iter<TableCell> {
        self.cells.iter()
    }
    pub fn num_cells(&self) -> usize {
        self.cells.len()
    }
}

impl TableCell {
    pub fn new(s: Handle) -> TableCell {
        if let Element(_, _, ref attrs) = s.borrow().node {
            let mut colspan = 1;
            for attr in attrs {
                if &attr.name.local == "colspan" {
                    let v:&str = &*attr.value;
                    colspan = v.parse().unwrap_or(1);
                } else {
                    //println!("Attr: {:?}", attr);
                }
            }
            TableCell{ content: s.clone(), colspan: colspan }
        } else {
            panic!("TableCell::new received a non-Element");
        }
    }
    pub fn render<T:Write>(&self, width: usize, err_out: &mut T) -> String
    {
        dom_to_string(self.content.clone(), err_out, width)
    }
}

fn handle_td(handle: Handle) -> TableCell {
    TableCell::new(handle)
}

fn handle_tr<T:Write>(handle: Handle, _: &mut T) -> TableRow {
    let node = handle.borrow();

    let mut row = TableRow::new();

    for child in node.children.iter() {
        match child.borrow().node {
            Element(ref name, _, _) => {
                match *name {
                    qualname!(html, "td") => {
                        row.push(handle_td(child.clone()));
                    },
                    _ => println!("  [[tr child: {:?}]]", name),
                }
            },
            Comment(_) => {},
            _ => { /*result.push_str(&format!("Unhandled in table: {:?}\n", node));*/ },
        }
    }

    row
}

fn handle_tbody<T:Write>(handle: Handle, err_out: &mut T, width: usize) -> String {
    let node = handle.borrow();

    let mut table = Table::new();

    for child in node.children.iter() {
        match child.borrow().node {
            Element(ref name, _, _) => {
                match *name {
                    qualname!(html, "tr") => {
                        table.push(handle_tr(child.clone(), err_out));
                    },
                    _ => println!("  [[tbody child: {:?}]]", name),
                }
            },
            Comment(_) => {},
            _ => { /*result.push_str(&format!("Unhandled in table: {:?}\n", node));*/ },
        }
    }

    /* Now lay out the table.  Use the simple option of giving each column
     * same width.  TODO: be cleverer, and handle multi-width cells, etc. */
    let num_columns = table.rows().map(|r| r.num_cells()).max().unwrap();

    // Allow for | between columns.
    let col_width = (width - (num_columns-1))/num_columns;

    let mut result = String::new();

    /* Heuristic: scale the column widths according to how much content there is. */
    let test_col_width = 1000;  // Render width for measurement; shouldn't make much difference.
    let min_width = 5;
    let mut col_sizes = vec![0usize; num_columns];

    for row in table.rows() {
        for (colno, cell) in row.cells().enumerate() {
            let celldata = cell.render(test_col_width, &mut Discard{});
            col_sizes[colno] += celldata.len();
        }
    }
    let tot_size: usize = col_sizes.iter().sum();
    let mut col_widths:Vec<usize> = col_sizes.iter()
                                         .map(|sz| {
                                             if *sz == 0 {
                                                 0
                                             } else {
                                                 max(sz * width / tot_size, min_width)
                                             }
                                          }).collect();
    /* The minimums may have put the total width too high */
    while col_widths.iter().cloned().sum::<usize>() > width {
        let (i, _) = col_widths.iter().cloned().enumerate().max_by_key(|k| k.1).unwrap();
        col_widths[i] -= 1;
    }

    let mut rowline = String::new();
    for width in col_widths.iter().cloned().filter(|w:&usize| *w > 0) {
        rowline.push_str(&(0..(width-1)).map(|_| '-').collect::<String>());
        rowline.push('+');
    }
    if rowline.len() > 0 {
        rowline.pop().unwrap();  // Remove the last '+'.
    }
    result.push_str(&rowline);
    result.push('\n');

    for row in table.rows() {
        let (used_widths, formatted_cells): (Vec<usize>, Vec<String>) = row.cells()
                                              .enumerate()
                                              .flat_map(|(colno, cell)| {
                                                   let col_width = col_widths[colno];
                                                   if col_width > 0 {
                                                       Some((col_width, cell.render(col_width-1, err_out)))
                                                   } else {
                                                       None
                                                   }
                                               })
                                              .unzip();
        let line_sets: Vec<Vec<&str>> = formatted_cells.iter()
                                                       .map(|s| s.lines()
                                                                 .map(|line| line.trim_right())
                                                                 .collect())
                                                       .collect();
        let cell_height = line_sets.iter()
                                   .map(|v| v.len())
                                   .max().unwrap_or(0);
        for i in 0..cell_height {
            for (cellno, ls) in line_sets.iter().enumerate() {
                result.push_str(&format!("{: <width$}", ls.get(i).cloned().unwrap_or(""), width = used_widths[cellno]-1));
                if cellno == line_sets.len()-1 {
                    result.push('\n')
                } else {
                    result.push('|')
                }
            }
        }
        if cell_height > 0 {
            result.push_str(&rowline);
            result.push('\n');
        }
    }

    result
}

fn table_to_string<T:Write>(handle: Handle, err_out: &mut T, width: usize) -> String {
    let node = handle.borrow();
    let result = String::new();

    for child in node.children.iter() {
        match child.borrow().node {
            Element(ref name, _, _) => {
                match *name {
                    qualname!(html, "tbody") => return handle_tbody(child.clone(), err_out, width),
                    _ => { writeln!(err_out, "  [[table child: {:?}]]", name).unwrap();},
                }
            },
            Comment(_) => {},
            _ => { /*result.push_str(&format!("Unhandled in table: {:?}\n", node));*/ },
        }
    }

    result
}

pub fn from_read<R>(mut input: R, width: usize) -> String where R: io::Read {
    let opts = ParseOpts {
        tree_builder: TreeBuilderOpts {
            drop_doctype: true,
            ..Default::default()
        },
        ..Default::default()
    };
    let dom = parse_document(RcDom::default(), opts)
                   .from_utf8()
                   .read_from(&mut input)
                   .unwrap();

    dom_to_string(dom.document, &mut io::stderr(), width)
}

#[cfg(test)]
mod tests {
    use super::{from_read,wrap_text};
    #[test]
    fn test_table() {
        assert_eq!(from_read(&br##"

           1
           2
           3

"##[..], 12), r#"---+---+---
1  |2  |3
---+---+---
"#);
     }

     #[test]
     fn test_para() {
        assert_eq!(from_read(&b"
Hello
"[..], 10),
                   "Hello\n\n");
     }

     #[test]
     fn test_wrap() {
        assert_eq!(wrap_text("Hello", 10), "Hello\n");
     }
}
<commit_msg>Make colspan seem to work.<commit_after>#[macro_use]
extern crate string_cache;
extern crate html5ever;
extern crate unicode_width;

use std::io;
use std::io::Write;
use std::cmp::max;
use html5ever::{parse_document};
use html5ever::driver::ParseOpts;
use html5ever::tree_builder::TreeBuilderOpts;
use html5ever::rcdom::{RcDom,Handle,Text,Element,Document,Comment};
use html5ever::tendril::TendrilSink;
use unicode_width::{UnicodeWidthStr,UnicodeWidthChar};

/// A dummy writer which does nothing
struct Discard {}
impl Write for Discard {
    fn write(&mut self, bytes: &[u8]) -> std::result::Result<usize, io::Error> { Ok(bytes.len()) }
    fn flush(&mut self) -> std::result::Result<(), io::Error> { Ok(()) }
}

fn wrap_text(text: &str, width: usize) -> String {
    let mut result = String::new();
    let mut xpos = 0usize;
    for word in text.split_whitespace() {
        if width <= (xpos + 1) {
            result.push('\n');
            xpos = 0;
        }
        let space_left = width - xpos - 1;
        let word_width = UnicodeWidthStr::width(word);
        if word_width <= space_left {
            /* It fits; no problem.  Add a space if not at the
             * start of line.*/
            if xpos > 0 {
                result.push(' ');
                xpos += 1;
            }
            result.push_str(word);
            xpos += word_width;
            continue;
        }

        /* It doesn't fit.  If we're not at the start of the line,
         * then go to a new line. */
        if xpos > 0 {
            result.push('\n');
            xpos = 0;
        }

        /* We're now at the start of a line. */
        if word_width > width {
            /* It doesn't fit at all on the line, so break it. */
            for c in word.chars() {
                let c_width = UnicodeWidthChar::width(c).unwrap();
                if c_width + xpos > width {
                    /* Break here */
                    result.push('\n');
                    xpos = 0;
                }
                /* This might happen with really narrow spaces... */
                assert!(c_width <= width);

                result.push(c);
                xpos += c_width;
            }
        } else {
            result.push_str(word);
            xpos += word_width;
        }
    }
    /* Terminate with a newline if needed. */
    if xpos > 0 {
        result.push('\n');
    }
    result
}

fn get_text(handle: Handle) -> String {
    let node = handle.borrow();
    let mut result = String::new();
    if let Text(ref tstr) = node.node {
        result.push_str(&tstr);
    } else {
        for child in node.children.iter() {
            result.push_str(&get_text(child.clone()));
        }
    }
    result
}

fn get_wrapped_text(handle: Handle, width: usize) -> String {
    let text = get_text(handle);
    wrap_text(&text, width)
}

fn dom_to_string<T:Write>(handle: Handle, err_out: &mut T, width: usize) -> String {
    let node = handle.borrow();
    let mut result = String::new();
    match node.node {
        Document => {},
        Element(ref name, _, _) => {
            match *name {
                qualname!(html, "html") |
                qualname!(html, "div") |
                qualname!(html, "span") |
                qualname!(html, "body") => {
                    /* process children, but don't add anything */
                },
                qualname!(html, "link") |
                qualname!(html, "meta") |
                qualname!(html, "hr") |
                qualname!(html, "head") => {
                    /* Ignore the head and its children */
                    return result;
                },
                qualname!(html, "h1") |
                qualname!(html, "h2") |
                qualname!(html, "h3") |
                qualname!(html, "h4") |
                qualname!(html, "p") => {
                    return get_wrapped_text(handle.clone(), width) + "\n";
                },
                qualname!(html, "br") => {
                    result.push('\n');
                }
                qualname!(html, "table") => return table_to_string(handle.clone(), err_out, width),
                _ => {
                    write!(err_out, "Unhandled element: {:?}\n", name.local).unwrap();
                },
            }
          },
        Text(ref tstr) => {
            return wrap_text(tstr, width);
        }
        Comment(_) => {},
        _ => { write!(err_out, "Unhandled: {:?}\n", node).unwrap(); },
    }
    for child in node.children.iter() {
        result.push_str(&dom_to_string(child.clone(), err_out, width));
    }
    result
}

#[derive(Debug)]
struct TableCell {
    colspan: usize,
    content: Handle,
}

#[derive(Debug)]
struct TableRow {
    cells: Vec<TableCell>,
}

#[derive(Debug)]
struct Table {
    rows: Vec<TableRow>,
}

impl Table {
    pub fn new() -> Table {
        Table{ rows: Vec::new() }
    }
    pub fn push(&mut self, row: TableRow) {
        self.rows.push(row);
    }
    pub fn rows(&self) -> std::slice::Iter<TableRow> {
        self.rows.iter()
    }
}

impl TableRow {
    pub fn new() -> TableRow {
        TableRow{ cells: Vec::new() }
    }
    pub fn push(&mut self, cell: TableCell) {
        self.cells.push(cell);
    }
    pub fn cells(&self) -> std::slice::Iter<TableCell> {
        self.cells.iter()
    }
    /// Return an iterator over (column, &cell)s, which
    /// takes into account colspan.
    pub fn cell_columns(&self) -> Vec<(usize, &TableCell)> {
        let mut result = Vec::new();
        let mut colno = 0;
        for cell in &self.cells {
            result.push((colno, cell));
            colno += cell.colspan;
        }
        result
    }
    /// Count the number of cells in the row.
    /// Takes into account colspan.
    pub fn num_cells(&self) -> usize {
        self.cells.iter().map(|cell| cell.colspan).sum()
    }
}

impl TableCell {
    pub fn new(s: Handle) -> TableCell {
        if let Element(_, _, ref attrs) = s.borrow().node {
            let mut colspan = 1;
            for attr in attrs {
                if &attr.name.local == "colspan" {
                    let v:&str = &*attr.value;
                    colspan = v.parse().unwrap_or(1);
                } else {
                    //println!("Attr: {:?}", attr);
                }
            }
            TableCell{ content: s.clone(), colspan: colspan }
        } else {
            panic!("TableCell::new received a non-Element");
        }
    }
    pub fn render<T:Write>(&self, width: usize, err_out: &mut T) -> String
    {
        dom_to_string(self.content.clone(), err_out, width)
    }
}

fn handle_td(handle: Handle) -> TableCell {
    TableCell::new(handle)
}

fn handle_tr<T:Write>(handle: Handle, _: &mut T) -> TableRow {
    let node = handle.borrow();

    let mut row = TableRow::new();

    for child in node.children.iter() {
        match child.borrow().node {
            Element(ref name, _, _) => {
                match *name {
                    qualname!(html, "td") => {
                        row.push(handle_td(child.clone()));
                    },
                    _ => println!("  [[tr child: {:?}]]", name),
                }
            },
            Comment(_) => {},
            _ => { /*result.push_str(&format!("Unhandled in table: {:?}\n", node));*/ },
        }
    }

    row
}

fn handle_tbody<T:Write>(handle: Handle, err_out: &mut T, width: usize) -> String {
    let node = handle.borrow();

    let mut table = Table::new();

    for child in node.children.iter() {
        match child.borrow().node {
            Element(ref name, _, _) => {
                match *name {
                    qualname!(html, "tr") => {
                        table.push(handle_tr(child.clone(), err_out));
                    },
                    _ => println!("  [[tbody child: {:?}]]", name),
                }
            },
            Comment(_) => {},
            _ => { /*result.push_str(&format!("Unhandled in table: {:?}\n", node));*/ },
        }
    }

    /* Now lay out the table.  Use the simple option of giving each column
     * same width.  TODO: be cleverer, and handle multi-width cells, etc. */
    let num_columns = table.rows().map(|r| r.num_cells()).max().unwrap();

    let mut result = String::new();

    /* Heuristic: scale the column widths according to how much content there is. */
    let test_col_width = 1000;  // Render width for measurement; shouldn't make much difference.
    let min_width = 5;
    let mut col_sizes = vec![0usize; num_columns];

    for row in table.rows() {
        let mut colno = 0;
        for cell in row.cells() {
            let celldata = cell.render(test_col_width, &mut Discard{});
            // If the cell has a colspan>1, then spread its size between the
            // columns.
            let col_size = celldata.len() / cell.colspan;
            for i in 0..cell.colspan {
                col_sizes[colno + i] += col_size;
            }
            colno += cell.colspan;
        }
    }
    let tot_size: usize = col_sizes.iter().sum();
    let mut col_widths:Vec<usize> = col_sizes.iter()
                                         .map(|sz| {
                                             if *sz == 0 {
                                                 0
                                             } else {
                                                 max(sz * width / tot_size, min_width)
                                             }
                                          }).collect();
    /* The minimums may have put the total width too high */
    while col_widths.iter().cloned().sum::<usize>() > width {
        let (i, _) = col_widths.iter().cloned().enumerate().max_by_key(|k| k.1).unwrap();
        col_widths[i] -= 1;
    }

    let mut rowline = String::new();
    for width in col_widths.iter().cloned().filter(|w:&usize| *w > 0) {
        rowline.push_str(&(0..(width-1)).map(|_| '-').collect::<String>());
        rowline.push('+');
    }
    if rowline.len() > 0 {
        rowline.pop().unwrap();  // Remove the last '+'.
    }
    result.push_str(&rowline);
    result.push('\n');

    for row in table.rows() {
        let (used_widths, formatted_cells): (Vec<usize>, Vec<String>) = row.cell_columns()
                                              .into_iter()
                                              .flat_map(|(colno, cell)| {
                                                   let col_width:usize = col_widths[colno..colno+cell.colspan]
                                                                      .iter().sum();
                                                   if col_width > 0 {
                                                       Some((col_width, cell.render(col_width-1, err_out)))
                                                   } else {
                                                       None
                                                   }
                                               })
                                              .unzip();
        let line_sets: Vec<Vec<&str>> = formatted_cells.iter()
                                                       .map(|s| s.lines()
                                                                 .map(|line| line.trim_right())
                                                                 .collect())
                                                       .collect();
        let cell_height = line_sets.iter()
                                   .map(|v| v.len())
                                   .max().unwrap_or(0);
        for i in 0..cell_height {
            for (cellno, ls) in line_sets.iter().enumerate() {
                result.push_str(&format!("{: <width$}", ls.get(i).cloned().unwrap_or(""), width = used_widths[cellno]-1));
                if cellno == line_sets.len()-1 {
                    result.push('\n')
                } else {
                    result.push('|')
                }
            }
        }
        if cell_height > 0 {
            result.push_str(&rowline);
            result.push('\n');
        }
    }

    result
}

fn table_to_string<T:Write>(handle: Handle, err_out: &mut T, width: usize) -> String {
    let node = handle.borrow();
    let result = String::new();

    for child in node.children.iter() {
        match child.borrow().node {
            Element(ref name, _, _) => {
                match *name {
                    qualname!(html, "tbody") => return handle_tbody(child.clone(), err_out, width),
                    _ => { writeln!(err_out, "  [[table child: {:?}]]", name).unwrap();},
                }
            },
            Comment(_) => {},
            _ => { /*result.push_str(&format!("Unhandled in table: {:?}\n", node));*/ },
        }
    }

    result
}

pub fn from_read<R>(mut input: R, width: usize) -> String where R: io::Read {
    let opts = ParseOpts {
        tree_builder: TreeBuilderOpts {
            drop_doctype: true,
            ..Default::default()
        },
        ..Default::default()
    };
    let dom = parse_document(RcDom::default(), opts)
                   .from_utf8()
                   .read_from(&mut input)
                   .unwrap();

    dom_to_string(dom.document, &mut io::stderr(), width)
}

#[cfg(test)]
mod tests {
    use super::{from_read,wrap_text};
    #[test]
    fn test_table() {
        assert_eq!(from_read(&br##"

           1
           2
           3

"##[..], 12), r#"---+---+---
1  |2  |3
---+---+---
"#);
     }

     #[test]
     fn test_colspan() {
        assert_eq!(from_read(&br##"

           1
           2
           3

           12
           3

           1
           23

"##[..], 12), r#"---+---+---
1  |2  |3
---+---+---
12     |3
---+---+---
1  |23
---+---+---
"#);
     }

     #[test]
     fn test_para() {
        assert_eq!(from_read(&b"
Hello
"[..], 10),
                   "Hello\n\n");
     }

     #[test]
     fn test_wrap() {
        assert_eq!(wrap_text("Hello", 10), "Hello\n");
     }
}
<|endoftext|>


<commit_before>use std::time::Duration;

extern crate chrono;
use chrono::{DateTime, UTC};

///
/// Hubs
///

pub type HubId = String;

/// Metadata on a hub
#[derive(Debug)]
pub struct Hub {
    tags: Vec<String>,

    /// An id unique to this hub.
    id: HubId,
}

///
/// Endpoints
///

pub type EndPointId = String;

#[derive(Debug)]
pub enum IO {
    /// This endpoint supports inputs.
    Input {
        /// If `Some(duration)`, this endpoint can be polled, i.e. it
        /// will respond when the FoxBox requests the latest value.
        /// Parameter `duration` indicates the smallest interval
        /// between two updates.
        ///
        /// Otherwise, the endpoint cannot be polled and will push
        /// data to the FoxBox when it is available.
        ///
        /// # Examples
        ///
        /// - Long-running pollution or humidity sensors typically
        ///   do not accept requests and rather send batches of
        ///   data every 24h.
        poll: Option<Duration>,

        /// If `Some(duration)`, this endpoint can send the data to
        /// the FoxBox whenever it is updated. Parameter `duration`
        /// indicates the smallest interval between two updates.
        ///
        /// Otherwise, the endpoint cannot send data to the FoxBox
        /// and needs to be polled.
        trigger: Option<Duration>,

        /// Date at which the latest value was received, whether through
        /// polling or through a trigger.
        updated: DateTime<UTC>,
    },
    Output {
        /// If `Some(duration)`, this endpoint supports pushing,
        /// i.e. the FoxBox can send values.
        push: Option<Duration>,

        /// Date at which the latest value was sent to the endpoint.
        updated: DateTime<UTC>,
    }
}

/// An endpoint represents a single place where data can enter or
/// leave a device.
#[derive(Debug)]
pub struct Endpoint {
    /// Tags describing the endpoint.
    ///
    /// These tags can be set by the user, adapters or
    /// applications. They are used to regroup endpoints for rules.
    ///
    /// For instance "entrance".
    tags: Vec<String>,

    /// An id unique to this endpoint.
    id: EndPointId,

    io: IO,
}
<commit_msg>Adding kinds<commit_after>extern crate chrono;

///
/// Hubs
///

pub type HubId = String;

/// A hub represents a node to which several endpoints, as well as
/// other hubs can be connected. The FoxBox is the root hub. Simple
/// devices that can do a single thing (e.g. a button) are endpoints,
/// while complex devices containing several sensors or effectors are
/// also hubs, in which each sensor and each effector is an endpoint.
#[derive(Debug, Clone)]
pub struct Hub {
    /// Tags describing the hub.
    ///
    /// These tags can be set by the user, adapters or
    /// applications. They are used by applications.
    ///
    /// For instance "entrance".
    tags: Vec<String>,

    /// An id unique to this hub.
    id: HubId,

    /// Hubs depending on this hub.
    subhubs: Vec<Hub>,

    /// Endpoints connected directly to this hub.
    endpoints: Vec<EndPoint>,
}

///
/// Endpoints
///

pub type EndPointId = String;

/// The kind of value provided by an endpoint.
#[derive(Debug, Clone)]
pub struct ValueKind {
    /// The vendor. An empty string for standardized value kinds,
    /// otherwise a string identifying the owner of this non-standard
    /// value (e.g. "Mozilla")
    vendor: String,

    /// Identification of the adapter providing this kind of value.
    adapter: String,

    /// The nature of the value.
    ///
    /// For instance: "is-on", "is-open".
    nature: String,

    /// The data type of the value.
    typ: Type
}

#[derive(Debug, Clone)]
pub enum IO {
    /// This endpoint supports inputs.
    Input {
        /// The kind of value that can be obtained from this endpoint.
        kind: ValueKind,

        /// If `Some(duration)`, this endpoint can be polled, i.e. it
        /// will respond when the FoxBox requests the latest value.
        /// Parameter `duration` indicates the smallest interval
        /// between two updates.
        ///
        /// Otherwise, the endpoint cannot be polled and will push
        /// data to the FoxBox when it is available.
        ///
        /// # Examples
        ///
        /// - Long-running pollution or humidity sensors typically
        ///   do not accept requests and rather send batches of
        ///   data every 24h.
        poll: Option<std::time::Duration>,

        /// If `Some(duration)`, this endpoint can send the data to
        /// the FoxBox whenever it is updated. Parameter `duration`
        /// indicates the smallest interval between two updates.
        ///
        /// Otherwise, the endpoint cannot send data to the FoxBox
        /// and needs to be polled.
        trigger: Option<std::time::Duration>,

        /// Date at which the latest value was received, whether through
        /// polling or through a trigger.
        updated: chrono::DateTime<chrono::UTC>,
    },
    Output {
        /// The kind of value that can be sent to this endpoint.
        kind: ValueKind,

        /// If `Some(duration)`, this endpoint supports pushing,
        /// i.e. the FoxBox can send values.
        push: Option<std::time::Duration>,

        /// Date at which the latest value was sent to the endpoint.
        updated: chrono::DateTime<chrono::UTC>,
    }
}

/// An endpoint represents a single place where data can enter or
/// leave a device. Note that endpoints support either a single kind
/// of input or a single kind of output. Devices that support both
/// inputs or outputs, or several kinds of inputs, or several kinds of
/// outputs, are represented as hubs containing several endpoints.
#[derive(Debug, Clone)]
pub struct EndPoint {
    /// Tags describing the endpoint.
    ///
    /// These tags can be set by the user, adapters or
    /// applications. They are used to regroup endpoints for rules.
    ///
    /// For instance "entrance".
    tags: Vec<String>,

    /// An id unique to this endpoint.
    id: EndPointId,

    /// The update mechanism for this endpoint.
    mechanism: IO,

    /// The last time the device was seen.
    last_seen: chrono::DateTime<chrono::UTC>,
}

///
/// Values
///

#[derive(Debug, Clone)]
pub enum Type {
    ///
    /// # Trivial values
    ///

    /// An empty value. Used for instance to inform that a countdown
    /// has reached 0 or that a device is ready.
    Unit,

    /// A boolean. Used for instance for on-off switches, presence
    /// detectors, etc.
    Bool,

    ///
    /// # Time
    ///

    /// A duration. Used for instance in countdowns.
    Duration,

    /// A precise timestamp. Used for instance to determine when an
    /// event has taken place.
    TimeStamp,

    ///
    /// ...
    ///
    Color,
}

<|endoftext|>


<commit_before>//! Use this library to open a path or URL using the program configured on the system.
//!
//! # Usage
//!
//! ```test_harness,no_run
//! extern crate open;
//!
//! # #[test]
//! # fn doit() {
//! open::that("/path/to/file/with.fancy-extension");
//! if open::that("https://google.de").is_ok() {
//!     println!("Look at your browser !");
//! }
//! # }
//! ```
//!
//! # Notes
//! As an operating system program is used, chances are that the open operation fails.
//! Therfore, you are advised to at least check the result with `.is_err()` and
//! behave accordingly, e.g. by letting the user know what you tried to open, and failed.
use std::io;
use std::process::{Command, ExitStatus};

#[cfg(not(any(target_os = "windows", target_os = "macos")))]
pub fn that(path: &str) -> io::Result<ExitStatus> {
    let mut last_err: io::Result<ExitStatus> = Err(io::Error::from_raw_os_error(0));
    for program in &["xdg-open", "gnome-open", "kde-open"] {
        match Command::new(program).arg(path).spawn() {
            Ok(mut child) => return child.wait(),
            Err(err) => {
                last_err = Err(err);
                continue;
            },
        }
    }
    last_err
}

#[cfg(target_os = "windows")]
pub fn that(path: &str) -> io::Result<ExitStatus> {
    match Command::new("start").arg(path).spawn() {
        Ok(mut child) => child.wait(),
        Err(err) => Err(err),
    }
}

#[cfg(target_os = "macos")]
pub fn that(path: &str) -> io::Result<ExitStatus> {
    match Command::new("open").arg(path).spawn() {
        Ok(mut child) => child.wait(),
        Err(err) => Err(err),
    }
}<commit_msg>windows: start is a cmd command, not an executable<commit_after>//! Use this library to open a path or URL using the program configured on the system.
//!
//! # Usage
//!
//! ```test_harness,no_run
//! extern crate open;
//!
//! # #[test]
//! # fn doit() {
//! open::that("/path/to/file/with.fancy-extension");
//! if open::that("https://google.de").is_ok() {
//!     println!("Look at your browser !");
//! }
//! # }
//! ```
//!
//! # Notes
//! As an operating system program is used, chances are that the open operation fails.
//! Therfore, you are advised to at least check the result with `.is_err()` and
//! behave accordingly, e.g. by letting the user know what you tried to open, and failed.
use std::io;
use std::process::{Command, ExitStatus};

#[cfg(not(any(target_os = "windows", target_os = "macos")))]
pub fn that(path: &str) -> io::Result<ExitStatus> {
    let mut last_err: io::Result<ExitStatus> = Err(io::Error::from_raw_os_error(0));
    for program in &["xdg-open", "gnome-open", "kde-open"] {
        match Command::new(program).arg(path).spawn() {
            Ok(mut child) => return child.wait(),
            Err(err) => {
                last_err = Err(err);
                continue;
            },
        }
    }
    last_err
}

#[cfg(target_os = "windows")]
pub fn that(path: &str) -> io::Result<ExitStatus> {
    match Command::new("cmd").arg("/C").arg("start").arg(path).spawn() {
        Ok(mut child) => child.wait(),
        Err(err) => Err(err),
    }
}

#[cfg(target_os = "macos")]
pub fn that(path: &str) -> io::Result<ExitStatus> {
    match Command::new("open").arg(path).spawn() {
        Ok(mut child) => child.wait(),
        Err(err) => Err(err),
    }
}
<|endoftext|>


<commit_before>extern crate futures;

use futures::{Async, Poll, Future, Stream};

pub type BoxStateStream<T, S, E> = Box<StateStream<Item = T, State = S, Error = E> + Send>;

pub enum StreamEvent<I, S> {
    Next(I),
    Done(S),
}

pub trait StateStream {
    type Item;
    type State;
    type Error;

    fn poll(&mut self) -> Poll<StreamEvent<Self::Item, Self::State>, Self::Error>;

    #[inline]
    fn boxed(self) -> BoxStateStream<Self::Item, Self::State, Self::Error>
        where Self: Sized + Send + 'static
    {
        Box::new(self)
    }

    #[inline]
    fn into_future(self) -> IntoFuture<Self>
        where Self: Sized
    {
        IntoFuture(Some(self))
    }

    #[inline]
    fn into_stream(self) -> IntoStream<Self>
        where Self: Sized
    {
        IntoStream(self)
    }

    #[inline]
    fn map<F, B>(self, f: F) -> Map<Self, F>
        where Self: Sized,
              F: FnMut(Self::Item) -> B
    {
        Map {
            stream: self,
            f: f,
        }
    }

    #[inline]
    fn map_err<F, B>(self, f: F) -> MapErr<Self, F>
        where Self: Sized,
              F: FnMut(Self::Error) -> B
    {
        MapErr {
            stream: self,
            f: f,
        }
    }

    #[inline]
    fn  map_state<F, B>(self, f: F) -> MapState<Self, F>
        where Self: Sized,
              F: FnOnce(Self::State) -> B
    {
        MapState {
            stream: self,
            f: Some(f),
        }
    }
}

impl<S: ?Sized> StateStream for Box<S>
    where S: StateStream
{
    type Item = S::Item;
    type State = S::State;
    type Error = S::Error;

    #[inline]
    fn poll(&mut self) -> Poll<StreamEvent<S::Item, S::State>, S::Error> {
        S::poll(self)
    }
}

#[inline]
pub fn stream<S>(stream: S) -> FromStream<S>
    where S: Stream
{
    FromStream(stream)
}

pub struct FromStream<S>(S);

impl<S> StateStream for FromStream<S>
    where S: Stream
{
    type Item = S::Item;
    type State = ();
    type Error = S::Error;

    #[inline]
    fn poll(&mut self) -> Poll<StreamEvent<S::Item, ()>, S::Error> {
        self.0.poll().map(|a| {
            match a {
                Async::Ready(Some(i)) => Async::Ready(StreamEvent::Next(i)),
                Async::Ready(None) => Async::Ready(StreamEvent::Done(())),
                Async::NotReady => Async::NotReady,
            }
        })
    }
}

pub struct IntoFuture<S>(Option<S>);

impl<S> Future for IntoFuture<S>
    where S: StateStream
{
    type Item = (StreamEvent<S::Item, S::State>, S);
    type Error = (S::Error, S);

    #[inline]
    fn poll(&mut self) -> Poll<(StreamEvent<S::Item, S::State>, S), (S::Error, S)> {
        let item = match self.0.as_mut().expect("polling IntoFuture twice").poll() {
            Ok(Async::NotReady) => return Ok(Async::NotReady),
            Ok(Async::Ready(i)) => Ok(i),
            Err(e) => Err(e),
        };
        let stream = self.0.take().unwrap();
        match item {
            Ok(i) => Ok(Async::Ready((i, stream))),
            Err(e) => Err((e, stream)),
        }
    }
}

pub struct IntoStream<S>(S);

impl<S> Stream for IntoStream<S>
    where S: StateStream
{
    type Item = S::Item;
    type Error = S::Error;

    #[inline]
    fn poll(&mut self) -> Poll<Option<S::Item>, S::Error> {
        self.0.poll().map(|a| {
            match a {
                Async::Ready(StreamEvent::Next(i)) => Async::Ready(Some(i)),
                Async::Ready(StreamEvent::Done(_)) => Async::Ready(None),
                Async::NotReady => Async::NotReady,
            }
        })
    }
}

pub struct Map<S, F> {
    stream: S,
    f: F,
}

impl<S, F, B> StateStream for Map<S, F>
    where S: StateStream,
          F: FnMut(S::Item) -> B
{
    type Item = B;
    type State = S::State;
    type Error = S::Error;

    #[inline]
    fn poll(&mut self) -> Poll<StreamEvent<B, S::State>, S::Error> {
        self.stream.poll().map(|a| {
            match a {
                Async::Ready(StreamEvent::Next(i)) => Async::Ready(StreamEvent::Next((self.f)(i))),
                Async::Ready(StreamEvent::Done(s)) => Async::Ready(StreamEvent::Done(s)),
                Async::NotReady => Async::NotReady,
            }
        })
    }
}

pub struct MapErr<S, F> {
    stream: S,
    f: F,
}

impl<S, F, B> StateStream for MapErr<S, F>
    where S: StateStream,
          F: FnMut(S::Error) -> B
{
    type Item = S::Item;
    type State = S::State;
    type Error = B;

    #[inline]
    fn poll(&mut self) -> Poll<StreamEvent<S::Item, S::State>, B> {
        match self.stream.poll() {
            Ok(a) => Ok(a),
            Err(e) => Err((self.f)(e))
        }
    }
}

pub struct MapState<S, F> {
    stream: S,
    f: Option<F>,
}

impl<S, F, B> StateStream for MapState<S, F>
    where S: StateStream,
          F: FnOnce(S::State) -> B
{
    type Item = S::Item;
    type State = B;
    type Error = S::Error;

    #[inline]
    fn poll(&mut self) -> Poll<StreamEvent<S::Item, B>, S::Error> {
        self.stream.poll().map(|a| {
            match a {
                Async::Ready(StreamEvent::Next(i)) => Async::Ready(StreamEvent::Next(i)),
                Async::Ready(StreamEvent::Done(s)) => {
                    let f = self.f.take().expect("polled MapState after completion");
                    Async::Ready(StreamEvent::Done(f(s)))
                }
                Async::NotReady => Async::NotReady,
            }
        })
    }
}
<commit_msg>Add Collect<commit_after>extern crate futures;

use futures::{Async, Poll, Future, Stream};
use std::mem;

pub type BoxStateStream<T, S, E> = Box<StateStream<Item = T, State = S, Error = E> + Send>;

pub enum StreamEvent<I, S> {
    Next(I),
    Done(S),
}

pub trait StateStream {
    type Item;
    type State;
    type Error;

    fn poll(&mut self) -> Poll<StreamEvent<Self::Item, Self::State>, Self::Error>;

    #[inline]
    fn boxed(self) -> BoxStateStream<Self::Item, Self::State, Self::Error>
        where Self: Sized + Send + 'static
    {
        Box::new(self)
    }

    #[inline]
    fn into_future(self) -> IntoFuture<Self>
        where Self: Sized
    {
        IntoFuture(Some(self))
    }

    #[inline]
    fn into_stream(self) -> IntoStream<Self>
        where Self: Sized
    {
        IntoStream(self)
    }

    #[inline]
    fn map<F, B>(self, f: F) -> Map<Self, F>
        where Self: Sized,
              F: FnMut(Self::Item) -> B
    {
        Map {
            stream: self,
            f: f,
        }
    }

    #[inline]
    fn map_err<F, B>(self, f: F) -> MapErr<Self, F>
        where Self: Sized,
              F: FnMut(Self::Error) -> B
    {
        MapErr {
            stream: self,
            f: f,
        }
    }

    #[inline]
    fn map_state<F, B>(self, f: F) -> MapState<Self, F>
        where Self: Sized,
              F: FnOnce(Self::State) -> B
    {
        MapState {
            stream: self,
            f: Some(f),
        }
    }

    #[inline]
    fn collect(self) -> Collect<Self>
        where Self: Sized
    {
        Collect {
            stream: self,
            items: vec![],
        }
    }
}

impl<S: ?Sized> StateStream for Box<S>
    where S: StateStream
{
    type Item = S::Item;
    type State = S::State;
    type Error = S::Error;

    #[inline]
    fn poll(&mut self) -> Poll<StreamEvent<S::Item, S::State>, S::Error> {
        S::poll(self)
    }
}

#[inline]
pub fn stream<S>(stream: S) -> FromStream<S>
    where S: Stream
{
    FromStream(stream)
}

pub struct FromStream<S>(S);

impl<S> StateStream for FromStream<S>
    where S: Stream
{
    type Item = S::Item;
    type State = ();
    type Error = S::Error;

    #[inline]
    fn poll(&mut self) -> Poll<StreamEvent<S::Item, ()>, S::Error> {
        self.0.poll().map(|a| {
            match a {
                Async::Ready(Some(i)) => Async::Ready(StreamEvent::Next(i)),
                Async::Ready(None) => Async::Ready(StreamEvent::Done(())),
                Async::NotReady => Async::NotReady,
            }
        })
    }
}

pub struct IntoFuture<S>(Option<S>);

impl<S> Future for IntoFuture<S>
    where S: StateStream
{
    type Item = (StreamEvent<S::Item, S::State>, S);
    type Error = (S::Error, S);

    #[inline]
    fn poll(&mut self) -> Poll<(StreamEvent<S::Item, S::State>, S), (S::Error, S)> {
        let item = match self.0.as_mut().expect("polling IntoFuture twice").poll() {
            Ok(Async::NotReady) => return Ok(Async::NotReady),
            Ok(Async::Ready(i)) => Ok(i),
            Err(e) => Err(e),
        };
        let stream = self.0.take().unwrap();
        match item {
            Ok(i) => Ok(Async::Ready((i, stream))),
            Err(e) => Err((e, stream)),
        }
    }
}

pub struct IntoStream<S>(S);

impl<S> Stream for IntoStream<S>
    where S: StateStream
{
    type Item = S::Item;
    type Error = S::Error;

    #[inline]
    fn poll(&mut self) -> Poll<Option<S::Item>, S::Error> {
        self.0.poll().map(|a| {
            match a {
                Async::Ready(StreamEvent::Next(i)) => Async::Ready(Some(i)),
                Async::Ready(StreamEvent::Done(_)) => Async::Ready(None),
                Async::NotReady => Async::NotReady,
            }
        })
    }
}

pub struct Map<S, F> {
    stream: S,
    f: F,
}

impl<S, F, B> StateStream for Map<S, F>
    where S: StateStream,
          F: FnMut(S::Item) -> B
{
    type Item = B;
    type State = S::State;
    type Error = S::Error;

    #[inline]
    fn poll(&mut self) -> Poll<StreamEvent<B, S::State>, S::Error> {
        self.stream.poll().map(|a| {
            match a {
                Async::Ready(StreamEvent::Next(i)) => Async::Ready(StreamEvent::Next((self.f)(i))),
                Async::Ready(StreamEvent::Done(s)) => Async::Ready(StreamEvent::Done(s)),
                Async::NotReady => Async::NotReady,
            }
        })
    }
}

pub struct MapErr<S, F> {
    stream: S,
    f: F,
}

impl<S, F, B> StateStream for MapErr<S, F>
    where S: StateStream,
          F: FnMut(S::Error) -> B
{
    type Item = S::Item;
    type State = S::State;
    type Error = B;

    #[inline]
    fn poll(&mut self) -> Poll<StreamEvent<S::Item, S::State>, B> {
        match self.stream.poll() {
            Ok(a) => Ok(a),
            Err(e) => Err((self.f)(e))
        }
    }
}

pub struct MapState<S, F> {
    stream: S,
    f: Option<F>,
}

impl<S, F, B> StateStream for MapState<S, F>
    where S: StateStream,
          F: FnOnce(S::State) -> B
{
    type Item = S::Item;
    type State = B;
    type Error = S::Error;

    #[inline]
    fn poll(&mut self) -> Poll<StreamEvent<S::Item, B>, S::Error> {
        self.stream.poll().map(|a| {
            match a {
                Async::Ready(StreamEvent::Next(i)) => Async::Ready(StreamEvent::Next(i)),
                Async::Ready(StreamEvent::Done(s)) => {
                    let f = self.f.take().expect("polled MapState after completion");
                    Async::Ready(StreamEvent::Done(f(s)))
                }
                Async::NotReady => Async::NotReady,
            }
        })
    }
}

pub struct Collect<S>
    where S: StateStream
{
    stream: S,
    items: Vec<S::Item>,
}

impl<S> Future for Collect<S>
    where S: StateStream
{
    type Item = (Vec<S::Item>, S::State);
    type Error = S::Error;

    #[inline]
    fn poll(&mut self) -> Poll<(Vec<S::Item>, S::State), S::Error> {
        loop {
            match self.stream.poll() {
                Ok(Async::Ready(StreamEvent::Next(i))) => self.items.push(i),
                Ok(Async::Ready(StreamEvent::Done(s))) => {
                    let items = mem::replace(&mut self.items, vec![]);
                    return Ok(Async::Ready((items, s)))
                }
                Ok(Async::NotReady) => return Ok(Async::NotReady),
                Err(e) => return Err(e),
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::io::Command;
use std::os;
use std::str;

pub fn target_supported() -> bool {
    os::getenv("HOST") == os::getenv("TARGET") ||
        os::getenv("PKG_CONFIG_ALLOW_CROSS") == Some("1".to_string())
}

pub struct Options {
    pub statik: bool,
    pub atleast_version: Option<String>,
}

pub fn find_library(name: &str) -> Result<(), String> {
    find_library_opts(name, &default_options(name))
}

pub fn find_library_opts(name: &str, options: &Options) -> Result<(), String> {
    if os::getenv(format!("{}_NO_PKG_CONFIG", envify(name)).as_slice()).is_some() {
        return Err(format!("pkg-config requested to be aborted for {}", name))
    } else if !target_supported() {
        return Err("pkg-config doesn't handle cross compilation. Use \
                    PKG_CONFIG_ALLOW_CROSS=1 to override".to_string());
    }
    let mut cmd = Command::new("pkg-config");
    if options.statik {
        cmd.arg("--static");
    }
    cmd.arg("--libs")
       .env("PKG_CONFIG_ALLOW_SYSTEM_LIBS", "1")
       .arg(name);
    match options.atleast_version {
        Some(ref v) => { cmd.arg(format!("--atleast-version={}", v)); }
        None => {}
    }
    let out = try!(cmd.output().map_err(|e| {
        format!("failed to run `{}`: {}", cmd, e)
    }));
    let stdout = str::from_utf8(out.output.as_slice()).unwrap();
    let stderr = str::from_utf8(out.error.as_slice()).unwrap();
    if !out.status.success() {
        let mut msg = format!("`{}` did not exit successfully: {}", cmd,
                              out.status);
        if stdout.len() > 0 {
            msg.push_str("\n--- stdout\n");
            msg.push_str(stdout);
        }
        if stderr.len() > 0 {
            msg.push_str("\n--- stderr\n");
            msg.push_str(stderr);
        }
        return Err(msg)
    }

    for arg in stdout.split(' ').filter(|l| !l.is_empty() && l.len() > 2) {
        let val = arg.slice_from(2);
        if arg.starts_with("-l") {
            if options.statik {
                println!("cargo:rustc-flags=-l {}:static", val);
            } else {
                println!("cargo:rustc-flags=-l {}", val);
            }
        } else if arg.starts_with("-L") {
            println!("cargo:rustc-flags=-L {}", val);
        }
    }
    Ok(())
}

pub fn default_options(name: &str) -> Options {
    let name = envify(name);
    let statik = if os::getenv(format!("{}_STATIC", name).as_slice()).is_some() {
        true
    } else if os::getenv(format!("{}_DYNAMIC", name).as_slice()).is_some() {
        false
    } else if os::getenv("PKG_CONFIG_ALL_STATIC").is_some() {
        true
    } else if os::getenv("PKG_CONFIG_ALL_DYNAMIC").is_some() {
        false
    } else {
        false
    };
    Options { statik: statik, atleast_version: None }
}

fn envify(name: &str) -> String {
    name.chars().map(|c| c.to_uppercase()).map(|c| if c == '-' {'_'} else {c})
        .collect()
}
<commit_msg>Don't include static system libraries<commit_after>#![feature(slicing_syntax)]

use std::io::Command;
use std::io::fs::PathExtensions;
use std::os;
use std::str;

pub fn target_supported() -> bool {
    os::getenv("HOST") == os::getenv("TARGET") ||
        os::getenv("PKG_CONFIG_ALLOW_CROSS") == Some("1".to_string())
}

pub struct Options {
    pub statik: bool,
    pub atleast_version: Option<String>,
}

pub fn find_library(name: &str) -> Result<(), String> {
    find_library_opts(name, &default_options(name))
}

pub fn find_library_opts(name: &str, options: &Options) -> Result<(), String> {
    if os::getenv(format!("{}_NO_PKG_CONFIG", envify(name)).as_slice()).is_some() {
        return Err(format!("pkg-config requested to be aborted for {}", name))
    } else if !target_supported() {
        return Err("pkg-config doesn't handle cross compilation. Use \
                    PKG_CONFIG_ALLOW_CROSS=1 to override".to_string());
    }
    let mut cmd = Command::new("pkg-config");
    if options.statik {
        cmd.arg("--static");
    }
    cmd.arg("--libs")
       .env("PKG_CONFIG_ALLOW_SYSTEM_LIBS", "1")
       .arg(name);
    match options.atleast_version {
        Some(ref v) => { cmd.arg(format!("--atleast-version={}", v)); }
        None => {}
    }
    let out = try!(cmd.output().map_err(|e| {
        format!("failed to run `{}`: {}", cmd, e)
    }));
    let stdout = str::from_utf8(out.output.as_slice()).unwrap();
    let stderr = str::from_utf8(out.error.as_slice()).unwrap();
    if !out.status.success() {
        let mut msg = format!("`{}` did not exit successfully: {}", cmd,
                              out.status);
        if stdout.len() > 0 {
            msg.push_str("\n--- stdout\n");
            msg.push_str(stdout);
        }
        if stderr.len() > 0 {
            msg.push_str("\n--- stderr\n");
            msg.push_str(stderr);
        }
        return Err(msg)
    }

    let mut dirs = Vec::new();
    let parts = stdout.split(' ').filter(|l| !l.is_empty() && l.len() > 2)
                      .map(|arg| (arg[0..2], arg[2..]))
                      .collect::<Vec<_>>();
    for &(flag, val) in parts.iter() {
        if flag == "-L" {
            println!("cargo:rustc-flags=-L {}", val);
            dirs.push(Path::new(val));
        }
    }
    for &(flag, val) in parts.iter() {
        if flag == "-l" {
            if options.statik && !is_system_lib(val, dirs[]) {
                println!("cargo:rustc-flags=-l {}:static", val);
            } else {
                println!("cargo:rustc-flags=-l {}", val);
            }
        }
    }
    Ok(())
}

pub fn default_options(name: &str) -> Options {
    let name = envify(name);
    let statik = if os::getenv(format!("{}_STATIC", name).as_slice()).is_some() {
        true
    } else if os::getenv(format!("{}_DYNAMIC", name).as_slice()).is_some() {
        false
    } else if os::getenv("PKG_CONFIG_ALL_STATIC").is_some() {
        true
    } else if os::getenv("PKG_CONFIG_ALL_DYNAMIC").is_some() {
        false
    } else {
        false
    };
    Options { statik: statik, atleast_version: None }
}

fn envify(name: &str) -> String {
    name.chars().map(|c| c.to_uppercase()).map(|c| if c == '-' {'_'} else {c})
        .collect()
}

fn is_system_lib(name: &str, dirs: &[Path]) -> bool {
    let libname = format!("lib{}.a", name);
    let root = Path::new("/usr");
    !dirs.iter().any(|d| {
        !root.is_ancestor_of(d) && d.join(&libname).exists()
    })
}
<|endoftext|>