<commit_before>/// The Toml Set extensions

#[cfg(feature = "typed")]
use serde::Serialize;
use toml::Value;

use crate::error::{Error, Result};
use crate::tokenizer::tokenize_with_seperator;
use crate::tokenizer::Token;

pub trait TomlValueSetExt {
    /// Extension function for setting a value in the current toml::Value document
    /// using a custom seperator
    ///
    /// # Semantics
    ///
    /// The function _never_ creates intermediate data structures (Tables or Arrays) in the
    /// document.
    ///
    /// # Return value
    ///
    /// * If the set operation worked correctly, `Ok(None)` is returned.
    /// * If the set operation replaced an existing value `Ok(Some(old_value))` is returned
    /// * On failure, `Err(e)` is returned:
    ///     * If the query is `"a.b.c"` but there is no table `"b"`: error
    ///     * If the query is `"a.b.[0]"` but "`b"` is not an array: error
    ///     * If the query is `"a.b.[3]"` but the array at "`b"` has no index `3`: error
    ///     * etc.
    ///
    fn set_with_seperator(&mut self, query: &str, sep: char, value: Value)
        -> Result<Option<Value>>;

    /// Extension function for setting a value from the current toml::Value document
    ///
    /// See documentation of `TomlValueSetExt::set_with_seperator`
    fn set(&mut self, query: &str, value: Value) -> Result<Option<Value>> {
        self.set_with_seperator(query, '.', value)
    }

    /// A convenience method for setting any arbitrary serializable value.
    #[cfg(feature = "typed")]
    fn set_serialized<S: Serialize>(&mut self, query: &str, value: S) -> Result<Option<Value>> {
        let value = Value::try_from(value).map_err(Error::TomlSerialize)?;
        self.set(query, value)
    }
}

impl TomlValueSetExt for Value {
    fn set_with_seperator(
        &mut self,
        query: &str,
        sep: char,
        value: Value,
    ) -> Result<Option<Value>> {
        use crate::resolver::mut_resolver::resolve;

        let mut tokens = tokenize_with_seperator(query, sep)?;
        let last = tokens.pop_last();

        let val = resolve(self, &tokens, true)?.unwrap(); // safe because of resolve() guarantees
        let last = last.unwrap_or_else(|| Box::new(tokens));

        match *last {
            Token::Identifier { ident, .. } => match val {
                Value::Table(ref mut t) => Ok(t.insert(ident, value)),
                Value::Array(_) => Err(Error::NoIdentifierInArray(ident)),
                _ => Err(Error::QueryingValueAsTable(ident)),
            },

            Token::Index { idx, .. } => match val {
                Value::Array(ref mut a) => {
                    if a.len() > idx {
                        let result = a.swap_remove(idx);
                        a.insert(idx, value);
                        Ok(Some(result))
                    } else {
                        a.push(value);
                        Ok(None)
                    }
                }
                Value::Table(_) => Err(Error::NoIndexInTable(idx)),
                _ => Err(Error::QueryingValueAsArray(idx)),
            },
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use toml::from_str as toml_from_str;
    use toml::Value;

    #[test]
    fn test_set_with_seperator_into_table() {
        let mut toml: Value = toml_from_str(
            r#"
        [table]
        a = 0
        "#,
        )
        .unwrap();

        let res = toml.set_with_seperator(&String::from("table.a"), '.', Value::Integer(1));

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_some());
        let res = res.unwrap();
        assert!(is_match!(res, Value::Integer(0)));

        assert!(is_match!(toml, Value::Table(_)));
        match toml {
            Value::Table(ref t) => {
                assert!(!t.is_empty());

                let inner = t.get("table");
                assert!(inner.is_some());

                let inner = inner.unwrap();
                assert!(is_match!(inner, Value::Table(_)));
                match inner {
                    Value::Table(ref t) => {
                        assert!(!t.is_empty());

                        let a = t.get("a");
                        assert!(a.is_some());

                        let a = a.unwrap();
                        assert!(is_match!(a, Value::Integer(1)));
                    }
                    _ => panic!("What just happenend?"),
                }
            }
            _ => panic!("What just happenend?"),
        }
    }

    #[test]
    fn test_set_with_seperator_into_table_key_nonexistent() {
        let mut toml: Value = toml_from_str(
            r#"
        [table]
        "#,
        )
        .unwrap();

        let res = toml.set_with_seperator(&String::from("table.a"), '.', Value::Integer(1));

        assert!(res.is_ok());
        let res = res.unwrap();

        assert!(res.is_none());

        assert!(is_match!(toml, Value::Table(_)));
        match toml {
            Value::Table(ref t) => {
                assert!(!t.is_empty());

                let inner = t.get("table");
                assert!(inner.is_some());

                let inner = inner.unwrap();
                assert!(is_match!(inner, Value::Table(_)));
                match inner {
                    Value::Table(ref t) => {
                        assert!(!t.is_empty());

                        let a = t.get("a");
                        assert!(a.is_some());

                        let a = a.unwrap();
                        assert!(is_match!(a, Value::Integer(1)));
                    }
                    _ => panic!("What just happenend?"),
                }
            }
            _ => panic!("What just happenend?"),
        }
    }

    #[test]
    fn test_set_with_seperator_into_array() {
        use std::ops::Index;

        let mut toml: Value = toml_from_str(
            r#"
        array = [ 0 ]
        "#,
        )
        .unwrap();

        let res = toml.set_with_seperator(&String::from("array.[0]"), '.', Value::Integer(1));

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_some());
        let res = res.unwrap();
        assert!(is_match!(res, Value::Integer(0)));

        assert!(is_match!(toml, Value::Table(_)));
        match toml {
            Value::Table(ref t) => {
                assert!(!t.is_empty());

                let inner = t.get("array");
                assert!(inner.is_some());

                let inner = inner.unwrap();
                assert!(is_match!(inner, Value::Array(_)));
                match inner {
                    Value::Array(ref a) => {
                        assert!(!a.is_empty());
                        assert!(is_match!(a.index(0), Value::Integer(1)));
                    }
                    _ => panic!("What just happenend?"),
                }
            }
            _ => panic!("What just happenend?"),
        }
    }

    #[test]
    fn test_set_with_seperator_into_table_index_nonexistent() {
        use std::ops::Index;

        let mut toml: Value = toml_from_str(
            r#"
        array = []
        "#,
        )
        .unwrap();

        let res = toml.set_with_seperator(&String::from("array.[0]"), '.', Value::Integer(1));

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_none());

        assert!(is_match!(toml, Value::Table(_)));
        match toml {
            Value::Table(ref t) => {
                assert!(!t.is_empty());

                let inner = t.get("array");
                assert!(inner.is_some());

                let inner = inner.unwrap();
                assert!(is_match!(inner, Value::Array(_)));
                match inner {
                    Value::Array(ref a) => {
                        assert!(!a.is_empty());
                        assert!(is_match!(a.index(0), Value::Integer(1)));
                    }
                    _ => panic!("What just happenend?"),
                }
            }
            _ => panic!("What just happenend?"),
        }
    }

    #[test]
    fn test_set_with_seperator_into_nested_table() {
        let mut toml: Value = toml_from_str(
            r#"
        [a.b.c]
        d = 0
        "#,
        )
        .unwrap();

        let res = toml.set_with_seperator(&String::from("a.b.c.d"), '.', Value::Integer(1));

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_some());
        let res = res.unwrap();
        assert!(is_match!(res, Value::Integer(0)));

        assert!(is_match!(toml, Value::Table(_)));
        match toml {
            Value::Table(ref t) => {
                assert!(!t.is_empty());

                let a = t.get("a");
                assert!(a.is_some());

                let a = a.unwrap();
                assert!(is_match!(a, Value::Table(_)));
                match a {
                    Value::Table(ref a) => {
                        assert!(!a.is_empty());

                        let b_tab = a.get("b");
                        assert!(b_tab.is_some());

                        let b_tab = b_tab.unwrap();
                        assert!(is_match!(b_tab, Value::Table(_)));
                        match b_tab {
                            Value::Table(ref b) => {
                                assert!(!b.is_empty());

                                let c_tab = b.get("c");
                                assert!(c_tab.is_some());

                                let c_tab = c_tab.unwrap();
                                assert!(is_match!(c_tab, Value::Table(_)));
                                match c_tab {
                                    Value::Table(ref c) => {
                                        assert!(!c.is_empty());

                                        let d = c.get("d");
                                        assert!(d.is_some());

                                        let d = d.unwrap();
                                        assert!(is_match!(d, Value::Integer(1)));
                                    }
                                    _ => panic!("What just happenend?"),
                                }
                            }
                            _ => panic!("What just happenend?"),
                        }
                    }
                    _ => panic!("What just happenend?"),
                }
            }
            _ => panic!("What just happenend?"),
        }
    }

    #[test]
    fn test_set_with_seperator_into_nonexistent_table() {
        let mut toml: Value = toml_from_str("").unwrap();

        let res = toml.set_with_seperator(&String::from("table.a"), '.', Value::Integer(1));

        assert!(res.is_err());

        let res = res.unwrap_err();
        assert!(is_match!(res, Error::IdentifierNotFoundInDocument(_)));
    }

    #[test]
    fn test_set_with_seperator_into_nonexistent_array() {
        let mut toml: Value = toml_from_str("").unwrap();

        let res = toml.set_with_seperator(&String::from("[0]"), '.', Value::Integer(1));

        assert!(res.is_err());

        let res = res.unwrap_err();
        assert!(is_match!(res, Error::NoIndexInTable(0)));
    }

    #[test]
    fn test_set_with_seperator_ident_into_ary() {
        let mut toml: Value = toml_from_str(
            r#"
        array = [ 0 ]
        "#,
        )
        .unwrap();

        let res = toml.set_with_seperator(&String::from("array.foo"), '.', Value::Integer(2));

        assert!(res.is_err());
        let res = res.unwrap_err();

        assert!(is_match!(res, Error::NoIdentifierInArray(_)));
    }

    #[test]
    fn test_set_with_seperator_index_into_table() {
        let mut toml: Value = toml_from_str(
            r#"
        foo = { bar = 1 }
        "#,
        )
        .unwrap();

        let res = toml.set_with_seperator(&String::from("foo.[0]"), '.', Value::Integer(2));

        assert!(res.is_err());
        let res = res.unwrap_err();

        assert!(is_match!(res, Error::NoIndexInTable(_)));
    }

    #[test]
    fn test_set_with_seperator_ident_into_non_structure() {
        let mut toml: Value = toml_from_str(
            r#"
        val = 0
        "#,
        )
        .unwrap();

        let res = toml.set_with_seperator(&String::from("val.foo"), '.', Value::Integer(2));

        assert!(res.is_err());
        let res = res.unwrap_err();

        assert!(is_match!(res, Error::QueryingValueAsTable(_)));
    }

    #[test]
    fn test_set_with_seperator_index_into_non_structure() {
        let mut toml: Value = toml_from_str(
            r#"
        foo = 1
        "#,
        )
        .unwrap();

        let res = toml.set_with_seperator(&String::from("foo.[0]"), '.', Value::Integer(2));

        assert!(res.is_err());
        let res = res.unwrap_err();

        assert!(is_match!(res, Error::QueryingValueAsArray(_)));
    }

    #[cfg(feature = "typed")]
    #[test]
    fn test_serialize() {
        use crate::insert::TomlValueInsertExt;
        use toml::map::Map;

        #[derive(Serialize, Deserialize, Debug)]
        struct Test {
            a: u64,
            s: String,
        }

        let mut toml = Value::Table(Map::new());
        let test = Test {
            a: 15,
            s: String::from("Helloworld"),
        };

        assert!(toml
            .insert_serialized("table.value", test)
            .unwrap()
            .is_none());

        eprintln!("{:#}", toml);

        match toml {
            Value::Table(ref tab) => match tab.get("table").unwrap() {
                Value::Table(ref inner) => match inner.get("value").unwrap() {
                    Value::Table(ref data) => {
                        assert!(is_match!(data.get("a").unwrap(), Value::Integer(15)));
                        match data.get("s").unwrap() {
                            Value::String(ref s) => assert_eq!(s, "Helloworld"),
                            _ => unreachable!(),
                        };
                    }
                    _ => unreachable!(),
                },
                _ => unreachable!(),
            },
            _ => unreachable!(),
        }
    }
}
<commit_msg>Allow high cognitive complexity in test functions<commit_after>/// The Toml Set extensions

#[cfg(feature = "typed")]
use serde::Serialize;
use toml::Value;

use crate::error::{Error, Result};
use crate::tokenizer::tokenize_with_seperator;
use crate::tokenizer::Token;

pub trait TomlValueSetExt {
    /// Extension function for setting a value in the current toml::Value document
    /// using a custom seperator
    ///
    /// # Semantics
    ///
    /// The function _never_ creates intermediate data structures (Tables or Arrays) in the
    /// document.
    ///
    /// # Return value
    ///
    /// * If the set operation worked correctly, `Ok(None)` is returned.
    /// * If the set operation replaced an existing value `Ok(Some(old_value))` is returned
    /// * On failure, `Err(e)` is returned:
    ///     * If the query is `"a.b.c"` but there is no table `"b"`: error
    ///     * If the query is `"a.b.[0]"` but "`b"` is not an array: error
    ///     * If the query is `"a.b.[3]"` but the array at "`b"` has no index `3`: error
    ///     * etc.
    ///
    fn set_with_seperator(&mut self, query: &str, sep: char, value: Value)
        -> Result<Option<Value>>;

    /// Extension function for setting a value from the current toml::Value document
    ///
    /// See documentation of `TomlValueSetExt::set_with_seperator`
    fn set(&mut self, query: &str, value: Value) -> Result<Option<Value>> {
        self.set_with_seperator(query, '.', value)
    }

    /// A convenience method for setting any arbitrary serializable value.
    #[cfg(feature = "typed")]
    fn set_serialized<S: Serialize>(&mut self, query: &str, value: S) -> Result<Option<Value>> {
        let value = Value::try_from(value).map_err(Error::TomlSerialize)?;
        self.set(query, value)
    }
}

impl TomlValueSetExt for Value {
    fn set_with_seperator(
        &mut self,
        query: &str,
        sep: char,
        value: Value,
    ) -> Result<Option<Value>> {
        use crate::resolver::mut_resolver::resolve;

        let mut tokens = tokenize_with_seperator(query, sep)?;
        let last = tokens.pop_last();

        let val = resolve(self, &tokens, true)?.unwrap(); // safe because of resolve() guarantees
        let last = last.unwrap_or_else(|| Box::new(tokens));

        match *last {
            Token::Identifier { ident, .. } => match val {
                Value::Table(ref mut t) => Ok(t.insert(ident, value)),
                Value::Array(_) => Err(Error::NoIdentifierInArray(ident)),
                _ => Err(Error::QueryingValueAsTable(ident)),
            },

            Token::Index { idx, .. } => match val {
                Value::Array(ref mut a) => {
                    if a.len() > idx {
                        let result = a.swap_remove(idx);
                        a.insert(idx, value);
                        Ok(Some(result))
                    } else {
                        a.push(value);
                        Ok(None)
                    }
                }
                Value::Table(_) => Err(Error::NoIndexInTable(idx)),
                _ => Err(Error::QueryingValueAsArray(idx)),
            },
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use toml::from_str as toml_from_str;
    use toml::Value;

    #[test]
    fn test_set_with_seperator_into_table() {
        let mut toml: Value = toml_from_str(
            r#"
        [table]
        a = 0
        "#,
        )
        .unwrap();

        let res = toml.set_with_seperator(&String::from("table.a"), '.', Value::Integer(1));

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_some());
        let res = res.unwrap();
        assert!(is_match!(res, Value::Integer(0)));

        assert!(is_match!(toml, Value::Table(_)));
        match toml {
            Value::Table(ref t) => {
                assert!(!t.is_empty());

                let inner = t.get("table");
                assert!(inner.is_some());

                let inner = inner.unwrap();
                assert!(is_match!(inner, Value::Table(_)));
                match inner {
                    Value::Table(ref t) => {
                        assert!(!t.is_empty());

                        let a = t.get("a");
                        assert!(a.is_some());

                        let a = a.unwrap();
                        assert!(is_match!(a, Value::Integer(1)));
                    }
                    _ => panic!("What just happenend?"),
                }
            }
            _ => panic!("What just happenend?"),
        }
    }

    #[test]
    fn test_set_with_seperator_into_table_key_nonexistent() {
        let mut toml: Value = toml_from_str(
            r#"
        [table]
        "#,
        )
        .unwrap();

        let res = toml.set_with_seperator(&String::from("table.a"), '.', Value::Integer(1));

        assert!(res.is_ok());
        let res = res.unwrap();

        assert!(res.is_none());

        assert!(is_match!(toml, Value::Table(_)));
        match toml {
            Value::Table(ref t) => {
                assert!(!t.is_empty());

                let inner = t.get("table");
                assert!(inner.is_some());

                let inner = inner.unwrap();
                assert!(is_match!(inner, Value::Table(_)));
                match inner {
                    Value::Table(ref t) => {
                        assert!(!t.is_empty());

                        let a = t.get("a");
                        assert!(a.is_some());

                        let a = a.unwrap();
                        assert!(is_match!(a, Value::Integer(1)));
                    }
                    _ => panic!("What just happenend?"),
                }
            }
            _ => panic!("What just happenend?"),
        }
    }

    #[test]
    fn test_set_with_seperator_into_array() {
        use std::ops::Index;

        let mut toml: Value = toml_from_str(
            r#"
        array = [ 0 ]
        "#,
        )
        .unwrap();

        let res = toml.set_with_seperator(&String::from("array.[0]"), '.', Value::Integer(1));

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_some());
        let res = res.unwrap();
        assert!(is_match!(res, Value::Integer(0)));

        assert!(is_match!(toml, Value::Table(_)));
        match toml {
            Value::Table(ref t) => {
                assert!(!t.is_empty());

                let inner = t.get("array");
                assert!(inner.is_some());

                let inner = inner.unwrap();
                assert!(is_match!(inner, Value::Array(_)));
                match inner {
                    Value::Array(ref a) => {
                        assert!(!a.is_empty());
                        assert!(is_match!(a.index(0), Value::Integer(1)));
                    }
                    _ => panic!("What just happenend?"),
                }
            }
            _ => panic!("What just happenend?"),
        }
    }

    #[test]
    fn test_set_with_seperator_into_table_index_nonexistent() {
        use std::ops::Index;

        let mut toml: Value = toml_from_str(
            r#"
        array = []
        "#,
        )
        .unwrap();

        let res = toml.set_with_seperator(&String::from("array.[0]"), '.', Value::Integer(1));

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_none());

        assert!(is_match!(toml, Value::Table(_)));
        match toml {
            Value::Table(ref t) => {
                assert!(!t.is_empty());

                let inner = t.get("array");
                assert!(inner.is_some());

                let inner = inner.unwrap();
                assert!(is_match!(inner, Value::Array(_)));
                match inner {
                    Value::Array(ref a) => {
                        assert!(!a.is_empty());
                        assert!(is_match!(a.index(0), Value::Integer(1)));
                    }
                    _ => panic!("What just happenend?"),
                }
            }
            _ => panic!("What just happenend?"),
        }
    }

    #[test]
    #[allow(clippy::cognitive_complexity)]
    fn test_set_with_seperator_into_nested_table() {
        let mut toml: Value = toml_from_str(
            r#"
        [a.b.c]
        d = 0
        "#,
        )
        .unwrap();

        let res = toml.set_with_seperator(&String::from("a.b.c.d"), '.', Value::Integer(1));

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_some());
        let res = res.unwrap();
        assert!(is_match!(res, Value::Integer(0)));

        assert!(is_match!(toml, Value::Table(_)));
        match toml {
            Value::Table(ref t) => {
                assert!(!t.is_empty());

                let a = t.get("a");
                assert!(a.is_some());

                let a = a.unwrap();
                assert!(is_match!(a, Value::Table(_)));
                match a {
                    Value::Table(ref a) => {
                        assert!(!a.is_empty());

                        let b_tab = a.get("b");
                        assert!(b_tab.is_some());

                        let b_tab = b_tab.unwrap();
                        assert!(is_match!(b_tab, Value::Table(_)));
                        match b_tab {
                            Value::Table(ref b) => {
                                assert!(!b.is_empty());

                                let c_tab = b.get("c");
                                assert!(c_tab.is_some());

                                let c_tab = c_tab.unwrap();
                                assert!(is_match!(c_tab, Value::Table(_)));
                                match c_tab {
                                    Value::Table(ref c) => {
                                        assert!(!c.is_empty());

                                        let d = c.get("d");
                                        assert!(d.is_some());

                                        let d = d.unwrap();
                                        assert!(is_match!(d, Value::Integer(1)));
                                    }
                                    _ => panic!("What just happenend?"),
                                }
                            }
                            _ => panic!("What just happenend?"),
                        }
                    }
                    _ => panic!("What just happenend?"),
                }
            }
            _ => panic!("What just happenend?"),
        }
    }

    #[test]
    fn test_set_with_seperator_into_nonexistent_table() {
        let mut toml: Value = toml_from_str("").unwrap();

        let res = toml.set_with_seperator(&String::from("table.a"), '.', Value::Integer(1));

        assert!(res.is_err());

        let res = res.unwrap_err();
        assert!(is_match!(res, Error::IdentifierNotFoundInDocument(_)));
    }

    #[test]
    fn test_set_with_seperator_into_nonexistent_array() {
        let mut toml: Value = toml_from_str("").unwrap();

        let res = toml.set_with_seperator(&String::from("[0]"), '.', Value::Integer(1));

        assert!(res.is_err());

        let res = res.unwrap_err();
        assert!(is_match!(res, Error::NoIndexInTable(0)));
    }

    #[test]
    fn test_set_with_seperator_ident_into_ary() {
        let mut toml: Value = toml_from_str(
            r#"
        array = [ 0 ]
        "#,
        )
        .unwrap();

        let res = toml.set_with_seperator(&String::from("array.foo"), '.', Value::Integer(2));

        assert!(res.is_err());
        let res = res.unwrap_err();

        assert!(is_match!(res, Error::NoIdentifierInArray(_)));
    }

    #[test]
    fn test_set_with_seperator_index_into_table() {
        let mut toml: Value = toml_from_str(
            r#"
        foo = { bar = 1 }
        "#,
        )
        .unwrap();

        let res = toml.set_with_seperator(&String::from("foo.[0]"), '.', Value::Integer(2));

        assert!(res.is_err());
        let res = res.unwrap_err();

        assert!(is_match!(res, Error::NoIndexInTable(_)));
    }

    #[test]
    fn test_set_with_seperator_ident_into_non_structure() {
        let mut toml: Value = toml_from_str(
            r#"
        val = 0
        "#,
        )
        .unwrap();

        let res = toml.set_with_seperator(&String::from("val.foo"), '.', Value::Integer(2));

        assert!(res.is_err());
        let res = res.unwrap_err();

        assert!(is_match!(res, Error::QueryingValueAsTable(_)));
    }

    #[test]
    fn test_set_with_seperator_index_into_non_structure() {
        let mut toml: Value = toml_from_str(
            r#"
        foo = 1
        "#,
        )
        .unwrap();

        let res = toml.set_with_seperator(&String::from("foo.[0]"), '.', Value::Integer(2));

        assert!(res.is_err());
        let res = res.unwrap_err();

        assert!(is_match!(res, Error::QueryingValueAsArray(_)));
    }

    #[cfg(feature = "typed")]
    #[test]
    fn test_serialize() {
        use crate::insert::TomlValueInsertExt;
        use toml::map::Map;

        #[derive(Serialize, Deserialize, Debug)]
        struct Test {
            a: u64,
            s: String,
        }

        let mut toml = Value::Table(Map::new());
        let test = Test {
            a: 15,
            s: String::from("Helloworld"),
        };

        assert!(toml
            .insert_serialized("table.value", test)
            .unwrap()
            .is_none());

        eprintln!("{:#}", toml);

        match toml {
            Value::Table(ref tab) => match tab.get("table").unwrap() {
                Value::Table(ref inner) => match inner.get("value").unwrap() {
                    Value::Table(ref data) => {
                        assert!(is_match!(data.get("a").unwrap(), Value::Integer(15)));
                        match data.get("s").unwrap() {
                            Value::String(ref s) => assert_eq!(s, "Helloworld"),
                            _ => unreachable!(),
                        };
                    }
                    _ => unreachable!(),
                },
                _ => unreachable!(),
            },
            _ => unreachable!(),
        }
    }
}
<|endoftext|>


<commit_before>//! Module `tcp` provides support for channels that communicate
//! over TCP.

use serialize::{Decodable, Encodable};
use serialize::json::{Decoder, DecoderError, decode, Encoder};
use std::io::{IoError, IoErrorKind, IoResult, TcpStream};
use std::io::net::ip::ToSocketAddr;
use std::io::net::tcp::TcpAcceptor;
use std::sync::{Arc, Mutex};
use super::ReceiverError;

/// `TcpSender` is the sender half of a TCP connection.
pub struct TcpSender<T>(Sender<T>);

impl<T> TcpSender<T> where T: Encodable<Encoder<'static>, IoError> + Send {
    /// Create a new TcpSender (aka TCP client) for the specified address. It will
    /// fail if no TcpReceiver (aka TCP server) is waiting to receive the connection.
    #[allow(unused_must_use)]
    pub fn new<A: ToSocketAddr>(addr: A) -> IoResult<TcpSender<T>> {
        let (tx, rx) = channel();
        let mut stream = try!(TcpStream::connect(addr));
        spawn(proc() {
            for t in rx.iter() {
                let e = Encoder::buffer_encode(&t);
                stream.write_le_uint(e.len());
                stream.write(e.as_slice());
                stream.flush();
            }
        });
        Ok(TcpSender(tx))
    }
}

impl<T> super::Sender<T> for TcpSender<T> where T: Encodable<Encoder<'static>, IoError> + Send {
    /// Non-blocking send along the channel.
    fn send(&mut self, t: T) {
        self.0.send(t);
    }
}

/// TcpReceiver is the receiver half of a TCP connection.
pub struct TcpReceiver<T> {
    streams: Arc<Mutex<Vec<TcpStream>>>,
    acceptor: TcpAcceptor,
    closed: bool,
}

impl<T> TcpReceiver<T> {
    /// Create a new TcpReceiver (aka TCP server) bound to the specified address.
    #[allow(unused_must_use)]
    pub fn new<A: ToSocketAddr>(addr: A) -> IoResult<TcpReceiver<T>> {
        use std::io::{Acceptor, Listener};
        use std::io::net::tcp::TcpListener;

        let streams = Arc::new(Mutex::new(Vec::new()));

        let listener = try!(TcpListener::bind(addr));
        let acceptor = try!(listener.listen());
        {
            let streams = streams.clone();
            let mut acceptor = acceptor.clone();
            spawn(proc() {
                for stream in acceptor.incoming() {
                    let mut streams = streams.lock();
                    match stream {
                        Ok(stream) => streams.push(stream),
                        Err(ref e) if e.kind == IoErrorKind::EndOfFile => return,
                        Err(e) => panic!("{}", e),
                    }
                }
            });
        }

        Ok(TcpReceiver{ streams: streams, acceptor: acceptor, closed: false })
    }
}

impl<T> super::Receiver<T> for TcpReceiver<T> where T: Decodable<Decoder, DecoderError> {
    /// Attempt to receive a value on the channel. This method blocks until a value
    /// is available.
    fn try_recv(&mut self) -> Result<T, ReceiverError> {
        use std::task;
        let mut finished = Vec::new();
        loop {
            {
                if self.closed {
                    return Err(ReceiverError::EndOfFile);
                }
                let mut streams = self.streams.lock();
                let mut found = None;
                for (i, stream) in streams.iter_mut().enumerate() {
                    stream.set_read_timeout(Some(10)); // TODO: set this value as a config?
                    let size = match stream.read_le_uint() {
                        Ok(size) => size,
                        Err(ref e) if e.kind == IoErrorKind::TimedOut => continue,
                        Err(ref e) if e.kind == IoErrorKind::EndOfFile => { finished.push(i); continue },
                        Err(e) => return Err(ReceiverError::IoError(e)),
                    };
                    let data = try!(stream.read_exact(size));
                    let string = try!(String::from_utf8(data));
                    let decoded = try!(decode::<T>(string.as_slice()));
                    found = Some(decoded);
                    break;
                }
                if finished.len() > 0 {
                    let mut offset = 0u;
                    for i in finished.iter() {
                        streams.remove(*i - offset);
                        offset += 1;
                    }
                    finished.clear();
                }
                if let Some(decoded) = found {
                    return Ok(decoded);
                }
            }
            task::deschedule();
        }
    }
}

#[unsafe_destructor]
impl<T> Drop for TcpReceiver<T> {
    #[allow(unused_must_use)]
    fn drop(&mut self) {
        self.acceptor.close_accept();
        self.closed = true;
    }
}

#[cfg(test)]
mod test {
    use super::super::{Sender, Receiver};
    use super::{TcpSender, TcpReceiver};

    #[deriving(Encodable, Decodable)]
    enum MyEnum {
        NoValue,
        IntValue(int),
    }

    #[test]
    fn send_and_recv_string() {
        const ADDR: &'static str = "127.0.0.1:8080";
        let mut receiver: TcpReceiver<String> = TcpReceiver::new(ADDR).unwrap();
        let mut sender: TcpSender<String> = TcpSender::new(ADDR).unwrap();

        sender.send("hello superchan!".into_string());
        assert_eq!("hello superchan!".into_string(), receiver.recv());
    }

    #[test]
    fn send_and_recv_int() {
        const ADDR: &'static str = "127.0.0.1:8081";
        let mut receiver: TcpReceiver<int> = TcpReceiver::new(ADDR).unwrap();
        let mut sender: TcpSender<int> = TcpSender::new(ADDR).unwrap();

        sender.send(-13);
        assert_eq!(-13, receiver.recv());
    }

    #[test]
    fn send_and_recv_custom() {
        const ADDR: &'static str = "127.0.0.1:8082";
        let mut receiver: TcpReceiver<MyEnum> = TcpReceiver::new(ADDR).unwrap();
        let mut sender: TcpSender<MyEnum> = TcpSender::new(ADDR).unwrap();

        sender.send(MyEnum::IntValue(3));
        match receiver.recv() {
            MyEnum::IntValue(val) => assert_eq!(val, 3),
            _ => panic!("received unexpected MyEnum value"),
        }
    }

    #[test]
    fn multi_send() {
        const ADDR: &'static str = "127.0.0.1:8083";
        let mut receiver: TcpReceiver<int> = TcpReceiver::new(ADDR).unwrap();
        let mut sender1: TcpSender<int> = TcpSender::new(ADDR).unwrap();
        let mut sender2: TcpSender<int> = TcpSender::new(ADDR).unwrap();

        sender1.send(1);
        sender2.send(2);
        let (val1, val2) = (receiver.recv(), receiver.recv());
        assert!((val1 == 1 && val2 == 2) || (val1 == 2 && val2 == 1));
    }
}
<commit_msg>Make `std::comm` usages more explicit.<commit_after>//! Module `tcp` provides support for channels that communicate
//! over TCP.

use serialize::{Decodable, Encodable};
use serialize::json::{Decoder, DecoderError, decode, Encoder};
use std::comm;
use std::io::{IoError, IoErrorKind, IoResult, TcpStream};
use std::io::net::ip::ToSocketAddr;
use std::io::net::tcp::TcpAcceptor;
use std::sync::{Arc, Mutex};
use super::ReceiverError;

/// `TcpSender` is the sender half of a TCP connection.
pub struct TcpSender<T>(comm::Sender<T>);

impl<T> TcpSender<T> where T: Encodable<Encoder<'static>, IoError> + Send {
    /// Create a new TcpSender (aka TCP client) for the specified address. It will
    /// fail if no TcpReceiver (aka TCP server) is waiting to receive the connection.
    #[allow(unused_must_use)]
    pub fn new<A: ToSocketAddr>(addr: A) -> IoResult<TcpSender<T>> {
        let (tx, rx) = comm::channel();
        let mut stream = try!(TcpStream::connect(addr));
        spawn(proc() {
            for t in rx.iter() {
                let e = Encoder::buffer_encode(&t);
                stream.write_le_uint(e.len());
                stream.write(e.as_slice());
                stream.flush();
            }
        });
        Ok(TcpSender(tx))
    }
}

impl<T> super::Sender<T> for TcpSender<T> where T: Encodable<Encoder<'static>, IoError> + Send {
    /// Non-blocking send along the channel.
    fn send(&mut self, t: T) {
        self.0.send(t);
    }
}

/// TcpReceiver is the receiver half of a TCP connection.
pub struct TcpReceiver<T> {
    streams: Arc<Mutex<Vec<TcpStream>>>,
    acceptor: TcpAcceptor,
    closed: bool,
}

impl<T> TcpReceiver<T> {
    /// Create a new TcpReceiver (aka TCP server) bound to the specified address.
    #[allow(unused_must_use)]
    pub fn new<A: ToSocketAddr>(addr: A) -> IoResult<TcpReceiver<T>> {
        use std::io::{Acceptor, Listener};
        use std::io::net::tcp::TcpListener;

        let streams = Arc::new(Mutex::new(Vec::new()));

        let listener = try!(TcpListener::bind(addr));
        let acceptor = try!(listener.listen());
        {
            let streams = streams.clone();
            let mut acceptor = acceptor.clone();
            spawn(proc() {
                for stream in acceptor.incoming() {
                    let mut streams = streams.lock();
                    match stream {
                        Ok(stream) => streams.push(stream),
                        Err(ref e) if e.kind == IoErrorKind::EndOfFile => return,
                        Err(e) => panic!("{}", e),
                    }
                }
            });
        }

        Ok(TcpReceiver{ streams: streams, acceptor: acceptor, closed: false })
    }
}

impl<T> super::Receiver<T> for TcpReceiver<T> where T: Decodable<Decoder, DecoderError> {
    /// Attempt to receive a value on the channel. This method blocks until a value
    /// is available.
    fn try_recv(&mut self) -> Result<T, ReceiverError> {
        use std::task;
        let mut finished = Vec::new();
        loop {
            {
                if self.closed {
                    return Err(ReceiverError::EndOfFile);
                }
                let mut streams = self.streams.lock();
                let mut found = None;
                for (i, stream) in streams.iter_mut().enumerate() {
                    stream.set_read_timeout(Some(10)); // TODO: set this value as a config?
                    let size = match stream.read_le_uint() {
                        Ok(size) => size,
                        Err(ref e) if e.kind == IoErrorKind::TimedOut => continue,
                        Err(ref e) if e.kind == IoErrorKind::EndOfFile => { finished.push(i); continue },
                        Err(e) => return Err(ReceiverError::IoError(e)),
                    };
                    let data = try!(stream.read_exact(size));
                    let string = try!(String::from_utf8(data));
                    let decoded = try!(decode::<T>(string.as_slice()));
                    found = Some(decoded);
                    break;
                }
                if finished.len() > 0 {
                    let mut offset = 0u;
                    for i in finished.iter() {
                        streams.remove(*i - offset);
                        offset += 1;
                    }
                    finished.clear();
                }
                if let Some(decoded) = found {
                    return Ok(decoded);
                }
            }
            task::deschedule();
        }
    }
}

#[unsafe_destructor]
impl<T> Drop for TcpReceiver<T> {
    #[allow(unused_must_use)]
    fn drop(&mut self) {
        self.acceptor.close_accept();
        self.closed = true;
    }
}

#[cfg(test)]
mod test {
    use super::super::{Sender, Receiver};
    use super::{TcpSender, TcpReceiver};

    #[deriving(Encodable, Decodable)]
    enum MyEnum {
        NoValue,
        IntValue(int),
    }

    #[test]
    fn send_and_recv_string() {
        const ADDR: &'static str = "127.0.0.1:8080";
        let mut receiver: TcpReceiver<String> = TcpReceiver::new(ADDR).unwrap();
        let mut sender: TcpSender<String> = TcpSender::new(ADDR).unwrap();

        sender.send("hello superchan!".into_string());
        assert_eq!("hello superchan!".into_string(), receiver.recv());
    }

    #[test]
    fn send_and_recv_int() {
        const ADDR: &'static str = "127.0.0.1:8081";
        let mut receiver: TcpReceiver<int> = TcpReceiver::new(ADDR).unwrap();
        let mut sender: TcpSender<int> = TcpSender::new(ADDR).unwrap();

        sender.send(-13);
        assert_eq!(-13, receiver.recv());
    }

    #[test]
    fn send_and_recv_custom() {
        const ADDR: &'static str = "127.0.0.1:8082";
        let mut receiver: TcpReceiver<MyEnum> = TcpReceiver::new(ADDR).unwrap();
        let mut sender: TcpSender<MyEnum> = TcpSender::new(ADDR).unwrap();

        sender.send(MyEnum::IntValue(3));
        match receiver.recv() {
            MyEnum::IntValue(val) => assert_eq!(val, 3),
            _ => panic!("received unexpected MyEnum value"),
        }
    }

    #[test]
    fn multi_send() {
        const ADDR: &'static str = "127.0.0.1:8083";
        let mut receiver: TcpReceiver<int> = TcpReceiver::new(ADDR).unwrap();
        let mut sender1: TcpSender<int> = TcpSender::new(ADDR).unwrap();
        let mut sender2: TcpSender<int> = TcpSender::new(ADDR).unwrap();

        sender1.send(1);
        sender2.send(2);
        let (val1, val2) = (receiver.recv(), receiver.recv());
        assert!((val1 == 1 && val2 == 2) || (val1 == 2 && val2 == 1));
    }
}
<|endoftext|>


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

use std::cell::RefCell;
use std::io;
use std::net::{ToSocketAddrs, UdpSocket};
use libc;

use IntoInner;
use socket::Socket;

pub struct UdpBuilder {
    socket: RefCell<Option<Socket>>,
}

impl UdpBuilder {
    /// Constructs a new UdpBuilder with the `AF_INET` domain, the `SOCK_DGRAM`
    /// type, and with a protocol argument of 0.
    ///
    /// Note that passing other kinds of flags or arguments can be done through
    /// the `FromRaw{Fd,Socket}` implementation.
    pub fn new_v4() -> io::Result<UdpBuilder> {
        Socket::new(libc::AF_INET, libc::SOCK_DGRAM).map(::FromInner::from_inner)
    }

    /// Constructs a new UdpBuilder with the `AF_INET6` domain, the `SOCK_DGRAM`
    /// type, and with a protocol argument of 0.
    ///
    /// Note that passing other kinds of flags or arguments can be done through
    /// the `FromRaw{Fd,Socket}` implementation.
    pub fn new_v6() -> io::Result<UdpBuilder> {
        Socket::new(libc::AF_INET6, libc::SOCK_DGRAM).map(::FromInner::from_inner)
    }

    /// Binds this socket to the specified address.
    ///
    /// This function directly corresponds to the bind(2) function on Windows
    /// and Unix.
    pub fn bind<T>(&self, addr: T) -> io::Result<UdpSocket>
        where T: ToSocketAddrs
    {
        try!(self.with_socket(|sock| {
            let addr = try!(::one_addr(addr));
            sock.connect(&addr)
        }));
        Ok(self.socket.borrow_mut().take().unwrap().into_inner().into_udp_socket())
    }

    fn with_socket<F>(&self, f: F) -> io::Result<()>
        where F: FnOnce(&Socket) -> io::Result<()>
    {
        match *self.socket.borrow() {
            Some(ref s) => f(s),
            None => Err(io::Error::new(io::ErrorKind::Other,
                                       "builder has already finished its socket")),
        }
    }
}

impl ::AsInner for UdpBuilder {
    type Inner = RefCell<Option<Socket>>;
    fn as_inner(&self) -> &RefCell<Option<Socket>> { &self.socket }
}

impl ::FromInner for UdpBuilder {
    type Inner = Socket;
    fn from_inner(sock: Socket) -> UdpBuilder {
        UdpBuilder { socket: RefCell::new(Some(sock)) }
    }
}

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

use std::cell::RefCell;
use std::io;
use std::net::{ToSocketAddrs, UdpSocket};
use libc;

use IntoInner;
use socket::Socket;

pub struct UdpBuilder {
    socket: RefCell<Option<Socket>>,
}

impl UdpBuilder {
    /// Constructs a new UdpBuilder with the `AF_INET` domain, the `SOCK_DGRAM`
    /// type, and with a protocol argument of 0.
    ///
    /// Note that passing other kinds of flags or arguments can be done through
    /// the `FromRaw{Fd,Socket}` implementation.
    pub fn new_v4() -> io::Result<UdpBuilder> {
        Socket::new(libc::AF_INET, libc::SOCK_DGRAM).map(::FromInner::from_inner)
    }

    /// Constructs a new UdpBuilder with the `AF_INET6` domain, the `SOCK_DGRAM`
    /// type, and with a protocol argument of 0.
    ///
    /// Note that passing other kinds of flags or arguments can be done through
    /// the `FromRaw{Fd,Socket}` implementation.
    pub fn new_v6() -> io::Result<UdpBuilder> {
        Socket::new(libc::AF_INET6, libc::SOCK_DGRAM).map(::FromInner::from_inner)
    }

    /// Binds this socket to the specified address.
    ///
    /// This function directly corresponds to the bind(2) function on Windows
    /// and Unix.
    pub fn bind<T>(&self, addr: T) -> io::Result<UdpSocket>
        where T: ToSocketAddrs
    {
        try!(self.with_socket(|sock| {
            let addr = try!(::one_addr(addr));
            sock.bind(&addr)
        }));
        Ok(self.socket.borrow_mut().take().unwrap().into_inner().into_udp_socket())
    }

    fn with_socket<F>(&self, f: F) -> io::Result<()>
        where F: FnOnce(&Socket) -> io::Result<()>
    {
        match *self.socket.borrow() {
            Some(ref s) => f(s),
            None => Err(io::Error::new(io::ErrorKind::Other,
                                       "builder has already finished its socket")),
        }
    }
}

impl ::AsInner for UdpBuilder {
    type Inner = RefCell<Option<Socket>>;
    fn as_inner(&self) -> &RefCell<Option<Socket>> { &self.socket }
}

impl ::FromInner for UdpBuilder {
    type Inner = Socket;
    fn from_inner(sock: Socket) -> UdpBuilder {
        UdpBuilder { socket: RefCell::new(Some(sock)) }
    }
}

<|endoftext|>


<commit_before>use super::*;
use crate::rpcclient::RpcClient;
use crate::sign::Sign;
use crate::viewport::Viewport;

/// Try get value of an variable from RPC params.
pub fn try_get<R: DeserializeOwned>(key: &str, params: &Value) -> Fallible<Option<R>> {
    let value = &params[key];
    if value == &Value::Null {
        Ok(None)
    } else {
        Ok(serde_json::from_value(value.clone())?)
    }
}

#[derive(Clone)]
pub struct Vim {
    pub rpcclient: Arc<RpcClient>,
}

impl Vim {
    pub fn new(rpcclient: Arc<RpcClient>) -> Self {
        Self { rpcclient }
    }

    /// Fundamental functions.

    pub fn command(&self, cmds: impl Serialize) -> Fallible<()> {
        self.rpcclient.notify("s:command", &cmds)
    }

    pub fn eval<E, T>(&self, exp: E) -> Fallible<T>
    where
        E: VimExp,
        T: DeserializeOwned,
    {
        self.rpcclient.call("eval", &exp.to_exp())
    }

    /// Function wrappers.

    pub fn getbufvar<R: DeserializeOwned>(&self, bufname: &str, var: &str) -> Fallible<R> {
        self.rpcclient.call("getbufvar", json!([bufname, var]))
    }

    pub fn get_filename(&self, params: &Value) -> Fallible<String> {
        let key = "filename";
        let expr = "LSP#filename()";

        try_get(key, params)?.map_or_else(|| self.eval(expr), Ok)
    }

    pub fn get_languageId(&self, filename: &str, params: &Value) -> Fallible<String> {
        let key = "languageId";
        let expr = "&filetype";

        try_get(key, params)?.map_or_else(|| self.getbufvar(filename, expr), Ok)
    }

    pub fn get_bufnr(&self, filename: &str, params: &Value) -> Fallible<Bufnr> {
        let key = "bufnr";

        try_get(key, params)?.map_or_else(|| self.eval(format!("bufnr('{}')", filename)), Ok)
    }

    pub fn get_viewport(&self, params: &Value) -> Fallible<Viewport> {
        let key = "viewport";
        let expr = "LSP#viewport()";

        try_get(key, params)?.map_or_else(|| self.eval(expr), Ok)
    }

    pub fn get_position(&self, params: &Value) -> Fallible<Position> {
        let key = "position";
        let expr = "LSP#position()";

        try_get(key, params)?.map_or_else(|| self.eval(expr), Ok)
    }

    pub fn get_current_word(&self, params: &Value) -> Fallible<String> {
        let key = "cword";
        let expr = "expand('<cword>')";

        try_get(key, params)?.map_or_else(|| self.eval(expr), Ok)
    }

    pub fn get_goto_cmd(&self, params: &Value) -> Fallible<Option<String>> {
        let key = "gotoCmd";

        try_get(key, params)
    }

    pub fn get_tab_size(&self) -> Fallible<u64> {
        let expr = "shiftwidth()";

        self.eval(expr)
    }

    pub fn get_insert_spaces(&self, filename: &str) -> Fallible<bool> {
        let insert_spaces: i8 = self.getbufvar(filename, "&expandtab")?;
        Ok(insert_spaces == 1)
    }

    pub fn get_text(&self, bufname: &str) -> Fallible<Vec<String>> {
        self.rpcclient.call("LSP#text", json!([bufname]))
    }

    pub fn get_handle(&self, params: &Value) -> Fallible<bool> {
        let key = "handle";

        try_get(key, params)?.map_or_else(|| Ok(true), Ok)
    }

    pub fn echo(&self, message: impl AsRef<str>) -> Fallible<()> {
        self.rpcclient.notify("s:Echo", message.as_ref())
    }

    pub fn echo_ellipsis(&self, message: impl AsRef<str>) -> Fallible<()> {
        let message = message.as_ref().lines().collect::<Vec<_>>().join(" ");
        self.rpcclient.notify("s:EchoEllipsis", message)
    }

    pub fn echomsg_ellipsis(&self, message: impl AsRef<str>) -> Fallible<()> {
        let message = message.as_ref().lines().collect::<Vec<_>>().join(" ");
        self.rpcclient.notify("s:EchomsgEllipsis", message)
    }

    pub fn echomsg(&self, message: impl AsRef<str>) -> Fallible<()> {
        self.rpcclient.notify("s:Echomsg", message.as_ref())
    }

    pub fn echoerr(&self, message: impl AsRef<str>) -> Fallible<()> {
        self.rpcclient.notify("s:Echoerr", message.as_ref())
    }

    pub fn echowarn(&self, message: impl AsRef<str>) -> Fallible<()> {
        self.rpcclient.notify("s:Echowarn", message.as_ref())
    }

    pub fn cursor(&self, lnum: u64, col: u64) -> Fallible<()> {
        self.rpcclient.notify("cursor", json!([lnum, col]))
    }

    #[allow(dead_code)]
    pub fn setline(&self, lnum: u64, text: &[String]) -> Fallible<()> {
        self.rpcclient.notify("setline", json!([lnum, text]))
    }

    pub fn edit(&self, goto_cmd: &Option<String>, path: impl AsRef<Path>) -> Fallible<()> {
        let path = path.as_ref().to_string_lossy();
        let goto = goto_cmd.as_deref().unwrap_or("edit");
        self.rpcclient.notify("s:Edit", json!([goto, path]))?;
        Ok(())
    }

    pub fn setqflist(&self, list: &[QuickfixEntry], action: &str, title: &str) -> Fallible<()> {
        info!("Begin setqflist");
        let parms = json!([list, action]);
        self.rpcclient.notify("setqflist", parms)?;
        let parms = json!([[], "a", { "title": title }]);
        self.rpcclient.notify("setqflist", parms)?;
        Ok(())
    }

    pub fn setloclist(&self, list: &[QuickfixEntry], action: &str, title: &str) -> Fallible<()> {
        let parms = json!([0, list, action]);
        self.rpcclient.notify("setloclist", parms)?;
        let parms = json!([0, [], "a", { "title": title }]);
        self.rpcclient.notify("setloclist", parms)?;
        Ok(())
    }

    pub fn create_namespace(&self, name: &str) -> Fallible<i64> {
        self.rpcclient.call("nvim_create_namespace", [name])
    }

    pub fn set_virtual_texts(
        &self,
        buf_id: i64,
        ns_id: i64,
        line_start: u64,
        line_end: u64,
        virtual_texts: &[VirtualText],
    ) -> Fallible<i8> {
        self.rpcclient.call(
            "s:set_virtual_texts",
            json!([buf_id, ns_id, line_start, line_end, virtual_texts]),
        )
    }

    pub fn set_signs(
        &self,
        filename: &str,
        signs_to_add: &[Sign],
        signs_to_delete: &[Sign],
    ) -> Fallible<i8> {
        self.rpcclient.call(
            "s:set_signs",
            json!([filename, signs_to_add, signs_to_delete]),
        )
    }
}
<commit_msg>Always normalize filename (#970)<commit_after>use super::*;
use crate::rpcclient::RpcClient;
use crate::sign::Sign;
use crate::viewport::Viewport;

/// Try get value of an variable from RPC params.
pub fn try_get<R: DeserializeOwned>(key: &str, params: &Value) -> Fallible<Option<R>> {
    let value = &params[key];
    if value == &Value::Null {
        Ok(None)
    } else {
        Ok(serde_json::from_value(value.clone())?)
    }
}

#[derive(Clone)]
pub struct Vim {
    pub rpcclient: Arc<RpcClient>,
}

impl Vim {
    pub fn new(rpcclient: Arc<RpcClient>) -> Self {
        Self { rpcclient }
    }

    /// Fundamental functions.

    pub fn command(&self, cmds: impl Serialize) -> Fallible<()> {
        self.rpcclient.notify("s:command", &cmds)
    }

    pub fn eval<E, T>(&self, exp: E) -> Fallible<T>
    where
        E: VimExp,
        T: DeserializeOwned,
    {
        self.rpcclient.call("eval", &exp.to_exp())
    }

    /// Function wrappers.

    pub fn getbufvar<R: DeserializeOwned>(&self, bufname: &str, var: &str) -> Fallible<R> {
        self.rpcclient.call("getbufvar", json!([bufname, var]))
    }

    pub fn get_filename(&self, params: &Value) -> Fallible<String> {
        let key = "filename";
        let expr = "LSP#filename()";

        let filename: String = try_get(key, params)?.map_or_else(|| self.eval(expr), Ok)?;
        Ok(filename.canonicalize())
    }

    pub fn get_languageId(&self, filename: &str, params: &Value) -> Fallible<String> {
        let key = "languageId";
        let expr = "&filetype";

        try_get(key, params)?.map_or_else(|| self.getbufvar(filename, expr), Ok)
    }

    pub fn get_bufnr(&self, filename: &str, params: &Value) -> Fallible<Bufnr> {
        let key = "bufnr";

        try_get(key, params)?.map_or_else(|| self.eval(format!("bufnr('{}')", filename)), Ok)
    }

    pub fn get_viewport(&self, params: &Value) -> Fallible<Viewport> {
        let key = "viewport";
        let expr = "LSP#viewport()";

        try_get(key, params)?.map_or_else(|| self.eval(expr), Ok)
    }

    pub fn get_position(&self, params: &Value) -> Fallible<Position> {
        let key = "position";
        let expr = "LSP#position()";

        try_get(key, params)?.map_or_else(|| self.eval(expr), Ok)
    }

    pub fn get_current_word(&self, params: &Value) -> Fallible<String> {
        let key = "cword";
        let expr = "expand('<cword>')";

        try_get(key, params)?.map_or_else(|| self.eval(expr), Ok)
    }

    pub fn get_goto_cmd(&self, params: &Value) -> Fallible<Option<String>> {
        let key = "gotoCmd";

        try_get(key, params)
    }

    pub fn get_tab_size(&self) -> Fallible<u64> {
        let expr = "shiftwidth()";

        self.eval(expr)
    }

    pub fn get_insert_spaces(&self, filename: &str) -> Fallible<bool> {
        let insert_spaces: i8 = self.getbufvar(filename, "&expandtab")?;
        Ok(insert_spaces == 1)
    }

    pub fn get_text(&self, bufname: &str) -> Fallible<Vec<String>> {
        self.rpcclient.call("LSP#text", json!([bufname]))
    }

    pub fn get_handle(&self, params: &Value) -> Fallible<bool> {
        let key = "handle";

        try_get(key, params)?.map_or_else(|| Ok(true), Ok)
    }

    pub fn echo(&self, message: impl AsRef<str>) -> Fallible<()> {
        self.rpcclient.notify("s:Echo", message.as_ref())
    }

    pub fn echo_ellipsis(&self, message: impl AsRef<str>) -> Fallible<()> {
        let message = message.as_ref().lines().collect::<Vec<_>>().join(" ");
        self.rpcclient.notify("s:EchoEllipsis", message)
    }

    pub fn echomsg_ellipsis(&self, message: impl AsRef<str>) -> Fallible<()> {
        let message = message.as_ref().lines().collect::<Vec<_>>().join(" ");
        self.rpcclient.notify("s:EchomsgEllipsis", message)
    }

    pub fn echomsg(&self, message: impl AsRef<str>) -> Fallible<()> {
        self.rpcclient.notify("s:Echomsg", message.as_ref())
    }

    pub fn echoerr(&self, message: impl AsRef<str>) -> Fallible<()> {
        self.rpcclient.notify("s:Echoerr", message.as_ref())
    }

    pub fn echowarn(&self, message: impl AsRef<str>) -> Fallible<()> {
        self.rpcclient.notify("s:Echowarn", message.as_ref())
    }

    pub fn cursor(&self, lnum: u64, col: u64) -> Fallible<()> {
        self.rpcclient.notify("cursor", json!([lnum, col]))
    }

    #[allow(dead_code)]
    pub fn setline(&self, lnum: u64, text: &[String]) -> Fallible<()> {
        self.rpcclient.notify("setline", json!([lnum, text]))
    }

    pub fn edit(&self, goto_cmd: &Option<String>, path: impl AsRef<Path>) -> Fallible<()> {
        let path = path.as_ref().to_string_lossy();
        let goto = goto_cmd.as_deref().unwrap_or("edit");
        self.rpcclient.notify("s:Edit", json!([goto, path]))?;
        Ok(())
    }

    pub fn setqflist(&self, list: &[QuickfixEntry], action: &str, title: &str) -> Fallible<()> {
        info!("Begin setqflist");
        let parms = json!([list, action]);
        self.rpcclient.notify("setqflist", parms)?;
        let parms = json!([[], "a", { "title": title }]);
        self.rpcclient.notify("setqflist", parms)?;
        Ok(())
    }

    pub fn setloclist(&self, list: &[QuickfixEntry], action: &str, title: &str) -> Fallible<()> {
        let parms = json!([0, list, action]);
        self.rpcclient.notify("setloclist", parms)?;
        let parms = json!([0, [], "a", { "title": title }]);
        self.rpcclient.notify("setloclist", parms)?;
        Ok(())
    }

    pub fn create_namespace(&self, name: &str) -> Fallible<i64> {
        self.rpcclient.call("nvim_create_namespace", [name])
    }

    pub fn set_virtual_texts(
        &self,
        buf_id: i64,
        ns_id: i64,
        line_start: u64,
        line_end: u64,
        virtual_texts: &[VirtualText],
    ) -> Fallible<i8> {
        self.rpcclient.call(
            "s:set_virtual_texts",
            json!([buf_id, ns_id, line_start, line_end, virtual_texts]),
        )
    }

    pub fn set_signs(
        &self,
        filename: &str,
        signs_to_add: &[Sign],
        signs_to_delete: &[Sign],
    ) -> Fallible<i8> {
        self.rpcclient.call(
            "s:set_signs",
            json!([filename, signs_to_add, signs_to_delete]),
        )
    }
}
<|endoftext|>


<commit_before>
use iron::{Iron, IronResult, Listening, status};
use iron::error::HttpResult;
use iron::response::Response;
use iron::request::Request;
use iron::middleware::Handler;
use iron::mime::Mime;

use sysinfo::{System, SystemExt};
use std::sync::{Arc, Mutex};
use SysinfoExt;
use serde_json;

const INDEX_HTML: &'static str = include_str!("index.html");

struct SysinfoIronHandler(Arc<Mutex<System>>);

impl Handler for SysinfoIronHandler {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        match req.url.path().last() {
            Some(path) => {
                if *path == "" {
                    Ok(Response::with((status::Ok,
                                       "text/html".parse::<Mime>().unwrap(),
                                       INDEX_HTML)))
                } else {
                    let mut system = self.0.lock().unwrap();
                    system.refresh_all();
                    let sysinfo = SysinfoExt::new(&system);
                    Ok(Response::with((status::Ok,
                                       "application/json".parse::<Mime>().unwrap(),
                                       serde_json::to_string(&sysinfo).unwrap_or(String::new()))))
                }
            },
            None => Ok(Response::with((status::NotFound, "Not found")))
        }
    }
}

pub fn start_web_server(sock_addr: Option<String>) -> HttpResult<Listening> {
    let system = Arc::new(Mutex::new(System::new()));
    let mut iron = Iron::new(SysinfoIronHandler(system));
    iron.threads = 4;
    iron.http(sock_addr.unwrap_or("localhost:3000".to_owned()))
}
<commit_msg>Update only every 5 seconds, whatever the number of clients<commit_after>
use iron::{Iron, IronResult, Listening, status};
use iron::error::HttpResult;
use iron::response::Response;
use iron::request::Request;
use iron::middleware::Handler;
use iron::mime::Mime;

use sysinfo::{System, SystemExt};
use std::sync::{Arc, RwLock};
use std::thread;
use std::time::Duration;
use SysinfoExt;
use serde_json;

const INDEX_HTML: &'static str = include_str!("index.html");

struct SysinfoIronHandler(Arc<RwLock<System>>);

impl Handler for SysinfoIronHandler {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        match req.url.path().last() {
            Some(path) => {
                if *path == "" {
                    Ok(Response::with((status::Ok,
                                       "text/html".parse::<Mime>().unwrap(),
                                       INDEX_HTML)))
                } else {
                    let system = self.0.read().unwrap();
                    let sysinfo = SysinfoExt::new(&system);
                    Ok(Response::with((status::Ok,
                                       "application/json".parse::<Mime>().unwrap(),
                                       serde_json::to_string(&sysinfo).unwrap_or(String::new()))))
                }
            },
            None => Ok(Response::with((status::NotFound, "Not found")))
        }
    }
}

pub fn start_web_server(sock_addr: Option<String>) -> HttpResult<Listening> {
    let system = Arc::new(RwLock::new(System::new()));
    let system_clone = system.clone();
    thread::spawn(move || {
        loop {
            {
                let mut system = system_clone.write().unwrap();
                system.refresh_all();
            }
            thread::sleep(Duration::new(5, 0));
        }
    });
    let mut iron = Iron::new(SysinfoIronHandler(system));
    iron.threads = 4;
    iron.http(sock_addr.unwrap_or("localhost:3000".to_owned()))
}
<|endoftext|>


<commit_before>use std::fmt::Write;
use proc_macro2::{TokenTree, Delimiter};
use syn;
use syn::buffer::Cursor;
use syn::parse::{Result, ParseStream};
use quote::ToTokens;

enum Chunk {
    Text( String ),
    Block( syn::Block )
}

pub struct StringifiedCode {
    chunks: Vec< Chunk >
}

impl StringifiedCode {
    fn push( &mut self, string: &str ) {
        match self.chunks.last_mut() {
            None | Some( Chunk::Block( .. ) ) => {},
            Some( Chunk::Text( ref mut buffer ) ) => {
                if buffer.chars().last().unwrap_or( ' ' ).is_alphanumeric() && string.chars().next().unwrap_or( ' ' ).is_alphanumeric() {
                    buffer.push( ' ' );
                }
                buffer.push_str( string );
                return;
            }
        }

        self.chunks.push( Chunk::Text( string.into() ) );
    }

    fn push_block( &mut self, block: syn::Block ) {
        self.chunks.push( Chunk::Block( block ) );
    }

    pub fn arg_count( &self ) -> usize {
        self.chunks.iter().filter( |chunk|
            match chunk {
                Chunk::Block( .. ) => true,
                _ => false
            }
        ).count()
    }

    pub fn code( &self, initial_placeholder_index: usize ) -> String {
        let capacity = self.chunks.iter().map( |chunk|
            match chunk {
                Chunk::Text( text ) => text.len(),
                Chunk::Block( .. ) => 4
            }
        ).fold( 0, |sum, len| sum + len );

        let mut counter = initial_placeholder_index;
        let mut output = String::with_capacity( capacity );
        for chunk in &self.chunks {
            match chunk {
                Chunk::Text( text ) => output.push_str( text ),
                Chunk::Block( _ ) => {
                    write!( output, "(${})", counter ).unwrap();
                    counter += 1;
                }
            }
        }

        output
    }
}

fn stringify< 'a >( mut cursor: Cursor< 'a >, output: &mut StringifiedCode ) -> Result< Cursor< 'a > > {
    while let Some( (tt, next) ) = cursor.token_tree() {
        cursor = match tt {
            TokenTree::Punct( ref punct ) if punct.as_char() == '@' && next.group( Delimiter::Brace ).is_some() => {
                let (tt, next_next) = next.token_tree().unwrap();
                output.push_block( syn::parse2( tt.into_token_stream() )? );
                next_next
            },
            TokenTree::Group( ref group ) => {
                let (start, end) = match group.delimiter() {
                    Delimiter::Brace => ("{", "}"),
                    Delimiter::Bracket => ("[", "]"),
                    Delimiter::Parenthesis => ("(", ")"),
                    Delimiter::None => ("", "")
                };

                output.push( start );
                let inner = cursor.group( group.delimiter() ).unwrap().0;
                stringify( inner, output )?;
                output.push( end );
                next
            },
            _ => {
                let token = tt.to_string();
                output.push( &token );
                next
            }
        };
    }

    Ok( cursor )
}

impl syn::parse::Parse for StringifiedCode {
    fn parse( input: ParseStream ) -> Result< Self > {
        input.step( |cursor| {
            let mut output = StringifiedCode {
                chunks: Vec::new()
            };
            let cursor = stringify( *cursor, &mut output )?;
            Ok( (output, cursor) )
        })
    }
}

#[cfg(test)]
mod tests {
    use super::StringifiedCode;
    use proc_macro2::TokenStream;

    fn assert_stringify( input: TokenStream, initial_placeholder: usize, expected: &str ) {
        let snippet: StringifiedCode = syn::parse2( input ).unwrap();
        assert_eq!( snippet.code( initial_placeholder ), expected );
    }

    #[test]
    fn test_stringify() {
        assert_stringify( quote! { return thing; }, 0, "return thing;" );
        assert_stringify( quote! { console.log }, 0, "console.log" );
        assert_stringify( quote! { 1.0 }, 0, "1.0" );
        assert_stringify( quote! { [ 1.0 ] }, 0, "[1.0]" );
        assert_stringify( quote! { { 1.0 } }, 0, "{1.0}" );
        assert_stringify( quote! { ( 1.0 ) }, 0, "(1.0)" );
        assert_stringify( quote! { a b }, 0, "a b" );
        assert_stringify( quote! { === }, 0, "===" );
        assert_stringify( quote! { ++i }, 0, "++i" );
        assert_stringify( quote! { i++ }, 0, "i++" );
        assert_stringify( quote! { --i }, 0, "--i" );
        assert_stringify( quote! { i-- }, 0, "i--" );
        assert_stringify( quote! { ( @{1} ); }, 0, "(($0));" );
        assert_stringify(
            quote! { console.log( "Hello!", @{1234i32} ); },
            0,
            "console.log(\"Hello!\",($0));"
        );
        assert_stringify(
            quote! { @{a}.fn( @{b} ); },
            0,
            "($0).fn(($1));"
        );
        assert_stringify(
            quote! { @{a}.fn( @{b} ); },
            1,
            "($1).fn(($2));"
        );
    }
}
<commit_msg>Fix the `js!` macro whitespace trimming<commit_after>use std::fmt::Write;
use proc_macro2::{TokenTree, Delimiter};
use syn;
use syn::buffer::Cursor;
use syn::parse::{Result, ParseStream};
use quote::ToTokens;

enum Chunk {
    Text( String ),
    Block( syn::Block )
}

pub struct StringifiedCode {
    chunks: Vec< Chunk >
}

fn can_trim_whitespace_next_to( ch: char ) -> bool {
    ch.is_whitespace() || "=+-*/%<>&^|~?:{}()[];.,".contains( ch )
}

impl StringifiedCode {
    fn push( &mut self, string: &str ) {
        match self.chunks.last_mut() {
            None | Some( Chunk::Block( .. ) ) => {},
            Some( Chunk::Text( ref mut buffer ) ) => {
                let l = buffer.chars().last().unwrap_or( ' ' );
                let r = string.chars().next().unwrap_or( ' ' );
                if !can_trim_whitespace_next_to( l ) && !can_trim_whitespace_next_to( r ) {
                    buffer.push( ' ' );
                }
                buffer.push_str( string );
                return;
            }
        }

        self.chunks.push( Chunk::Text( string.into() ) );
    }

    fn push_block( &mut self, block: syn::Block ) {
        self.chunks.push( Chunk::Block( block ) );
    }

    pub fn arg_count( &self ) -> usize {
        self.chunks.iter().filter( |chunk|
            match chunk {
                Chunk::Block( .. ) => true,
                _ => false
            }
        ).count()
    }

    pub fn code( &self, initial_placeholder_index: usize ) -> String {
        let capacity = self.chunks.iter().map( |chunk|
            match chunk {
                Chunk::Text( text ) => text.len(),
                Chunk::Block( .. ) => 4
            }
        ).fold( 0, |sum, len| sum + len );

        let mut counter = initial_placeholder_index;
        let mut output = String::with_capacity( capacity );
        for chunk in &self.chunks {
            match chunk {
                Chunk::Text( text ) => output.push_str( text ),
                Chunk::Block( _ ) => {
                    write!( output, "(${})", counter ).unwrap();
                    counter += 1;
                }
            }
        }

        output
    }
}

fn stringify< 'a >( mut cursor: Cursor< 'a >, output: &mut StringifiedCode ) -> Result< Cursor< 'a > > {
    while let Some( (tt, next) ) = cursor.token_tree() {
        cursor = match tt {
            TokenTree::Punct( ref punct ) if punct.as_char() == '@' && next.group( Delimiter::Brace ).is_some() => {
                let (tt, next_next) = next.token_tree().unwrap();
                output.push_block( syn::parse2( tt.into_token_stream() )? );
                next_next
            },
            TokenTree::Group( ref group ) => {
                let (start, end) = match group.delimiter() {
                    Delimiter::Brace => ("{", "}"),
                    Delimiter::Bracket => ("[", "]"),
                    Delimiter::Parenthesis => ("(", ")"),
                    Delimiter::None => ("", "")
                };

                output.push( start );
                let inner = cursor.group( group.delimiter() ).unwrap().0;
                stringify( inner, output )?;
                output.push( end );
                next
            },
            _ => {
                let token = tt.to_string();
                output.push( &token );
                next
            }
        };
    }

    Ok( cursor )
}

impl syn::parse::Parse for StringifiedCode {
    fn parse( input: ParseStream ) -> Result< Self > {
        input.step( |cursor| {
            let mut output = StringifiedCode {
                chunks: Vec::new()
            };
            let cursor = stringify( *cursor, &mut output )?;
            Ok( (output, cursor) )
        })
    }
}

#[cfg(test)]
mod tests {
    use super::StringifiedCode;
    use proc_macro2::TokenStream;

    fn assert_stringify( input: TokenStream, initial_placeholder: usize, expected: &str ) {
        let snippet: StringifiedCode = syn::parse2( input ).unwrap();
        assert_eq!( snippet.code( initial_placeholder ), expected );
    }

    #[test]
    fn test_stringify() {
        assert_stringify( quote! { return thing; }, 0, "return thing;" );
        assert_stringify( quote! { console.log }, 0, "console.log" );
        assert_stringify( quote! { 1.0 }, 0, "1.0" );
        assert_stringify( quote! { [ 1.0 ] }, 0, "[1.0]" );
        assert_stringify( quote! { { 1.0 } }, 0, "{1.0}" );
        assert_stringify( quote! { ( 1.0 ) }, 0, "(1.0)" );
        assert_stringify( quote! { a b }, 0, "a b" );
        assert_stringify( quote! { === }, 0, "===" );
        assert_stringify( quote! { ++i }, 0, "++i" );
        assert_stringify( quote! { i++ }, 0, "i++" );
        assert_stringify( quote! { --i }, 0, "--i" );
        assert_stringify( quote! { i-- }, 0, "i--" );
        assert_stringify( quote! { return _.sum([1, 2]); }, 0, "return _.sum([1,2]);" );
        assert_stringify( quote! { return $; }, 0, "return $;" );
        assert_stringify( quote! { ( @{1} ); }, 0, "(($0));" );
        assert_stringify(
            quote! { console.log( "Hello!", @{1234i32} ); },
            0,
            "console.log(\"Hello!\",($0));"
        );
        assert_stringify(
            quote! { @{a}.fn( @{b} ); },
            0,
            "($0).fn(($1));"
        );
        assert_stringify(
            quote! { @{a}.fn( @{b} ); },
            1,
            "($1).fn(($2));"
        );
    }
}
<|endoftext|>


<commit_before>extern crate cc;

/*

N.B. This build script is invoked by `cargo` by way of this configuration
in our Cargo.toml:

    [project]
    ...
    build = "src/cffi_build.rs"

Within, we use the `gcc` crate to compile the CFFI C sources (`native_engine.c`)
generated by `bootstrap.sh` into a (private) static lib (`libnative_engine_ffi.a`),
which then gets linked into the final `cargo build` product (the native engine binary).
This process mixes the Python module initialization function and other symbols into the
native engine binary, allowing us to address it both as an importable python module
(`from _native_engine import X`) as well as a C library (`ffi.dlopen(native_engine.so)`).

*/

use std::fs;
use std::io::{Read, Result};
use std::path::{Path, PathBuf};

fn main() {
  let mut config = cc::Build::new();

  // Don't implicitly set -Wall -Wextra because cffi generates code with warnings.
  // TODO: Consider removing this if/when https://github.com/alexcrichton/cc-rs/pull/248 lands.
  config.warnings(false);

  // N.B. The filename of this source code - at generation time - must line up 1:1 with the
  // python import name, as python keys the initialization function name off of the import name.
  let cffi_dir = Path::new("src/cffi");
  let c_path = mark_for_change_detection(cffi_dir.join("native_engine.c"));
  let env_script_path = mark_for_change_detection(cffi_dir.join("native_engine.cflags"));

  config.file(c_path.to_str().unwrap());
  for flag in make_flags(&env_script_path).unwrap() {
    config.flag(flag.as_str());
  }

  config.compile("libnative_engine_ffi.a");
}

fn mark_for_change_detection(path: PathBuf) -> PathBuf {
  // Restrict re-compilation check to just our input files.
  // See: http://doc.crates.io/build-script.html#outputs-of-the-build-script
  println!("cargo:rerun-if-changed={}", path.to_str().unwrap());
  path
}

fn make_flags(env_script_path: &Path) -> Result<Vec<String>> {
  let mut contents = String::new();
  fs::File::open(env_script_path)?.read_to_string(
    &mut contents,
  )?;
  // It would be a shame if someone were to include a space in an actual quoted value.
  // If they did that, I guess we'd need to implement shell tokenization or something.
  return Ok(contents.trim().split(' ').map(str::to_owned).collect());
}
<commit_msg>Use split_whitespace for parsing of cflags. (#5038)<commit_after>extern crate cc;

/*

N.B. This build script is invoked by `cargo` by way of this configuration
in our Cargo.toml:

    [project]
    ...
    build = "src/cffi_build.rs"

Within, we use the `gcc` crate to compile the CFFI C sources (`native_engine.c`)
generated by `bootstrap.sh` into a (private) static lib (`libnative_engine_ffi.a`),
which then gets linked into the final `cargo build` product (the native engine binary).
This process mixes the Python module initialization function and other symbols into the
native engine binary, allowing us to address it both as an importable python module
(`from _native_engine import X`) as well as a C library (`ffi.dlopen(native_engine.so)`).

*/

use std::fs;
use std::io::{Read, Result};
use std::path::{Path, PathBuf};

fn main() {
  let mut config = cc::Build::new();

  // Don't implicitly set -Wall -Wextra because cffi generates code with warnings.
  // TODO: Consider removing this if/when https://github.com/alexcrichton/cc-rs/pull/248 lands.
  config.warnings(false);

  // N.B. The filename of this source code - at generation time - must line up 1:1 with the
  // python import name, as python keys the initialization function name off of the import name.
  let cffi_dir = Path::new("src/cffi");
  let c_path = mark_for_change_detection(cffi_dir.join("native_engine.c"));
  let env_script_path = mark_for_change_detection(cffi_dir.join("native_engine.cflags"));

  config.file(c_path.to_str().unwrap());
  for flag in make_flags(&env_script_path).unwrap() {
    config.flag(flag.as_str());
  }

  config.compile("libnative_engine_ffi.a");
}

fn mark_for_change_detection(path: PathBuf) -> PathBuf {
  // Restrict re-compilation check to just our input files.
  // See: http://doc.crates.io/build-script.html#outputs-of-the-build-script
  println!("cargo:rerun-if-changed={}", path.to_str().unwrap());
  path
}

fn make_flags(env_script_path: &Path) -> Result<Vec<String>> {
  let mut contents = String::new();
  fs::File::open(env_script_path)?.read_to_string(
    &mut contents,
  )?;
  // It would be a shame if someone were to include a space in an actual quoted value.
  // If they did that, I guess we'd need to implement shell tokenization or something.
  return Ok(
    contents
      .trim()
      .split_whitespace()
      .map(str::to_owned)
      .collect(),
  );
}
<|endoftext|>