<commit_before>// TODO maybe pull from https://github.com/rust-lang/rust-www/blob/master/_data/team.yml instead

use std::collections::BTreeMap;

use diesel::prelude::*;
use toml;
use serde::de;

use super::DB_POOL;
use domain::github::GitHubUser;
use error::*;

//==============================================================================
// Public API
//==============================================================================

lazy_static! {
    pub static ref SETUP: RfcbotConfig = read_rfcbot_cfg_validated();
}

#[derive(Debug, Deserialize)]
pub struct RfcbotConfig {
    fcp_behaviors: BTreeMap<String, FcpBehavior>,
    teams: BTreeMap<TeamLabel, Team>,
}

impl RfcbotConfig {
    /// Retrive an iterator over all the team labels.
    pub fn team_labels(&self) -> impl Iterator<Item = &TeamLabel> {
        self.teams.keys()
    }

    /// Retrive an iterator over all the (team label, team) pairs.
    pub fn teams(&self) -> impl Iterator<Item = (&TeamLabel, &Team)> {
        self.teams.iter()
    }

    /// Are we allowed to auto-close issues after F-FCP in this repo?
    pub fn should_ffcp_auto_close(&self, repo: &str) -> bool {
        self.fcp_behaviors.get(repo).map(|fcp| fcp.close).unwrap_or_default()
    }

    /// Are we allowed to auto-postpone issues after F-FCP in this repo?
    pub fn should_ffcp_auto_postpone(&self, repo: &str) -> bool {
        self.fcp_behaviors.get(repo).map(|fcp| fcp.postpone).unwrap_or_default()
    }
}

#[derive(Debug, Deserialize)]
pub struct FcpBehavior {
    #[serde(default)]
    close: bool,
    #[serde(default)]
    postpone: bool,
}

#[derive(Debug, Deserialize)]
pub struct Team {
    // FIXME: The two following first fields don't seem to be used anywhere...
    // If this is intended, document why.
    name: String,
    ping: String,
    members: Vec<String>,
}

impl Team {
    pub fn member_logins(&self) -> impl Iterator<Item = &str> {
        self.members.iter().map(|s| s.as_str())
    }
}

#[derive(Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct TeamLabel(pub String);

impl<'de> de::Deserialize<'de> for TeamLabel {
    fn deserialize<D: de::Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
        String::deserialize(de).map(TeamLabel)
    }

    fn deserialize_in_place<D: de::Deserializer<'de>>(de: D, place: &mut Self)
        -> Result<(), D::Error>
    {
        String::deserialize_in_place(de, &mut place.0)
    }
}

//==============================================================================
// Implementation details
//==============================================================================

/// Read the validated `rfcbot.toml` configuration file.
fn read_rfcbot_cfg_validated() -> RfcbotConfig {
    let cfg = read_rfcbot_cfg();

    cfg.teams.values().for_each(|team|
        team.validate()
            .expect("unable to verify team member from database.
if you're running this for tests, make sure you've pulled github users from prod")
    );

    cfg
}

/// Read the unprocessed `rfcbot.toml` configuration file.
fn read_rfcbot_cfg() -> RfcbotConfig {
    read_rfcbot_cfg_from(
        include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/rfcbot.toml")))
}

fn read_rfcbot_cfg_from(input: &str) -> RfcbotConfig {
    toml::from_str(input).expect("couldn't parse rfcbot.toml!")
}

impl Team {
    fn validate(&self) -> DashResult<()> {
        use domain::schema::githubuser::dsl::*;
        let conn = &*(DB_POOL.get()?);

        // bail if they don't exist, but we don't want to actually keep the id in ram
        for member_login in self.member_logins() {
            match githubuser
                    .filter(login.eq(member_login))
                    .first::<GitHubUser>(conn)
            {
                Ok(_) => (),
                Err(why) => {
                    error!("unable to find {} in database: {:?}", member_login, why);
                    return Err(why.into());
                }
            }
        }

        Ok(())
    }
}

//==============================================================================
// Tests
//==============================================================================

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn setup_parser_correct() {
let test = r#"
[fcp_behaviors]

[fcp_behaviors."rust-lang/alpha"]
close = true
postpone = true

[fcp_behaviors."foobar/beta"]
close = false

[fcp_behaviors."bazquux/gamma"]
postpone = false

[fcp_behaviors."wibble/epsilon"]

[teams]

[teams.avengers]
name = "The Avengers"
ping = "marvel/avengers"
members = [
  "hulk",
  "thor",
  "thevision",
  "blackwidow",
  "spiderman",
  "captainamerica",
]

[teams.justice-league]
name = "Justice League of America"
ping = "dc-comics/justice-league"
members = [
  "superman",
  "wonderwoman",
  "aquaman",
  "batman",
  "theflash"
]
"#;
        let cfg = read_rfcbot_cfg_from(test);

        // Labels are correct:
        assert_eq!(cfg.team_labels().map(|tl| tl.0.clone()).collect::<Vec<_>>(),
                   vec!["avengers", "justice-league"]);

        // Teams are correct:
        let map: BTreeMap<_, _> =
            cfg.teams().map(|(k, v)| (k.0.clone(), v.clone())).collect();

        let avengers = map.get("avengers").unwrap();
        assert_eq!(avengers.name, "The Avengers");
        assert_eq!(avengers.ping, "marvel/avengers");
        assert_eq!(avengers.member_logins().collect::<Vec<_>>(),
            vec!["hulk", "thor", "thevision", "blackwidow",
                 "spiderman", "captainamerica"]);

        let jsa = map.get("justice-league").unwrap();
        assert_eq!(jsa.name, "Justice League of America");
        assert_eq!(jsa.ping, "dc-comics/justice-league");
        assert_eq!(jsa.member_logins().collect::<Vec<_>>(),
            vec!["superman", "wonderwoman", "aquaman", "batman", "theflash"]);

        // Random non-existent team does not exist:
        assert!(map.get("random").is_none());

        // FFCP behavior correct:
        assert!(cfg.should_ffcp_auto_close("rust-lang/alpha"));
        assert!(cfg.should_ffcp_auto_postpone("rust-lang/alpha"));
        assert!(!cfg.should_ffcp_auto_close("foobar/beta"));
        assert!(!cfg.should_ffcp_auto_postpone("foobar/beta"));
        assert!(!cfg.should_ffcp_auto_close("bazquux/gamma"));
        assert!(!cfg.should_ffcp_auto_postpone("bazquux/gamma"));
        assert!(!cfg.should_ffcp_auto_close("wibble/epsilon"));
        assert!(!cfg.should_ffcp_auto_postpone("wibble/epsilon"));
        assert!(!cfg.should_ffcp_auto_close("random"));
        assert!(!cfg.should_ffcp_auto_postpone("random"));
    }

    #[test]
    fn cfg_file_wellformed() {
        // Just parse it and ensure that we get no panics for now!
        // This is a crap test; but, better than nothing.
        let _ = read_rfcbot_cfg();
    }

    #[test]
    fn team_members_exist() {
        for (label, _) in SETUP.teams.iter() {
            println!("found team {:?}", label);
        }
    }
}
<commit_msg>comment out Team.{name, ping} from Team; but keep in rfcbot.toml!<commit_after>// TODO maybe pull from https://github.com/rust-lang/rust-www/blob/master/_data/team.yml instead

use std::collections::BTreeMap;

use diesel::prelude::*;
use toml;
use serde::de;

use super::DB_POOL;
use domain::github::GitHubUser;
use error::*;

//==============================================================================
// Public API
//==============================================================================

lazy_static! {
    pub static ref SETUP: RfcbotConfig = read_rfcbot_cfg_validated();
}

#[derive(Debug, Deserialize)]
pub struct RfcbotConfig {
    fcp_behaviors: BTreeMap<String, FcpBehavior>,
    teams: BTreeMap<TeamLabel, Team>,
}

impl RfcbotConfig {
    /// Retrive an iterator over all the team labels.
    pub fn team_labels(&self) -> impl Iterator<Item = &TeamLabel> {
        self.teams.keys()
    }

    /// Retrive an iterator over all the (team label, team) pairs.
    pub fn teams(&self) -> impl Iterator<Item = (&TeamLabel, &Team)> {
        self.teams.iter()
    }

    /// Are we allowed to auto-close issues after F-FCP in this repo?
    pub fn should_ffcp_auto_close(&self, repo: &str) -> bool {
        self.fcp_behaviors.get(repo).map(|fcp| fcp.close).unwrap_or_default()
    }

    /// Are we allowed to auto-postpone issues after F-FCP in this repo?
    pub fn should_ffcp_auto_postpone(&self, repo: &str) -> bool {
        self.fcp_behaviors.get(repo).map(|fcp| fcp.postpone).unwrap_or_default()
    }
}

#[derive(Debug, Deserialize)]
pub struct FcpBehavior {
    #[serde(default)]
    close: bool,
    #[serde(default)]
    postpone: bool,
}

#[derive(Debug, Deserialize)]
pub struct Team {
    // FIXME(2018-05-16):
    // The two following first fields are not used anymore.
    // But they could still be useful. Consider what usage they could have.

    //name: String,
    //ping: String,

    members: Vec<String>,
}

impl Team {
    pub fn member_logins(&self) -> impl Iterator<Item = &str> {
        self.members.iter().map(|s| s.as_str())
    }
}

#[derive(Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct TeamLabel(pub String);

impl<'de> de::Deserialize<'de> for TeamLabel {
    fn deserialize<D: de::Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
        String::deserialize(de).map(TeamLabel)
    }

    fn deserialize_in_place<D: de::Deserializer<'de>>(de: D, place: &mut Self)
        -> Result<(), D::Error>
    {
        String::deserialize_in_place(de, &mut place.0)
    }
}

//==============================================================================
// Implementation details
//==============================================================================

/// Read the validated `rfcbot.toml` configuration file.
fn read_rfcbot_cfg_validated() -> RfcbotConfig {
    let cfg = read_rfcbot_cfg();

    cfg.teams.values().for_each(|team|
        team.validate()
            .expect("unable to verify team member from database.
if you're running this for tests, make sure you've pulled github users from prod")
    );

    cfg
}

/// Read the unprocessed `rfcbot.toml` configuration file.
fn read_rfcbot_cfg() -> RfcbotConfig {
    read_rfcbot_cfg_from(
        include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/rfcbot.toml")))
}

fn read_rfcbot_cfg_from(input: &str) -> RfcbotConfig {
    toml::from_str(input).expect("couldn't parse rfcbot.toml!")
}

impl Team {
    fn validate(&self) -> DashResult<()> {
        use domain::schema::githubuser::dsl::*;
        let conn = &*(DB_POOL.get()?);

        // bail if they don't exist, but we don't want to actually keep the id in ram
        for member_login in self.member_logins() {
            match githubuser
                    .filter(login.eq(member_login))
                    .first::<GitHubUser>(conn)
            {
                Ok(_) => (),
                Err(why) => {
                    error!("unable to find {} in database: {:?}", member_login, why);
                    return Err(why.into());
                }
            }
        }

        Ok(())
    }
}

//==============================================================================
// Tests
//==============================================================================

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn setup_parser_correct() {
let test = r#"
[fcp_behaviors]

[fcp_behaviors."rust-lang/alpha"]
close = true
postpone = true

[fcp_behaviors."foobar/beta"]
close = false

[fcp_behaviors."bazquux/gamma"]
postpone = false

[fcp_behaviors."wibble/epsilon"]

[teams]

[teams.avengers]
name = "The Avengers"
ping = "marvel/avengers"
members = [
  "hulk",
  "thor",
  "thevision",
  "blackwidow",
  "spiderman",
  "captainamerica",
]

[teams.justice-league]
name = "Justice League of America"
ping = "dc-comics/justice-league"
members = [
  "superman",
  "wonderwoman",
  "aquaman",
  "batman",
  "theflash"
]
"#;
        let cfg = read_rfcbot_cfg_from(test);

        // Labels are correct:
        assert_eq!(cfg.team_labels().map(|tl| tl.0.clone()).collect::<Vec<_>>(),
                   vec!["avengers", "justice-league"]);

        // Teams are correct:
        let map: BTreeMap<_, _> =
            cfg.teams().map(|(k, v)| (k.0.clone(), v.clone())).collect();

        let avengers = map.get("avengers").unwrap();
        //assert_eq!(avengers.name, "The Avengers");
        //assert_eq!(avengers.ping, "marvel/avengers");
        assert_eq!(avengers.member_logins().collect::<Vec<_>>(),
            vec!["hulk", "thor", "thevision", "blackwidow",
                 "spiderman", "captainamerica"]);

        let jsa = map.get("justice-league").unwrap();
        //assert_eq!(jsa.name, "Justice League of America");
        //assert_eq!(jsa.ping, "dc-comics/justice-league");
        assert_eq!(jsa.member_logins().collect::<Vec<_>>(),
            vec!["superman", "wonderwoman", "aquaman", "batman", "theflash"]);

        // Random non-existent team does not exist:
        assert!(map.get("random").is_none());

        // FFCP behavior correct:
        assert!(cfg.should_ffcp_auto_close("rust-lang/alpha"));
        assert!(cfg.should_ffcp_auto_postpone("rust-lang/alpha"));
        assert!(!cfg.should_ffcp_auto_close("foobar/beta"));
        assert!(!cfg.should_ffcp_auto_postpone("foobar/beta"));
        assert!(!cfg.should_ffcp_auto_close("bazquux/gamma"));
        assert!(!cfg.should_ffcp_auto_postpone("bazquux/gamma"));
        assert!(!cfg.should_ffcp_auto_close("wibble/epsilon"));
        assert!(!cfg.should_ffcp_auto_postpone("wibble/epsilon"));
        assert!(!cfg.should_ffcp_auto_close("random"));
        assert!(!cfg.should_ffcp_auto_postpone("random"));
    }

    #[test]
    fn cfg_file_wellformed() {
        // Just parse it and ensure that we get no panics for now!
        // This is a crap test; but, better than nothing.
        let _ = read_rfcbot_cfg();
    }

    #[test]
    fn team_members_exist() {
        for (label, _) in SETUP.teams.iter() {
            println!("found team {:?}", label);
        }
    }
}
<|endoftext|>


<commit_before>#![cfg(test)]

use futures_util::TryStreamExt;

use crate::{from_row, prelude::*, test_misc::get_opts, Conn, Row, TxOpts};

#[tokio::test]
async fn should_stream_text_result_sets() -> crate::Result<()> {
    const QUERY: &str = r"
        SELECT 1;
        SELECT 'foo' UNION ALL SELECT 'bar';
        SELECT 3, 4 UNION ALL SELECT 5, 6;
        SELECT FOO();
        SELECT 3.14;
    ";
    let mut conn = Conn::new(get_opts()).await?;
    let mut result = QUERY.run(&mut conn).await?;

    let stream = result.stream::<u8>().await?.unwrap();
    assert_eq!(vec![1], stream.try_collect::<Vec<_>>().await?);

    let _ = result.stream::<Row>().await?.unwrap();

    let stream = result.stream::<(u8, u8)>().await?.unwrap();
    assert_eq!(vec![(3, 4), (5, 6)], stream.try_collect::<Vec<_>>().await?);

    let _ = result.stream::<Row>().await.unwrap_err();
    assert!(result.stream::<Row>().await?.is_none());

    let mut conn = Conn::new(get_opts()).await?;
    let mut result = QUERY.run(&mut conn).await?;

    assert_eq!(vec![1], result.collect::<u8>().await?);

    assert_eq!("foo", from_row::<String>(result.next().await?.unwrap()));
    let _ = result.stream::<Row>().await?.unwrap();

    assert_eq!((3_u8, 4_u8), from_row(result.next().await?.unwrap()));
    assert_eq!(vec![(5, 6)], result.collect::<(u8, u8)>().await?);

    result.collect::<Row>().await.unwrap_err();
    assert!(result.stream::<Row>().await?.is_none());

    conn.disconnect().await?;
    Ok(())
}

#[tokio::test]
async fn dropped_query_result_should_emit_errors_on_cleanup() -> super::Result<()> {
    use crate::{Error::Server, ServerError};
    let mut conn = Conn::new(get_opts()).await?;
    conn.query_iter("SELECT '1'; BLABLA;").await?;
    assert!(matches!(
        conn.query_drop("DO 42;").await.unwrap_err(),
        Server(ServerError { code: 1064, .. })
    ));
    conn.disconnect().await?;
    Ok(())
}

#[tokio::test]
async fn should_try_collect() -> super::Result<()> {
    let mut conn = Conn::new(get_opts()).await?;
    let mut result = conn
        .query_iter(
            r"SELECT 'hello', 123
                UNION ALL
                SELECT 'world', 'bar'
                UNION ALL
                SELECT 'hello', 123
            ",
        )
        .await?;
    let mut rows = result.try_collect::<(String, u8)>().await?;
    assert!(rows.pop().unwrap().is_ok());
    assert!(rows.pop().unwrap().is_err());
    assert!(rows.pop().unwrap().is_ok());
    result.drop_result().await?;
    conn.disconnect().await?;
    Ok(())
}

#[tokio::test]
async fn should_try_collect_and_drop() -> super::Result<()> {
    let mut conn = Conn::new(get_opts()).await?;
    let mut rows = conn
        .query_iter(
            r"SELECT 'hello', 123
                UNION ALL
                SELECT 'world', 'bar'
                UNION ALL
                SELECT 'hello', 123;
                SELECT 'foo', 255;
            ",
        )
        .await?
        .try_collect_and_drop::<(String, u8)>()
        .await?;
    assert!(rows.pop().unwrap().is_ok());
    assert!(rows.pop().unwrap().is_err());
    assert!(rows.pop().unwrap().is_ok());
    conn.disconnect().await?;
    Ok(())
}

#[tokio::test]
async fn should_handle_mutliresult_set() -> super::Result<()> {
    let mut conn = Conn::new(get_opts()).await?;
    let mut result = conn
        .query_iter(
            r"SELECT 'hello', 123
                UNION ALL
                SELECT 'world', 231;
                SELECT 'foo', 255;
            ",
        )
        .await?;
    let rows_1 = result.collect::<(String, u8)>().await?;
    let rows_2 = result.collect_and_drop().await?;
    conn.disconnect().await?;

    assert_eq!((String::from("hello"), 123), rows_1[0]);
    assert_eq!((String::from("world"), 231), rows_1[1]);
    assert_eq!((String::from("foo"), 255), rows_2[0]);
    Ok(())
}

#[tokio::test]
async fn should_map_resultset() -> super::Result<()> {
    let mut conn = Conn::new(get_opts()).await?;
    let mut result = conn
        .query_iter(
            r"
                SELECT 'hello', 123
                UNION ALL
                SELECT 'world', 231;
                SELECT 'foo', 255;
            ",
        )
        .await?;

    let rows_1 = result.map(|row| from_row::<(String, u8)>(row)).await?;
    let rows_2 = result.map_and_drop(from_row).await?;
    conn.disconnect().await?;

    assert_eq!((String::from("hello"), 123), rows_1[0]);
    assert_eq!((String::from("world"), 231), rows_1[1]);
    assert_eq!((String::from("foo"), 255), rows_2[0]);
    Ok(())
}

#[tokio::test]
async fn should_reduce_resultset() -> super::Result<()> {
    let mut conn = Conn::new(get_opts()).await?;
    let mut result = conn
        .query_iter(
            r"SELECT 5
                UNION ALL
                SELECT 6;
                SELECT 7;",
        )
        .await?;
    let reduced = result
        .reduce(0, |mut acc, row| {
            acc += from_row::<i32>(row);
            acc
        })
        .await?;
    let rows_2 = result.collect_and_drop::<i32>().await?;
    conn.disconnect().await?;
    assert_eq!(11, reduced);
    assert_eq!(7, rows_2[0]);
    Ok(())
}

#[tokio::test]
async fn should_handle_multi_result_sets_where_some_results_have_no_output() -> super::Result<()> {
    const QUERY: &str = r"SELECT 1;
        UPDATE time_zone SET Time_zone_id = 1 WHERE Time_zone_id = 1;
        SELECT 2;
        SELECT 3;
        UPDATE time_zone SET Time_zone_id = 1 WHERE Time_zone_id = 1;
        UPDATE time_zone SET Time_zone_id = 1 WHERE Time_zone_id = 1;
        SELECT 4;";

    let mut c = Conn::new(get_opts()).await?;
    c.query_drop("CREATE TEMPORARY TABLE time_zone (Time_zone_id INT)")
        .await
        .unwrap();
    let mut t = c.start_transaction(TxOpts::new()).await?;
    t.query_drop(QUERY).await?;
    let r = t.query_iter(QUERY).await?;
    let out = r.collect_and_drop::<u8>().await?;
    assert_eq!(vec![1], out);
    let r = t.query_iter(QUERY).await?;
    r.for_each_and_drop(|x| assert_eq!(from_row::<u8>(x), 1))
        .await?;
    let r = t.query_iter(QUERY).await?;
    let out = r.map_and_drop(|row| from_row::<u8>(row)).await?;
    assert_eq!(vec![1], out);
    let r = t.query_iter(QUERY).await?;
    let out = r
        .reduce_and_drop(0u8, |acc, x| acc + from_row::<u8>(x))
        .await?;
    assert_eq!(1, out);
    t.query_drop(QUERY).await?;
    t.commit().await?;
    let result = c.exec_first("SELECT 1", ()).await?;
    c.disconnect().await?;
    assert_eq!(result, Some(1_u8));
    Ok(())
}

#[tokio::test]
async fn should_iterate_over_resultset() -> super::Result<()> {
    use std::sync::{
        atomic::{AtomicUsize, Ordering},
        Arc,
    };

    let acc = Arc::new(AtomicUsize::new(0));

    let mut conn = Conn::new(get_opts()).await?;
    let mut result = conn
        .query_iter(
            r"SELECT 2
                UNION ALL
                SELECT 3;
                SELECT 5;",
        )
        .await?;
    result
        .for_each({
            let acc = acc.clone();
            move |row| {
                acc.fetch_add(from_row::<usize>(row), Ordering::SeqCst);
            }
        })
        .await?;
    result
        .for_each_and_drop({
            let acc = acc.clone();
            move |row| {
                acc.fetch_add(from_row::<usize>(row), Ordering::SeqCst);
            }
        })
        .await?;
    conn.disconnect().await?;
    assert_eq!(acc.load(Ordering::SeqCst), 10);
    Ok(())
}
<commit_msg>fixup! Implement various methods related to QueryResult streaming<commit_after>#![cfg(test)]

use futures_util::TryStreamExt;

use crate::{from_row, prelude::*, test_misc::get_opts, Conn, Row, TxOpts};

#[tokio::test]
async fn should_stream_text_result_sets() -> crate::Result<()> {
    const QUERY: &str = r"
        SELECT 1;
        SELECT 'foo' UNION ALL SELECT 'bar';
        SELECT 3, 4 UNION ALL SELECT 5, 6;
        SELECT FOO();
        SELECT 3.14;
    ";
    let mut conn = Conn::new(get_opts()).await?;
    let mut result = QUERY.run(&mut conn).await?;

    let stream = result.stream::<u8>().await?.unwrap();
    assert_eq!(vec![1], stream.try_collect::<Vec<_>>().await?);

    let _ = result.stream::<Row>().await?.unwrap();

    let stream = result.stream::<(u8, u8)>().await?.unwrap();
    assert_eq!(vec![(3, 4), (5, 6)], stream.try_collect::<Vec<_>>().await?);

    let _ = result.stream::<Row>().await.unwrap_err();
    assert!(result.stream::<Row>().await?.is_none());

    let mut conn = Conn::new(get_opts()).await?;
    let mut result = QUERY.run(&mut conn).await?;

    assert_eq!(vec![1], result.collect::<u8>().await?);

    assert_eq!("foo", from_row::<String>(result.next().await?.unwrap()));
    let _ = result.stream::<Row>().await?.unwrap();

    assert_eq!((3_u8, 4_u8), from_row(result.next().await?.unwrap()));
    assert_eq!(vec![(5, 6)], result.collect::<(u8, u8)>().await?);

    result.collect::<Row>().await.unwrap_err();
    assert!(result.stream::<Row>().await?.is_none());

    conn.disconnect().await?;
    Ok(())
}

#[tokio::test]
async fn should_stream_binary_result_sets() -> crate::Result<()> {
    let mut conn = Conn::new(get_opts()).await?;
    let mut result = "SELECT ?".with((1_u8,)).run(&mut conn).await?;
    assert_eq!(vec![1], result.collect::<u8>().await?);
    assert!(result.stream::<Row>().await?.is_none());

    let mut result = "SELECT ?".with((1_u8,)).run(&mut conn).await?;
    assert_eq!(
        vec![1],
        result
            .stream::<u8>()
            .await?
            .unwrap()
            .try_collect::<Vec<_>>()
            .await?,
    );
    assert_eq!(result.collect::<u8>().await?, Vec::<u8>::new());

    conn.disconnect().await?;
    Ok(())
}

#[tokio::test]
async fn dropped_query_result_should_emit_errors_on_cleanup() -> super::Result<()> {
    use crate::{Error::Server, ServerError};
    let mut conn = Conn::new(get_opts()).await?;
    conn.query_iter("SELECT '1'; BLABLA;").await?;
    assert!(matches!(
        conn.query_drop("DO 42;").await.unwrap_err(),
        Server(ServerError { code: 1064, .. })
    ));
    conn.disconnect().await?;
    Ok(())
}

#[tokio::test]
async fn should_try_collect() -> super::Result<()> {
    let mut conn = Conn::new(get_opts()).await?;
    let mut result = conn
        .query_iter(
            r"SELECT 'hello', 123
                UNION ALL
                SELECT 'world', 'bar'
                UNION ALL
                SELECT 'hello', 123
            ",
        )
        .await?;
    let mut rows = result.try_collect::<(String, u8)>().await?;
    assert!(rows.pop().unwrap().is_ok());
    assert!(rows.pop().unwrap().is_err());
    assert!(rows.pop().unwrap().is_ok());
    result.drop_result().await?;
    conn.disconnect().await?;
    Ok(())
}

#[tokio::test]
async fn should_try_collect_and_drop() -> super::Result<()> {
    let mut conn = Conn::new(get_opts()).await?;
    let mut rows = conn
        .query_iter(
            r"SELECT 'hello', 123
                UNION ALL
                SELECT 'world', 'bar'
                UNION ALL
                SELECT 'hello', 123;
                SELECT 'foo', 255;
            ",
        )
        .await?
        .try_collect_and_drop::<(String, u8)>()
        .await?;
    assert!(rows.pop().unwrap().is_ok());
    assert!(rows.pop().unwrap().is_err());
    assert!(rows.pop().unwrap().is_ok());
    conn.disconnect().await?;
    Ok(())
}

#[tokio::test]
async fn should_handle_mutliresult_set() -> super::Result<()> {
    let mut conn = Conn::new(get_opts()).await?;
    let mut result = conn
        .query_iter(
            r"SELECT 'hello', 123
                UNION ALL
                SELECT 'world', 231;
                SELECT 'foo', 255;
            ",
        )
        .await?;
    let rows_1 = result.collect::<(String, u8)>().await?;
    let rows_2 = result.collect_and_drop().await?;
    conn.disconnect().await?;

    assert_eq!((String::from("hello"), 123), rows_1[0]);
    assert_eq!((String::from("world"), 231), rows_1[1]);
    assert_eq!((String::from("foo"), 255), rows_2[0]);
    Ok(())
}

#[tokio::test]
async fn should_map_resultset() -> super::Result<()> {
    let mut conn = Conn::new(get_opts()).await?;
    let mut result = conn
        .query_iter(
            r"
                SELECT 'hello', 123
                UNION ALL
                SELECT 'world', 231;
                SELECT 'foo', 255;
            ",
        )
        .await?;

    let rows_1 = result.map(|row| from_row::<(String, u8)>(row)).await?;
    let rows_2 = result.map_and_drop(from_row).await?;
    conn.disconnect().await?;

    assert_eq!((String::from("hello"), 123), rows_1[0]);
    assert_eq!((String::from("world"), 231), rows_1[1]);
    assert_eq!((String::from("foo"), 255), rows_2[0]);
    Ok(())
}

#[tokio::test]
async fn should_reduce_resultset() -> super::Result<()> {
    let mut conn = Conn::new(get_opts()).await?;
    let mut result = conn
        .query_iter(
            r"SELECT 5
                UNION ALL
                SELECT 6;
                SELECT 7;",
        )
        .await?;
    let reduced = result
        .reduce(0, |mut acc, row| {
            acc += from_row::<i32>(row);
            acc
        })
        .await?;
    let rows_2 = result.collect_and_drop::<i32>().await?;
    conn.disconnect().await?;
    assert_eq!(11, reduced);
    assert_eq!(7, rows_2[0]);
    Ok(())
}

#[tokio::test]
async fn should_handle_multi_result_sets_where_some_results_have_no_output() -> super::Result<()> {
    const QUERY: &str = r"SELECT 1;
        UPDATE time_zone SET Time_zone_id = 1 WHERE Time_zone_id = 1;
        SELECT 2;
        SELECT 3;
        UPDATE time_zone SET Time_zone_id = 1 WHERE Time_zone_id = 1;
        UPDATE time_zone SET Time_zone_id = 1 WHERE Time_zone_id = 1;
        SELECT 4;";

    let mut c = Conn::new(get_opts()).await?;
    c.query_drop("CREATE TEMPORARY TABLE time_zone (Time_zone_id INT)")
        .await
        .unwrap();
    let mut t = c.start_transaction(TxOpts::new()).await?;
    t.query_drop(QUERY).await?;
    let r = t.query_iter(QUERY).await?;
    let out = r.collect_and_drop::<u8>().await?;
    assert_eq!(vec![1], out);
    let r = t.query_iter(QUERY).await?;
    r.for_each_and_drop(|x| assert_eq!(from_row::<u8>(x), 1))
        .await?;
    let r = t.query_iter(QUERY).await?;
    let out = r.map_and_drop(|row| from_row::<u8>(row)).await?;
    assert_eq!(vec![1], out);
    let r = t.query_iter(QUERY).await?;
    let out = r
        .reduce_and_drop(0u8, |acc, x| acc + from_row::<u8>(x))
        .await?;
    assert_eq!(1, out);
    t.query_drop(QUERY).await?;
    t.commit().await?;
    let result = c.exec_first("SELECT 1", ()).await?;
    c.disconnect().await?;
    assert_eq!(result, Some(1_u8));
    Ok(())
}

#[tokio::test]
async fn should_iterate_over_resultset() -> super::Result<()> {
    use std::sync::{
        atomic::{AtomicUsize, Ordering},
        Arc,
    };

    let acc = Arc::new(AtomicUsize::new(0));

    let mut conn = Conn::new(get_opts()).await?;
    let mut result = conn
        .query_iter(
            r"SELECT 2
                UNION ALL
                SELECT 3;
                SELECT 5;",
        )
        .await?;
    result
        .for_each({
            let acc = acc.clone();
            move |row| {
                acc.fetch_add(from_row::<usize>(row), Ordering::SeqCst);
            }
        })
        .await?;
    result
        .for_each_and_drop({
            let acc = acc.clone();
            move |row| {
                acc.fetch_add(from_row::<usize>(row), Ordering::SeqCst);
            }
        })
        .await?;
    conn.disconnect().await?;
    assert_eq!(acc.load(Ordering::SeqCst), 10);
    Ok(())
}
<|endoftext|>


<commit_before>use mio_uds::UnixStream;
use mio::Ready;
use libc::{self,pid_t};
use std::io::{Seek,SeekFrom};
use std::fs::File;
use std::process::Command;
use std::os::unix::process::CommandExt;
use std::os::unix::io::{AsRawFd,FromRawFd,IntoRawFd};
use tempfile::tempfile;
use serde_json;
use nix;
use nix::unistd::*;

#[cfg(target_os = "macos")]
use std::ffi::CString;
#[cfg(target_os = "macos")]
use libc::{c_char,uint32_t,int32_t};
#[cfg(target_os = "macos")]
use std::iter::repeat;
#[cfg(target_os = "macos")]
use std::ptr::null_mut;

#[cfg(target_os = "freebsd")]
use libc::sysctl;
#[cfg(target_os = "freebsd")]
use std::ffi::c_void;
#[cfg(target_os = "freebsd")]
use std::iter::repeat;
#[cfg(target_os = "freebsd")]
use std::mem::size_of;

use sozu_command::config::Config;
use sozu_command::channel::Channel;
use sozu_command::state::ConfigState;
use sozu_command::scm_socket::{Listeners,ScmSocket};
use sozu_command::proxy::{ProxyRequest,ProxyResponse};
use sozu::server::Server;
use sozu::metrics;

use util;
use logging;
use command::Worker;

pub fn start_workers(executable_path: String, config: &Config) -> nix::Result<Vec<Worker>> {
  let state = ConfigState::new();
  let mut workers = Vec::new();
  for index in 0..config.worker_count {
    let listeners = Some(Listeners {
      http: Vec::new(),
      tls:  Vec::new(),
      tcp:  Vec::new(),
    });
    match start_worker_process(&index.to_string(), config, executable_path.clone(), &state, listeners) {
      Ok((pid, command, scm)) => {
        let w =  Worker::new(index as u32, pid, command, scm, config);
        workers.push(w);
      },
      Err(e) => return Err(e)
    };
  }
  Ok(workers)
}

pub fn start_worker(id: u32, config: &Config, executable_path: String, state: &ConfigState, listeners: Option<Listeners>) -> nix::Result<Worker> {
  match start_worker_process(&id.to_string(), config, executable_path, state, listeners) {
    Ok((pid, command, scm)) => {
      let w = Worker::new(id, pid, command, scm, config);
      Ok(w)
    },
    Err(e) => Err(e)
  }
}

pub fn begin_worker_process(fd: i32, scm: i32, configuration_state_fd: i32, id: i32,
  command_buffer_size: usize, max_command_buffer_size: usize) {
  let mut command: Channel<ProxyResponse,Config> = Channel::new(
    unsafe { UnixStream::from_raw_fd(fd) },
    command_buffer_size,
    max_command_buffer_size
  );

  command.set_nonblocking(false);

  let configuration_state_file = unsafe { File::from_raw_fd(configuration_state_fd) };
  let config_state: ConfigState = serde_json::from_reader(configuration_state_file)
    .expect("could not parse configuration state data");

  let worker_config = command.read_message().expect("worker could not read configuration from socket");
  //println!("got message: {:?}", worker_config);

  let worker_id = format!("{}-{:02}", "WRK", id);
  logging::setup(worker_id.clone(), &worker_config.log_level,
    &worker_config.log_target, worker_config.log_access_target.as_ref().map(|s| s.as_str()));
  info!("worker {} starting...", id);

  command.set_nonblocking(true);
  let mut command: Channel<ProxyResponse,ProxyRequest> = command.into();
  command.readiness.insert(Ready::readable());

  if let Some(ref metrics) = worker_config.metrics.as_ref() {
    metrics::setup(&metrics.address, worker_id, metrics.tagged_metrics, metrics.prefix.clone());
  }

  let mut server = Server::new_from_config(command, ScmSocket::new(scm), worker_config, config_state);

  info!("starting event loop");
  server.run();
  info!("ending event loop");
}

pub fn start_worker_process(id: &str, config: &Config, executable_path: String, state: &ConfigState, listeners: Option<Listeners>) -> nix::Result<(pid_t, Channel<ProxyRequest,ProxyResponse>, ScmSocket)> {
  trace!("parent({})", unsafe { libc::getpid() });

  let mut state_file = tempfile().expect("could not create temporary file for configuration state");
  util::disable_close_on_exec(state_file.as_raw_fd());

  serde_json::to_writer(&mut state_file, state).expect("could not write upgrade data to temporary file");
  state_file.seek(SeekFrom::Start(0)).expect("could not seek to beginning of file");

  let (server, client) = UnixStream::pair().unwrap();
  let (scm_server_fd, scm_client) = UnixStream::pair().unwrap();

  let scm_server = ScmSocket::new(scm_server_fd.into_raw_fd());

  util::disable_close_on_exec(client.as_raw_fd());
  util::disable_close_on_exec(scm_client.as_raw_fd());

  let mut command: Channel<Config,ProxyResponse> = Channel::new(
    server,
    config.command_buffer_size,
    config.max_command_buffer_size
  );
  command.set_nonblocking(false);

  info!("{} launching worker", id);
  debug!("executable path is {}", executable_path);
  match fork() {
    Ok(ForkResult::Parent{ child }) => {
      info!("{} worker launched: {}", id, child);
      command.write_message(config);
      command.set_nonblocking(true);

      if let Some(l) = listeners {
        info!("sending listeners to new worker: {:?}", l);
        let res = scm_server.send_listeners(&l);
        info!("sent listeners from master: {:?}", res);
        l.close();
      };
      util::disable_close_on_exec(scm_server.fd);

      let command: Channel<ProxyRequest,ProxyResponse> = command.into();
      Ok((child.into(), command, scm_server))
    },
    Ok(ForkResult::Child) => {
      trace!("child({}):\twill spawn a child", unsafe { libc::getpid() });
      Command::new(executable_path)
        .arg("worker")
        .arg("--id")
        .arg(id)
        .arg("--fd")
        .arg(client.as_raw_fd().to_string())
        .arg("--scm")
        .arg(scm_client.as_raw_fd().to_string())
        .arg("--configuration-state-fd")
        .arg(state_file.as_raw_fd().to_string())
        .arg("--command-buffer-size")
        .arg(config.command_buffer_size.to_string())
        .arg("--max-command-buffer-size")
        .arg(config.max_command_buffer_size.to_string())
        .exec();

      unreachable!();
    },
    Err(e) => {
      error!("Error during fork(): {}", e);
      Err(e)
    }
  }
}

#[cfg(target_os = "linux")]
pub unsafe fn get_executable_path() -> String {
  use std::fs;

  let path         = fs::read_link("/proc/self/exe").expect("/proc/self/exe doesn't exist");
  let mut path_str = path.into_os_string().into_string().expect("Failed to convert PathBuf to String");

  if path_str.ends_with(" (deleted)") {
    // The kernel appends " (deleted)" to the symlink when the original executable has been replaced
    let len = path_str.len();
    path_str.truncate(len - 10)
  }

  path_str
}

#[cfg(target_os = "macos")]
extern {
  pub fn _NSGetExecutablePath(buf: *mut c_char, size: *mut uint32_t) -> int32_t;
}

#[cfg(target_os = "macos")]
pub unsafe fn get_executable_path() -> String {
  let capacity = 2000;
  let mut temp:Vec<u8> = Vec::with_capacity(capacity);
  temp.extend(repeat(0).take(capacity));
  let pathbuf = CString::from_vec_unchecked(temp);
  let ptr = pathbuf.into_raw();

  let mut size:uint32_t = capacity as u32;
  if _NSGetExecutablePath(ptr, &mut size) == 0 {

    let mut temp2:Vec<u8> = Vec::with_capacity(capacity);
    temp2.extend(repeat(0).take(capacity));
    let pathbuf2 = CString::from_vec_unchecked(temp2);
    let ptr2 = pathbuf2.into_raw();

    if libc::realpath(ptr, ptr2) != null_mut() {
      let path = CString::from_raw(ptr2);
      path.to_str().expect("failed to convert CString to String").to_string()

    } else {
      panic!();
    }
  } else {
    panic!("buffer too small");
  }
}

#[cfg(target_os = "freebsd")]
pub unsafe fn get_executable_path() -> String {
    let mut capacity = 2000;
    let mut path: Vec<u8> = Vec::with_capacity(capacity);
    path.extend(repeat(0).take(capacity));

    // [CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1];
    let mib: Vec<i32> = vec![1, 14, 12, -1];
    let len = mib.len() * size_of::<i32>();
    let element_size = size_of::<i32>();

    let res = sysctl(
        mib.as_ptr(),
        (len / element_size) as u32,
        path.as_mut_ptr() as *mut c_void,
        &mut capacity,
        std::ptr::null() as *const c_void,
        0,
    );
    if res != 0 {
        panic!("Could not retrieve the path of the executable");
    }

    String::from_raw_parts(path.as_mut_ptr(), capacity - 1, path.len())
}
<commit_msg>uses the libC constants<commit_after>use mio_uds::UnixStream;
use mio::Ready;
use libc::{self,pid_t};
use std::io::{Seek,SeekFrom};
use std::fs::File;
use std::process::Command;
use std::os::unix::process::CommandExt;
use std::os::unix::io::{AsRawFd,FromRawFd,IntoRawFd};
use tempfile::tempfile;
use serde_json;
use nix;
use nix::unistd::*;

#[cfg(target_os = "macos")]
use std::ffi::CString;
#[cfg(target_os = "macos")]
use libc::{c_char,uint32_t,int32_t};
#[cfg(target_os = "macos")]
use std::iter::repeat;
#[cfg(target_os = "macos")]
use std::ptr::null_mut;

#[cfg(target_os = "freebsd")]
use libc::{CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, sysctl};
#[cfg(target_os = "freebsd")]
use std::ffi::c_void;
#[cfg(target_os = "freebsd")]
use std::iter::repeat;
#[cfg(target_os = "freebsd")]
use std::mem::size_of;

use sozu_command::config::Config;
use sozu_command::channel::Channel;
use sozu_command::state::ConfigState;
use sozu_command::scm_socket::{Listeners,ScmSocket};
use sozu_command::proxy::{ProxyRequest,ProxyResponse};
use sozu::server::Server;
use sozu::metrics;

use util;
use logging;
use command::Worker;

pub fn start_workers(executable_path: String, config: &Config) -> nix::Result<Vec<Worker>> {
  let state = ConfigState::new();
  let mut workers = Vec::new();
  for index in 0..config.worker_count {
    let listeners = Some(Listeners {
      http: Vec::new(),
      tls:  Vec::new(),
      tcp:  Vec::new(),
    });
    match start_worker_process(&index.to_string(), config, executable_path.clone(), &state, listeners) {
      Ok((pid, command, scm)) => {
        let w =  Worker::new(index as u32, pid, command, scm, config);
        workers.push(w);
      },
      Err(e) => return Err(e)
    };
  }
  Ok(workers)
}

pub fn start_worker(id: u32, config: &Config, executable_path: String, state: &ConfigState, listeners: Option<Listeners>) -> nix::Result<Worker> {
  match start_worker_process(&id.to_string(), config, executable_path, state, listeners) {
    Ok((pid, command, scm)) => {
      let w = Worker::new(id, pid, command, scm, config);
      Ok(w)
    },
    Err(e) => Err(e)
  }
}

pub fn begin_worker_process(fd: i32, scm: i32, configuration_state_fd: i32, id: i32,
  command_buffer_size: usize, max_command_buffer_size: usize) {
  let mut command: Channel<ProxyResponse,Config> = Channel::new(
    unsafe { UnixStream::from_raw_fd(fd) },
    command_buffer_size,
    max_command_buffer_size
  );

  command.set_nonblocking(false);

  let configuration_state_file = unsafe { File::from_raw_fd(configuration_state_fd) };
  let config_state: ConfigState = serde_json::from_reader(configuration_state_file)
    .expect("could not parse configuration state data");

  let worker_config = command.read_message().expect("worker could not read configuration from socket");
  //println!("got message: {:?}", worker_config);

  let worker_id = format!("{}-{:02}", "WRK", id);
  logging::setup(worker_id.clone(), &worker_config.log_level,
    &worker_config.log_target, worker_config.log_access_target.as_ref().map(|s| s.as_str()));
  info!("worker {} starting...", id);

  command.set_nonblocking(true);
  let mut command: Channel<ProxyResponse,ProxyRequest> = command.into();
  command.readiness.insert(Ready::readable());

  if let Some(ref metrics) = worker_config.metrics.as_ref() {
    metrics::setup(&metrics.address, worker_id, metrics.tagged_metrics, metrics.prefix.clone());
  }

  let mut server = Server::new_from_config(command, ScmSocket::new(scm), worker_config, config_state);

  info!("starting event loop");
  server.run();
  info!("ending event loop");
}

pub fn start_worker_process(id: &str, config: &Config, executable_path: String, state: &ConfigState, listeners: Option<Listeners>) -> nix::Result<(pid_t, Channel<ProxyRequest,ProxyResponse>, ScmSocket)> {
  trace!("parent({})", unsafe { libc::getpid() });

  let mut state_file = tempfile().expect("could not create temporary file for configuration state");
  util::disable_close_on_exec(state_file.as_raw_fd());

  serde_json::to_writer(&mut state_file, state).expect("could not write upgrade data to temporary file");
  state_file.seek(SeekFrom::Start(0)).expect("could not seek to beginning of file");

  let (server, client) = UnixStream::pair().unwrap();
  let (scm_server_fd, scm_client) = UnixStream::pair().unwrap();

  let scm_server = ScmSocket::new(scm_server_fd.into_raw_fd());

  util::disable_close_on_exec(client.as_raw_fd());
  util::disable_close_on_exec(scm_client.as_raw_fd());

  let mut command: Channel<Config,ProxyResponse> = Channel::new(
    server,
    config.command_buffer_size,
    config.max_command_buffer_size
  );
  command.set_nonblocking(false);

  info!("{} launching worker", id);
  debug!("executable path is {}", executable_path);
  match fork() {
    Ok(ForkResult::Parent{ child }) => {
      info!("{} worker launched: {}", id, child);
      command.write_message(config);
      command.set_nonblocking(true);

      if let Some(l) = listeners {
        info!("sending listeners to new worker: {:?}", l);
        let res = scm_server.send_listeners(&l);
        info!("sent listeners from master: {:?}", res);
        l.close();
      };
      util::disable_close_on_exec(scm_server.fd);

      let command: Channel<ProxyRequest,ProxyResponse> = command.into();
      Ok((child.into(), command, scm_server))
    },
    Ok(ForkResult::Child) => {
      trace!("child({}):\twill spawn a child", unsafe { libc::getpid() });
      Command::new(executable_path)
        .arg("worker")
        .arg("--id")
        .arg(id)
        .arg("--fd")
        .arg(client.as_raw_fd().to_string())
        .arg("--scm")
        .arg(scm_client.as_raw_fd().to_string())
        .arg("--configuration-state-fd")
        .arg(state_file.as_raw_fd().to_string())
        .arg("--command-buffer-size")
        .arg(config.command_buffer_size.to_string())
        .arg("--max-command-buffer-size")
        .arg(config.max_command_buffer_size.to_string())
        .exec();

      unreachable!();
    },
    Err(e) => {
      error!("Error during fork(): {}", e);
      Err(e)
    }
  }
}

#[cfg(target_os = "linux")]
pub unsafe fn get_executable_path() -> String {
  use std::fs;

  let path         = fs::read_link("/proc/self/exe").expect("/proc/self/exe doesn't exist");
  let mut path_str = path.into_os_string().into_string().expect("Failed to convert PathBuf to String");

  if path_str.ends_with(" (deleted)") {
    // The kernel appends " (deleted)" to the symlink when the original executable has been replaced
    let len = path_str.len();
    path_str.truncate(len - 10)
  }

  path_str
}

#[cfg(target_os = "macos")]
extern {
  pub fn _NSGetExecutablePath(buf: *mut c_char, size: *mut uint32_t) -> int32_t;
}

#[cfg(target_os = "macos")]
pub unsafe fn get_executable_path() -> String {
  let capacity = 2000;
  let mut temp:Vec<u8> = Vec::with_capacity(capacity);
  temp.extend(repeat(0).take(capacity));
  let pathbuf = CString::from_vec_unchecked(temp);
  let ptr = pathbuf.into_raw();

  let mut size:uint32_t = capacity as u32;
  if _NSGetExecutablePath(ptr, &mut size) == 0 {

    let mut temp2:Vec<u8> = Vec::with_capacity(capacity);
    temp2.extend(repeat(0).take(capacity));
    let pathbuf2 = CString::from_vec_unchecked(temp2);
    let ptr2 = pathbuf2.into_raw();

    if libc::realpath(ptr, ptr2) != null_mut() {
      let path = CString::from_raw(ptr2);
      path.to_str().expect("failed to convert CString to String").to_string()

    } else {
      panic!();
    }
  } else {
    panic!("buffer too small");
  }
}

#[cfg(target_os = "freebsd")]
pub unsafe fn get_executable_path() -> String {
    let mut capacity = 2000;
    let mut path: Vec<u8> = Vec::with_capacity(capacity);
    path.extend(repeat(0).take(capacity));

    let mib: Vec<i32> = vec![CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1];
    let len = mib.len() * size_of::<i32>();
    let element_size = size_of::<i32>();

    let res = sysctl(
        mib.as_ptr(),
        (len / element_size) as u32,
        path.as_mut_ptr() as *mut c_void,
        &mut capacity,
        std::ptr::null() as *const c_void,
        0,
    );
    if res != 0 {
        panic!("Could not retrieve the path of the executable");
    }

    String::from_raw_parts(path.as_mut_ptr(), capacity - 1, path.len())
}
<|endoftext|>


<commit_before>//! Tracing and profiling functions. Error and warning log.

use libc::{c_char, c_int, c_void};
use std::ffi::{CStr, CString};
use std::mem;
use std::ptr;
use std::str;
use std::time::Duration;

use super::ffi;
use {Result, Connection};
use error::error_from_sqlite_code;

/// Set up the process-wide SQLite error logging callback.
/// This function is marked unsafe for two reasons:
///
/// * The function is not threadsafe. No other SQLite calls may be made while
///   `config_log` is running, and multiple threads may not call `config_log`
///   simultaneously.
/// * The provided `callback` itself function has two requirements:
///     * It must not invoke any SQLite calls.
///     * It must be threadsafe if SQLite is used in a multithreaded way.
///
/// cf [The Error And Warning Log](http://sqlite.org/errlog.html).
pub unsafe fn config_log(callback: Option<fn(c_int, &str)>) -> Result<()> {
    extern "C" fn log_callback(p_arg: *mut c_void, err: c_int, msg: *const c_char) {
        let c_slice = unsafe { CStr::from_ptr(msg).to_bytes() };
        let callback: fn(c_int, &str) = unsafe { mem::transmute(p_arg) };

        if let Ok(s) = str::from_utf8(c_slice) {
            callback(err, s);
        }
    }

    let rc = match callback {
        Some(f) => {
            let p_arg: *mut c_void = mem::transmute(f);
            ffi::sqlite3_config(ffi::SQLITE_CONFIG_LOG, Some(log_callback), p_arg)
        }
        None => {
            let nullptr: *mut c_void = ptr::null_mut();
            ffi::sqlite3_config(ffi::SQLITE_CONFIG_LOG, nullptr, nullptr)
        }
    };

    if rc == ffi::SQLITE_OK {
        Ok(())
    } else {
        Err(error_from_sqlite_code(rc, None))
    }
}

/// Write a message into the error log established by `config_log`.
pub fn log(err_code: c_int, msg: &str) {
    let msg = CString::new(msg).expect("SQLite log messages cannot contain embedded zeroes");
    unsafe {
        ffi::sqlite3_log(err_code, msg.as_ptr());
    }
}

impl Connection {
    /// Register or clear a callback function that can be used for tracing the execution of SQL statements.
    ///
    /// Prepared statement placeholders are replaced/logged with their assigned values.
    /// There can only be a single tracer defined for each database connection.
    /// Setting a new tracer clears the old one.
    pub fn trace(&mut self, trace_fn: Option<fn(&str)>) {
        unsafe extern "C" fn trace_callback(p_arg: *mut c_void, z_sql: *const c_char) {
            let trace_fn: fn(&str) = mem::transmute(p_arg);
            let c_slice = CStr::from_ptr(z_sql).to_bytes();
            if let Ok(s) = str::from_utf8(c_slice) {
                trace_fn(s);
            }
        }

        let c = self.db.borrow_mut();
        match trace_fn {
            Some(f) => unsafe {
                ffi::sqlite3_trace(c.db(), Some(trace_callback), mem::transmute(f));
            },
            None => unsafe {
                ffi::sqlite3_trace(c.db(), None, ptr::null_mut());
            },
        }
    }

    /// Register or clear a callback function that can be used for profiling the execution of SQL statements.
    ///
    /// There can only be a single profiler defined for each database connection.
    /// Setting a new profiler clears the old one.
    pub fn profile(&mut self, profile_fn: Option<fn(&str, Duration)>) {
        unsafe extern "C" fn profile_callback(p_arg: *mut c_void,
                                              z_sql: *const c_char,
                                              nanoseconds: u64) {
            let profile_fn: fn(&str, Duration) = mem::transmute(p_arg);
            let c_slice = CStr::from_ptr(z_sql).to_bytes();
            if let Ok(s) = str::from_utf8(c_slice) {
                const NANOS_PER_SEC: u64 = 1_000_000_000;

                let duration = Duration::new(nanoseconds / NANOS_PER_SEC,
                                             (nanoseconds % NANOS_PER_SEC) as u32);
                profile_fn(s, duration);
            }
        }

        let c = self.db.borrow_mut();
        match profile_fn {
            Some(f) => unsafe {
                ffi::sqlite3_profile(c.db(), Some(profile_callback), mem::transmute(f))
            },
            None => unsafe { ffi::sqlite3_profile(c.db(), None, ptr::null_mut()) },
        };
    }
}

#[cfg(test)]
mod test {
    use std::sync::Mutex;
    use std::time::Duration;

    use Connection;

    #[test]
    fn test_trace() {
        lazy_static! {
            static ref TRACED_STMTS: Mutex<Vec<String>> = Mutex::new(Vec::new());
        }
        fn tracer(s: &str) {
            let mut traced_stmts = TRACED_STMTS.lock().unwrap();
            traced_stmts.push(s.to_owned());
        }

        let mut db = Connection::open_in_memory().unwrap();
        db.trace(Some(tracer));
        {
            let _ = db.query_row("SELECT ?", &[&1i32], |_| {});
            let _ = db.query_row("SELECT ?", &[&"hello"], |_| {});
        }
        db.trace(None);
        {
            let _ = db.query_row("SELECT ?", &[&2i32], |_| {});
            let _ = db.query_row("SELECT ?", &[&"goodbye"], |_| {});
        }

        let traced_stmts = TRACED_STMTS.lock().unwrap();
        assert_eq!(traced_stmts.len(), 2);
        assert_eq!(traced_stmts[0], "SELECT 1");
        assert_eq!(traced_stmts[1], "SELECT 'hello'");
    }

    #[test]
    fn test_profile() {
        lazy_static! {
            static ref PROFILED: Mutex<Vec<(String, Duration)>> = Mutex::new(Vec::new());
        }
        fn profiler(s: &str, d: Duration) {
            let mut profiled = PROFILED.lock().unwrap();
            profiled.push((s.to_owned(), d));
        }

        let mut db = Connection::open_in_memory().unwrap();
        db.profile(Some(profiler));
        db.execute_batch("PRAGMA application_id = 1").unwrap();
        db.profile(None);
        db.execute_batch("PRAGMA application_id = 2").unwrap();

        let profiled = PROFILED.lock().unwrap();
        assert_eq!(profiled.len(), 1);
        assert_eq!(profiled[0].0, "PRAGMA application_id = 1");
    }
}
<commit_msg>Fix crash due to 0-sized function type change in nightly<commit_after>//! Tracing and profiling functions. Error and warning log.

use libc::{c_char, c_int, c_void};
use std::ffi::{CStr, CString};
use std::mem;
use std::ptr;
use std::str;
use std::time::Duration;

use super::ffi;
use {Result, Connection};
use error::error_from_sqlite_code;

/// Set up the process-wide SQLite error logging callback.
/// This function is marked unsafe for two reasons:
///
/// * The function is not threadsafe. No other SQLite calls may be made while
///   `config_log` is running, and multiple threads may not call `config_log`
///   simultaneously.
/// * The provided `callback` itself function has two requirements:
///     * It must not invoke any SQLite calls.
///     * It must be threadsafe if SQLite is used in a multithreaded way.
///
/// cf [The Error And Warning Log](http://sqlite.org/errlog.html).
pub unsafe fn config_log(callback: Option<fn(c_int, &str)>) -> Result<()> {
    extern "C" fn log_callback(p_arg: *mut c_void, err: c_int, msg: *const c_char) {
        let c_slice = unsafe { CStr::from_ptr(msg).to_bytes() };
        let callback: fn(c_int, &str) = unsafe { mem::transmute(p_arg) };

        if let Ok(s) = str::from_utf8(c_slice) {
            callback(err, s);
        }
    }

    let rc = match callback {
        Some(f) => {
            let p_arg: *mut c_void = mem::transmute(f);
            ffi::sqlite3_config(ffi::SQLITE_CONFIG_LOG,
                                log_callback as extern "C" fn(_, _, _),
                                p_arg)
        }
        None => {
            let nullptr: *mut c_void = ptr::null_mut();
            ffi::sqlite3_config(ffi::SQLITE_CONFIG_LOG, nullptr, nullptr)
        }
    };

    if rc == ffi::SQLITE_OK {
        Ok(())
    } else {
        Err(error_from_sqlite_code(rc, None))
    }
}

/// Write a message into the error log established by `config_log`.
pub fn log(err_code: c_int, msg: &str) {
    let msg = CString::new(msg).expect("SQLite log messages cannot contain embedded zeroes");
    unsafe {
        ffi::sqlite3_log(err_code, msg.as_ptr());
    }
}

impl Connection {
    /// Register or clear a callback function that can be used for tracing the execution of SQL statements.
    ///
    /// Prepared statement placeholders are replaced/logged with their assigned values.
    /// There can only be a single tracer defined for each database connection.
    /// Setting a new tracer clears the old one.
    pub fn trace(&mut self, trace_fn: Option<fn(&str)>) {
        unsafe extern "C" fn trace_callback(p_arg: *mut c_void, z_sql: *const c_char) {
            let trace_fn: fn(&str) = mem::transmute(p_arg);
            let c_slice = CStr::from_ptr(z_sql).to_bytes();
            if let Ok(s) = str::from_utf8(c_slice) {
                trace_fn(s);
            }
        }

        let c = self.db.borrow_mut();
        match trace_fn {
            Some(f) => unsafe {
                ffi::sqlite3_trace(c.db(), Some(trace_callback), mem::transmute(f));
            },
            None => unsafe {
                ffi::sqlite3_trace(c.db(), None, ptr::null_mut());
            },
        }
    }

    /// Register or clear a callback function that can be used for profiling the execution of SQL statements.
    ///
    /// There can only be a single profiler defined for each database connection.
    /// Setting a new profiler clears the old one.
    pub fn profile(&mut self, profile_fn: Option<fn(&str, Duration)>) {
        unsafe extern "C" fn profile_callback(p_arg: *mut c_void,
                                              z_sql: *const c_char,
                                              nanoseconds: u64) {
            let profile_fn: fn(&str, Duration) = mem::transmute(p_arg);
            let c_slice = CStr::from_ptr(z_sql).to_bytes();
            if let Ok(s) = str::from_utf8(c_slice) {
                const NANOS_PER_SEC: u64 = 1_000_000_000;

                let duration = Duration::new(nanoseconds / NANOS_PER_SEC,
                                             (nanoseconds % NANOS_PER_SEC) as u32);
                profile_fn(s, duration);
            }
        }

        let c = self.db.borrow_mut();
        match profile_fn {
            Some(f) => unsafe {
                ffi::sqlite3_profile(c.db(), Some(profile_callback), mem::transmute(f))
            },
            None => unsafe { ffi::sqlite3_profile(c.db(), None, ptr::null_mut()) },
        };
    }
}

#[cfg(test)]
mod test {
    use std::sync::Mutex;
    use std::time::Duration;

    use Connection;

    #[test]
    fn test_trace() {
        lazy_static! {
            static ref TRACED_STMTS: Mutex<Vec<String>> = Mutex::new(Vec::new());
        }
        fn tracer(s: &str) {
            let mut traced_stmts = TRACED_STMTS.lock().unwrap();
            traced_stmts.push(s.to_owned());
        }

        let mut db = Connection::open_in_memory().unwrap();
        db.trace(Some(tracer));
        {
            let _ = db.query_row("SELECT ?", &[&1i32], |_| {});
            let _ = db.query_row("SELECT ?", &[&"hello"], |_| {});
        }
        db.trace(None);
        {
            let _ = db.query_row("SELECT ?", &[&2i32], |_| {});
            let _ = db.query_row("SELECT ?", &[&"goodbye"], |_| {});
        }

        let traced_stmts = TRACED_STMTS.lock().unwrap();
        assert_eq!(traced_stmts.len(), 2);
        assert_eq!(traced_stmts[0], "SELECT 1");
        assert_eq!(traced_stmts[1], "SELECT 'hello'");
    }

    #[test]
    fn test_profile() {
        lazy_static! {
            static ref PROFILED: Mutex<Vec<(String, Duration)>> = Mutex::new(Vec::new());
        }
        fn profiler(s: &str, d: Duration) {
            let mut profiled = PROFILED.lock().unwrap();
            profiled.push((s.to_owned(), d));
        }

        let mut db = Connection::open_in_memory().unwrap();
        db.profile(Some(profiler));
        db.execute_batch("PRAGMA application_id = 1").unwrap();
        db.profile(None);
        db.execute_batch("PRAGMA application_id = 2").unwrap();

        let profiled = PROFILED.lock().unwrap();
        assert_eq!(profiled.len(), 1);
        assert_eq!(profiled[0].0, "PRAGMA application_id = 1");
    }
}
<|endoftext|>


<commit_before>//! Type conversions for binding parameters and getting query results.

use super::{PreparedStatement, ResultRow,
            ColIx, ParamIx};
use super::{
    SqliteResult, SqliteErrorCode, SqliteError,
};
use super::ColumnType::SQLITE_NULL;

use time;

/// Values that can be bound to parameters in prepared statements.
pub trait ToSql {
    /// Bind the `ix`th parameter to this value (`self`).
    fn to_sql(&self, s: &mut PreparedStatement, ix: ParamIx) -> SqliteResult<()>;
}

/// A trait for result values from a query.
///
/// cf [sqlite3 result values][column].
///
/// *inspired by sfackler's FromSql (and some haskell bindings?)*
///
/// [column]: http://www.sqlite.org/c3ref/column_blob.html
///
///   - *TODO: consider a `types` submodule*
///   - *TODO: many more implementors, including Option<T>*
pub trait FromSql {
    /// Try to extract a `Self` type value from the `col`th colum of a `ResultRow`.
    fn from_sql(row: &ResultRow, col: ColIx) -> SqliteResult<Self>;
}

impl ToSql for i32 {
    fn to_sql(&self, s: &mut PreparedStatement, ix: ParamIx) -> SqliteResult<()> {
        s.bind_int(ix, *self)
    }
}

impl FromSql for i32 {
    fn from_sql(row: &ResultRow, col: ColIx) -> SqliteResult<i32> { Ok(row.column_int(col)) }
}

impl ToSql for i64 {
    fn to_sql(&self, s: &mut PreparedStatement, ix: ParamIx) -> SqliteResult<()> {
        s.bind_int64(ix, *self)
    }
}

impl FromSql for i64 {
    fn from_sql(row: &ResultRow, col: ColIx) -> SqliteResult<i64> { Ok(row.column_int64(col)) }
}

impl ToSql for f64 {
    fn to_sql(&self, s: &mut PreparedStatement, ix: ParamIx) -> SqliteResult<()> {
        s.bind_double(ix, *self)
    }
}

impl FromSql for f64 {
    fn from_sql(row: &ResultRow, col: ColIx) -> SqliteResult<f64> { Ok(row.column_double(col)) }
}

impl ToSql for u64 {
    fn to_sql(&self, s: &mut PreparedStatement, ix: ParamIx) -> SqliteResult<()> {
        s.bind_int64(ix as i64, *self);
    }
}

impl FromSql for u64 {
    fn from_sql(row: &ResultRow, col: ColIx) -> SqliteResult<u64> {
        Ok(row.column_int64(col) as u64);
    }
}

impl ToSql for bool {
    fn to_sql(&self, s: &mut PreparedStatement, ix: ParamIx) -> SqliteResult<()> {
        s.bind_int(ix, if *self { 1 } else { 0 })
    }
}

impl FromSql for bool {
    fn from_sql(row: &ResultRow, col: ColIx) -> SqliteResult<bool> { Ok(row.column_int(col)!=0) }
}

impl<T: ToSql + Clone> ToSql for Option<T> {
    fn to_sql(&self, s: &mut PreparedStatement, ix: ParamIx) -> SqliteResult<()> {
        match (*self).clone() {
            Some(x) => x.to_sql(s, ix),
            None => s.bind_null(ix)
        }
    }
}

impl<T: FromSql + Clone> FromSql for Option<T> {
    fn from_sql(row: &ResultRow, col: ColIx) -> SqliteResult<Option<T>> {
        match row.column_type(col) {
            SQLITE_NULL => Ok(None),
            _ => FromSql::from_sql(row, col).map(|x| Some(x))
        }
    }
}

impl ToSql for String {
    fn to_sql(&self, s: &mut PreparedStatement, ix: ParamIx) -> SqliteResult<()> {
        s.bind_text(ix, (*self).as_ref())
    }
}

impl FromSql for String {
    fn from_sql(row: &ResultRow, col: ColIx) -> SqliteResult<String> {
        Ok(row.column_text(col).unwrap_or(String::new()))
    }
}

impl<'a> ToSql for &'a [u8] {
    fn to_sql(&self, s: &mut PreparedStatement, ix: ParamIx) -> SqliteResult<()> {
        s.bind_blob(ix, *self)
    }
}

impl FromSql for Vec<u8> {
    fn from_sql(row: &ResultRow, col: ColIx) -> SqliteResult<Vec<u8>> {
        Ok(row.column_blob(col).unwrap_or(Vec::new()))
    }
}

/// Format of sqlite date strings
///
/// From [Date And Time Functions][lang_datefunc]:
/// > The datetime() function returns "YYYY-MM-DD HH:MM:SS"
/// [lang_datefunc]: http://www.sqlite.org/lang_datefunc.html
pub static SQLITE_TIME_FMT: &'static str = "%F %T";

impl FromSql for time::Tm {
    fn from_sql(row: &ResultRow, col: ColIx) -> SqliteResult<time::Tm> {
        let txt = row.column_text(col).unwrap_or(String::new());
        Ok( try!(time::strptime(txt.as_ref(), SQLITE_TIME_FMT)) )
    }
}

impl From<time::ParseError> for SqliteError {
    fn from(err: time::ParseError) -> SqliteError {
        SqliteError {
            kind: SqliteErrorCode::SQLITE_MISMATCH,
            desc: "Time did not match expected format",
            detail: Some(format!("Time parsing error: {}", err)),
        }
    }
}

impl ToSql for time::Timespec {
    fn to_sql(&self, s: &mut PreparedStatement, ix: ParamIx) -> SqliteResult<()> {
        let timestr = time::at_utc(*self).strftime(SQLITE_TIME_FMT)
            .unwrap() // unit tests ensure SQLITE_TIME_FMT is ok
            .to_string();
        s.bind_text(ix, timestr.as_ref())
    }
}

impl FromSql for time::Timespec {
    /// TODO: propagate error message
    fn from_sql(row: &ResultRow, col: ColIx) -> SqliteResult<time::Timespec> {
        let tmo: SqliteResult<time::Tm> = FromSql::from_sql(row, col);
        tmo.map(|tm| tm.to_timespec())
    }
}

#[cfg(test)]
mod tests {
    use time::Tm;
    use super::super::{DatabaseConnection, SqliteResult, ResultSet};
    use super::super::{ResultRowAccess};

    fn with_query<T, F>(sql: &str, mut f: F) -> SqliteResult<T>
        where F: FnMut(&mut ResultSet) -> T
    {
        let db = try!(DatabaseConnection::in_memory());
        let mut s = try!(db.prepare(sql));
        let mut rows = s.execute();
        Ok(f(&mut rows))
    }

    #[test]
    fn get_tm() {
        fn go() -> SqliteResult<()> {
            let conn = try!(DatabaseConnection::in_memory());
            let mut stmt = try!(
                conn.prepare("select datetime('2001-01-01', 'weekday 3', '3 hours')"));
            let mut results = stmt.execute();
            match results.step() {
                Ok(Some(ref mut row)) => {
                    assert_eq!(
                        row.get::<u32, Tm>(0),
                        Tm { tm_sec: 0,
                             tm_min: 0,
                             tm_hour: 3,
                             tm_mday: 3,
                             tm_mon: 0,
                             tm_year: 101,
                             tm_wday: 0,
                             tm_yday: 0,
                             tm_isdst: 0,
                             tm_utcoff: 0,
                             tm_nsec: 0
                        });
                    Ok(())
                },
                Ok(None) => panic!("no row"),
                Err(oops) =>  panic!("error: {:?}", oops)
            }
        }
        go().unwrap();
    }

    #[test]
    fn get_invalid_tm() {
        with_query("select 'not a time'", |results| {
            match results.step() {
                Ok(Some(ref mut row)) => {
                    let x : SqliteResult<Tm> = row.get_opt(0u32);
                    assert!(x.is_err());
                },
                Ok(None) => panic!("no row"),
                Err(oops) =>  panic!("error: {:?}", oops)
            };
        }).unwrap();
    }

    #[test]
    fn select_blob() {
        with_query("select x'ff0db0'", |results| {
            match results.step() {
                Ok(Some(ref mut row)) => {
                    let x : SqliteResult<Vec<u8>> = row.get_opt(0u32);
                    assert_eq!(x.ok().unwrap(), [0xff, 0x0d, 0xb0].to_vec());
                },
                Ok(None) => panic!("no row"),
                Err(oops) =>  panic!("error: {:?}", oops)
            };
        }).unwrap();
    }
}

// Local Variables:
// flycheck-rust-crate-root: "lib.rs"
// End:
<commit_msg>for another<commit_after>//! Type conversions for binding parameters and getting query results.

use super::{PreparedStatement, ResultRow,
            ColIx, ParamIx};
use super::{
    SqliteResult, SqliteErrorCode, SqliteError,
};
use super::ColumnType::SQLITE_NULL;

use time;

/// Values that can be bound to parameters in prepared statements.
pub trait ToSql {
    /// Bind the `ix`th parameter to this value (`self`).
    fn to_sql(&self, s: &mut PreparedStatement, ix: ParamIx) -> SqliteResult<()>;
}

/// A trait for result values from a query.
///
/// cf [sqlite3 result values][column].
///
/// *inspired by sfackler's FromSql (and some haskell bindings?)*
///
/// [column]: http://www.sqlite.org/c3ref/column_blob.html
///
///   - *TODO: consider a `types` submodule*
///   - *TODO: many more implementors, including Option<T>*
pub trait FromSql {
    /// Try to extract a `Self` type value from the `col`th colum of a `ResultRow`.
    fn from_sql(row: &ResultRow, col: ColIx) -> SqliteResult<Self>;
}

impl ToSql for i32 {
    fn to_sql(&self, s: &mut PreparedStatement, ix: ParamIx) -> SqliteResult<()> {
        s.bind_int(ix, *self)
    }
}

impl FromSql for i32 {
    fn from_sql(row: &ResultRow, col: ColIx) -> SqliteResult<i32> { Ok(row.column_int(col)) }
}

impl ToSql for i64 {
    fn to_sql(&self, s: &mut PreparedStatement, ix: ParamIx) -> SqliteResult<()> {
        s.bind_int64(ix, *self)
    }
}

impl FromSql for i64 {
    fn from_sql(row: &ResultRow, col: ColIx) -> SqliteResult<i64> { Ok(row.column_int64(col)) }
}

impl ToSql for f64 {
    fn to_sql(&self, s: &mut PreparedStatement, ix: ParamIx) -> SqliteResult<()> {
        s.bind_double(ix, *self)
    }
}

impl FromSql for f64 {
    fn from_sql(row: &ResultRow, col: ColIx) -> SqliteResult<f64> { Ok(row.column_double(col)) }
}

impl ToSql for u64 {
    fn to_sql(&self, s: &mut PreparedStatement, ix: ParamIx) -> SqliteResult<()> {
        s.bind_int64(ix as u64, *self);
    }
}

impl FromSql for u64 {
    fn from_sql(row: &ResultRow, col: ColIx) -> SqliteResult<u64> {
        Ok(row.column_int64(col) as u64);
    }
}

impl ToSql for bool {
    fn to_sql(&self, s: &mut PreparedStatement, ix: ParamIx) -> SqliteResult<()> {
        s.bind_int(ix, if *self { 1 } else { 0 })
    }
}

impl FromSql for bool {
    fn from_sql(row: &ResultRow, col: ColIx) -> SqliteResult<bool> { Ok(row.column_int(col)!=0) }
}

impl<T: ToSql + Clone> ToSql for Option<T> {
    fn to_sql(&self, s: &mut PreparedStatement, ix: ParamIx) -> SqliteResult<()> {
        match (*self).clone() {
            Some(x) => x.to_sql(s, ix),
            None => s.bind_null(ix)
        }
    }
}

impl<T: FromSql + Clone> FromSql for Option<T> {
    fn from_sql(row: &ResultRow, col: ColIx) -> SqliteResult<Option<T>> {
        match row.column_type(col) {
            SQLITE_NULL => Ok(None),
            _ => FromSql::from_sql(row, col).map(|x| Some(x))
        }
    }
}

impl ToSql for String {
    fn to_sql(&self, s: &mut PreparedStatement, ix: ParamIx) -> SqliteResult<()> {
        s.bind_text(ix, (*self).as_ref())
    }
}

impl FromSql for String {
    fn from_sql(row: &ResultRow, col: ColIx) -> SqliteResult<String> {
        Ok(row.column_text(col).unwrap_or(String::new()))
    }
}

impl<'a> ToSql for &'a [u8] {
    fn to_sql(&self, s: &mut PreparedStatement, ix: ParamIx) -> SqliteResult<()> {
        s.bind_blob(ix, *self)
    }
}

impl FromSql for Vec<u8> {
    fn from_sql(row: &ResultRow, col: ColIx) -> SqliteResult<Vec<u8>> {
        Ok(row.column_blob(col).unwrap_or(Vec::new()))
    }
}

/// Format of sqlite date strings
///
/// From [Date And Time Functions][lang_datefunc]:
/// > The datetime() function returns "YYYY-MM-DD HH:MM:SS"
/// [lang_datefunc]: http://www.sqlite.org/lang_datefunc.html
pub static SQLITE_TIME_FMT: &'static str = "%F %T";

impl FromSql for time::Tm {
    fn from_sql(row: &ResultRow, col: ColIx) -> SqliteResult<time::Tm> {
        let txt = row.column_text(col).unwrap_or(String::new());
        Ok( try!(time::strptime(txt.as_ref(), SQLITE_TIME_FMT)) )
    }
}

impl From<time::ParseError> for SqliteError {
    fn from(err: time::ParseError) -> SqliteError {
        SqliteError {
            kind: SqliteErrorCode::SQLITE_MISMATCH,
            desc: "Time did not match expected format",
            detail: Some(format!("Time parsing error: {}", err)),
        }
    }
}

impl ToSql for time::Timespec {
    fn to_sql(&self, s: &mut PreparedStatement, ix: ParamIx) -> SqliteResult<()> {
        let timestr = time::at_utc(*self).strftime(SQLITE_TIME_FMT)
            .unwrap() // unit tests ensure SQLITE_TIME_FMT is ok
            .to_string();
        s.bind_text(ix, timestr.as_ref())
    }
}

impl FromSql for time::Timespec {
    /// TODO: propagate error message
    fn from_sql(row: &ResultRow, col: ColIx) -> SqliteResult<time::Timespec> {
        let tmo: SqliteResult<time::Tm> = FromSql::from_sql(row, col);
        tmo.map(|tm| tm.to_timespec())
    }
}

#[cfg(test)]
mod tests {
    use time::Tm;
    use super::super::{DatabaseConnection, SqliteResult, ResultSet};
    use super::super::{ResultRowAccess};

    fn with_query<T, F>(sql: &str, mut f: F) -> SqliteResult<T>
        where F: FnMut(&mut ResultSet) -> T
    {
        let db = try!(DatabaseConnection::in_memory());
        let mut s = try!(db.prepare(sql));
        let mut rows = s.execute();
        Ok(f(&mut rows))
    }

    #[test]
    fn get_tm() {
        fn go() -> SqliteResult<()> {
            let conn = try!(DatabaseConnection::in_memory());
            let mut stmt = try!(
                conn.prepare("select datetime('2001-01-01', 'weekday 3', '3 hours')"));
            let mut results = stmt.execute();
            match results.step() {
                Ok(Some(ref mut row)) => {
                    assert_eq!(
                        row.get::<u32, Tm>(0),
                        Tm { tm_sec: 0,
                             tm_min: 0,
                             tm_hour: 3,
                             tm_mday: 3,
                             tm_mon: 0,
                             tm_year: 101,
                             tm_wday: 0,
                             tm_yday: 0,
                             tm_isdst: 0,
                             tm_utcoff: 0,
                             tm_nsec: 0
                        });
                    Ok(())
                },
                Ok(None) => panic!("no row"),
                Err(oops) =>  panic!("error: {:?}", oops)
            }
        }
        go().unwrap();
    }

    #[test]
    fn get_invalid_tm() {
        with_query("select 'not a time'", |results| {
            match results.step() {
                Ok(Some(ref mut row)) => {
                    let x : SqliteResult<Tm> = row.get_opt(0u32);
                    assert!(x.is_err());
                },
                Ok(None) => panic!("no row"),
                Err(oops) =>  panic!("error: {:?}", oops)
            };
        }).unwrap();
    }

    #[test]
    fn select_blob() {
        with_query("select x'ff0db0'", |results| {
            match results.step() {
                Ok(Some(ref mut row)) => {
                    let x : SqliteResult<Vec<u8>> = row.get_opt(0u32);
                    assert_eq!(x.ok().unwrap(), [0xff, 0x0d, 0xb0].to_vec());
                },
                Ok(None) => panic!("no row"),
                Err(oops) =>  panic!("error: {:?}", oops)
            };
        }).unwrap();
    }
}

// Local Variables:
// flycheck-rust-crate-root: "lib.rs"
// End:
<|endoftext|>


<commit_before>use std::process::{Command, Output};
use command;
use configuration;
use output;

pub fn provision(configuration: &configuration::Configuration) -> i32 {

    let mut packages = Vec::new();
    let mut commands = Vec::new();

    packages.extend(configuration.packages.iter().cloned());
    commands.extend(configuration.commands.clone());

    commands.reverse();
    packages.reverse();

    let mut exit_codes = Vec::new();

    while let Some(cmd) = commands.pop() {
        let shellout = run(&cmd);
        output::print_shellout::<command::Command>(&cmd, &shellout);
        let exit_status = shellout.status.clone();
        if exit_status.success() == false {
            exit_codes.push(exit_status.code().unwrap());
        }
    }

    while let Some(package) = packages.pop() {
        let shellout = install_package(&package);
        output::print_shellout::<String>(&package, &shellout);
        let exit_status = shellout.status.clone();
        if exit_status.success() == false {
            exit_codes.push(exit_status.code().unwrap());
        }
    }

    exit_codes.sort();

    match exit_codes.last() {
        Some(&0) => return 0,
        _       => return 1
    }
}

fn install_package(package: &String) -> Output {
    Command::new("sudo")
        .args(&vec!["yum", "install", "-y", package])
        .output()
        .unwrap_or_else(|e| { panic!("failed to execute process: {}", e) })
}

fn run(command: &command::Command) -> Output {
    let output_handle = Command::new(command.command.to_string())
        .args(&command.args)
        .output()
        .unwrap_or_else(|e| { panic!("failed to execute process: {}", e) });
    output_handle
}
<commit_msg>check if package is already installed and skip it<commit_after>use std::process::{Command, Output};
use command;
use configuration;
use output;

pub fn provision(configuration: &configuration::Configuration) -> i32 {

    let mut packages = Vec::new();
    let mut commands = Vec::new();

    packages.extend(configuration.packages.iter().cloned());
    commands.extend(configuration.commands.clone());

    commands.reverse();
    packages.reverse();

    let mut exit_codes = Vec::new();

    while let Some(cmd) = commands.pop() {
        let shellout = run(&cmd);
        output::print_shellout::<command::Command>(&cmd, &shellout);
        let exit_status = shellout.status.clone();
        if exit_status.success() == false {
            exit_codes.push(exit_status.code().unwrap());
        }
    }

    while let Some(package) = packages.pop() {
        if is_package_installed(&package) {
            continue;
        }
        let shellout = install_package(&package);
        output::print_shellout::<String>(&package, &shellout);
        let exit_status = shellout.status.clone();
        if exit_status.success() == false {
            exit_codes.push(exit_status.code().unwrap());
        }
    }

    exit_codes.sort();

    match exit_codes.last() {
        Some(&0) => return 0,
        _       => return 1
    }
}

fn is_package_installed(package_name: &String) -> bool {
    let command_str = format!("yum list installed {}", package_name);
    let output = Command::new(command_str)
        .output()
        .unwrap_or_else(|e| { panic!("failed to execute process: {}", e) });
    return output.status.success();
}

fn install_package(package: &String) -> Output {
    Command::new("sudo")
        .args(&vec!["yum", "install", "-y", package])
        .output()
        .unwrap_or_else(|e| { panic!("failed to execute process: {}", e) })
}

fn run(command: &command::Command) -> Output {
    let output_handle = Command::new(command.command.to_string())
        .args(&command.args)
        .output()
        .unwrap_or_else(|e| { panic!("failed to execute process: {}", e) });
    output_handle
}
<|endoftext|>


<commit_before>//! generic types for GPX

use geo::{ToGeo, Geometry};
use geo::{Point, LineString, MultiLineString};

use chrono::DateTime;
use chrono::prelude::Utc;

/// Gpx is the root element in the XML file.
#[derive(Default, Debug)]
pub struct Gpx {
    pub version: String,

    /// Metadata about the file.
    pub metadata: Option<Metadata>,

    /// A list of waypoints.
    pub waypoints: Vec<Waypoint>,

    /// A list of tracks.
    pub tracks: Vec<Track>,
}

/// Metadata is information about the GPX file, author, and copyright restrictions.
///
/// Providing rich, meaningful information about your GPX files allows others to
/// search for and use your GPS data.
#[derive(Default, Debug)]
pub struct Metadata {
    /// The name of the GPX file.
    pub name: Option<String>,

    /// A description of the contents of the GPX file.
    pub description: Option<String>,

    /// The person or organization who created the GPX file.
    pub author: Option<Person>,

    /// URLs associated with the location described in the file.
    pub links: Vec<Link>,

    /// The creation date of the file.
    pub time: Option<DateTime<Utc>>,

    /// Keywords associated with the file. Search engines or databases can use
    /// this information to classify the data.
    pub keywords: Option<String>,

    /*copyright: GpxCopyrightType,*/
    /*pub bounds: Option<Bbox<f64>>,*/
    /*extensions: GpxExtensionsType,*/
}

/// Track represents an ordered list of points describing a path.
#[derive(Default, Debug)]
pub struct Track {
    /// GPS name of track.
    pub name: Option<String>,

    /// GPS comment for track.
    pub comment: Option<String>,

    /// User description of track.
    pub description: Option<String>,

    /// Source of data. Included to give user some idea of reliability
    /// and accuracy of data.
    pub source: Option<String>,

    /// Links to external information about the track.
    pub links: Vec<Link>,

    /// Type (classification) of track.
    pub _type: Option<String>,

    /// A Track Segment holds a list of Track Points which are logically
    /// connected in order. To represent a single GPS track where GPS reception
    /// was lost, or the GPS receiver was turned off, start a new Track Segment
    /// for each continuous span of track data.
    pub segments: Vec<TrackSegment>,

    /* pub number: u8,*/
    /* extensions */
    /* trkSeg */
}

impl Track {
    /// Gives the multi-linestring that this track represents, which is multiple
    /// linestrings.
    pub fn multilinestring(&self) -> MultiLineString<f64> {
        self.segments.iter().map(|seg| seg.linestring()).collect()
    }

    /// Creates a new Track with default values.
    ///
    /// ```
    /// use gpx::{Track, TrackSegment};
    ///
    /// let mut track: Track = Track::new();
    ///
    /// let segment = TrackSegment::new();
    /// track.segments.push(segment);
    pub fn new() -> Track {
        Default::default()
    }
}

impl ToGeo<f64> for Track {
    fn to_geo(&self) -> Geometry<f64> {
        Geometry::MultiLineString(self.multilinestring())
    }
}

/// TrackSegment represents a list of track points.
///
/// This TrackSegment holds a list of Track Points which are logically
/// connected in order. To represent a single GPS track where GPS reception
/// was lost, or the GPS receiver was turned off, start a new Track Segment
/// for each continuous span of track data.
#[derive(Default, Debug)]
pub struct TrackSegment {
    /// Each Waypoint holds the coordinates, elevation, timestamp, and metadata
    /// for a single point in a track.
    pub points: Vec<Waypoint>,
    /* extensions */
}

impl TrackSegment {
    /// Gives the linestring of the segment's points, the sequence of points that
    /// comprises the track segment.
    pub fn linestring(&self) -> LineString<f64> {
        self.points.iter().map(|wpt| wpt.point()).collect()
    }

    /// Creates a new TrackSegment with default values.
    ///
    /// ```
    /// extern crate gpx;
    /// extern crate geo;
    ///
    /// use gpx::{TrackSegment, Waypoint};
    /// use geo::Point;
    ///
    /// fn main() {
    ///     let mut trkseg: TrackSegment = TrackSegment::new();
    ///
    ///     let point = Waypoint::new(Point::new(-121.97, 37.24));
    ///     trkseg.points.push(point);
    /// }
    pub fn new() -> TrackSegment {
        Default::default()
    }
}

impl ToGeo<f64> for TrackSegment {
    fn to_geo(&self) -> Geometry<f64> {
        Geometry::LineString(self.linestring())
    }
}

/// Waypoint represents a waypoint, point of interest, or named feature on a
/// map.
#[derive(Debug)]
pub struct Waypoint {
    /// The geographical point.
    point: Point<f64>,

    /// Elevation (in meters) of the point.
    pub elevation: Option<f64>,

    /// Creation/modification timestamp for element. Date and time in are in
    /// Univeral Coordinated Time (UTC), not local time! Conforms to ISO 8601
    /// specification for date/time representation. Fractional seconds are
    /// allowed for millisecond timing in tracklogs.
    pub time: Option<DateTime<Utc>>,

    /// The GPS name of the waypoint. This field will be transferred to and
    /// from the GPS. GPX does not place restrictions on the length of this
    /// field or the characters contained in it. It is up to the receiving
    /// application to validate the field before sending it to the GPS.
    pub name: Option<String>,

    /// GPS waypoint comment. Sent to GPS as comment.
    pub comment: Option<String>,

    /// A text description of the element. Holds additional information about
    /// the element intended for the user, not the GPS.
    pub description: Option<String>,

    /// Source of data. Included to give user some idea of reliability and
    /// accuracy of data. "Garmin eTrex", "USGS quad Boston North", e.g.
    pub source: Option<String>,

    /// Links to additional information about the waypoint.
    pub links: Vec<Link>,

    /// Text of GPS symbol name. For interchange with other programs, use the
    /// exact spelling of the symbol as displayed on the GPS. If the GPS
    /// abbreviates words, spell them out.
    pub symbol: Option<String>,

    /// Type (classification) of the waypoint.
    pub _type: Option<String>,

    // <magvar> degreesType </magvar> [0..1] ?
    // <geoidheight> xsd:decimal </geoidheight> [0..1] ?

    /// Type of GPS fix. `none` means GPS had no fix. To signify "the fix info
    /// is unknown", leave out `fix` entirely. Value comes from the list
    /// `{'none'|'2d'|'3d'|'dgps'|'pps'}`, where `pps` means that the military
    /// signal was used.
    pub fix: Option<Fix>,

    /// Number of satellites used to calculate the GPX fix.
    pub sat: Option<u64>,

    /// Horizontal dilution of precision.
    pub hdop: Option<f64>,

    /// Vertical dilution of precision.
    pub vdop: Option<f64>,

    /// Positional dilution of precision.
    pub pdop: Option<f64>,

    /// Number of seconds since last DGPS update, from the <ageofgpsdata> element.
    pub age: Option<f64>,

    /// ID of DGPS station used in differential correction, in the range [0, 1023].
    pub dgpsid: Option<u16>,

    // <extensions> extensionsType </extensions> [0..1] ?
}

impl Waypoint {
    /// Gives the geographical point of the waypoint.
    ///
    /// ```
    /// extern crate geo;
    /// extern crate gpx;
    ///
    /// use gpx::Waypoint;
    /// use geo::Point;
    ///
    /// fn main() {
    ///     // Kind of useless, but it shows the point.
    ///     let wpt = Waypoint::new(Point::new(-121.97, 37.24));
    ///     let point = wpt.point();
    ///
    ///     println!("waypoint latitude: {}, longitude: {}", point.lat(), point.lng());
    /// }
    /// ```
    pub fn point(&self) -> Point<f64> {
        self.point
    }

    /// Creates a new Waypoint from a given geographical point.
    ///
    /// ```
    /// extern crate geo;
    /// extern crate gpx;
    ///
    /// use gpx::Waypoint;
    /// use geo::Point;
    ///
    /// fn main() {
    ///     let point = Point::new(-121.97, 37.24);
    ///
    ///     let mut wpt = Waypoint::new(point);
    ///     wpt.elevation = Some(553.21);
    /// }
    /// ```
    pub fn new(point: Point<f64>) -> Waypoint {
        // Unfortunately we don't have an easy way to write this.
        Waypoint {
            point: point,
            elevation: None,
            time: None,
            name: None,
            comment: None,
            description: None,
            source: None,
            links: vec![],
            symbol: None,
            _type: None,
            fix: None,
            sat: None,
            hdop: None,
            vdop: None,
            pdop: None,
            age: None,
            dgpsid: None
        }
    }
}

impl ToGeo<f64> for Waypoint {
    fn to_geo(&self) -> Geometry<f64> {
        Geometry::Point(self.point())
    }
}

/// Person represents a person or organization.
#[derive(Default, Debug)]
pub struct Person {
    /// Name of person or organization.
    pub name: Option<String>,

    /// Email address.
    pub email: Option<String>,

    /// Link to Web site or other external information about person.
    pub link: Option<Link>,
}

/// Link represents a link to an external resource.
///
/// An external resource could be a web page, digital photo,
/// video clip, etc., with additional information.
#[derive(Default, Debug)]
pub struct Link {
    /// URL of hyperlink.
    pub href: String,

    /// Text of hyperlink.
    pub text: Option<String>,

    /// Mime type of content (image/jpeg)
    pub _type: Option<String>,
}

/// Type of the GPS fix.
#[derive(Debug, PartialEq)]
pub enum Fix {
    /// The GPS had no fix. To signify "the fix info is unknown", leave out the Fix entirely.
    None,
    /// 2D fix gives only longitude and latitude. It needs a minimum of 3 satellites.
    TwoDimensional,
    /// 3D fix gives longitude, latitude and altitude. It needs a minimum of 4 satellites.
    ThreeDimensional,
    /// Differential Global Positioning System.
    DGPS,
    /// Military signal.
    PPS,
    /// Other values that are not in the specification.
    Other(String)
}
<commit_msg>Derive clone for public types<commit_after>//! generic types for GPX

use geo::{ToGeo, Geometry};
use geo::{Point, LineString, MultiLineString};

use chrono::DateTime;
use chrono::prelude::Utc;

/// Gpx is the root element in the XML file.
#[derive(Clone, Default, Debug)]
pub struct Gpx {
    pub version: String,

    /// Metadata about the file.
    pub metadata: Option<Metadata>,

    /// A list of waypoints.
    pub waypoints: Vec<Waypoint>,

    /// A list of tracks.
    pub tracks: Vec<Track>,
}

/// Metadata is information about the GPX file, author, and copyright restrictions.
///
/// Providing rich, meaningful information about your GPX files allows others to
/// search for and use your GPS data.
#[derive(Clone, Default, Debug)]
pub struct Metadata {
    /// The name of the GPX file.
    pub name: Option<String>,

    /// A description of the contents of the GPX file.
    pub description: Option<String>,

    /// The person or organization who created the GPX file.
    pub author: Option<Person>,

    /// URLs associated with the location described in the file.
    pub links: Vec<Link>,

    /// The creation date of the file.
    pub time: Option<DateTime<Utc>>,

    /// Keywords associated with the file. Search engines or databases can use
    /// this information to classify the data.
    pub keywords: Option<String>,

    /*copyright: GpxCopyrightType,*/
    /*pub bounds: Option<Bbox<f64>>,*/
    /*extensions: GpxExtensionsType,*/
}

/// Track represents an ordered list of points describing a path.
#[derive(Clone, Default, Debug)]
pub struct Track {
    /// GPS name of track.
    pub name: Option<String>,

    /// GPS comment for track.
    pub comment: Option<String>,

    /// User description of track.
    pub description: Option<String>,

    /// Source of data. Included to give user some idea of reliability
    /// and accuracy of data.
    pub source: Option<String>,

    /// Links to external information about the track.
    pub links: Vec<Link>,

    /// Type (classification) of track.
    pub _type: Option<String>,

    /// A Track Segment holds a list of Track Points which are logically
    /// connected in order. To represent a single GPS track where GPS reception
    /// was lost, or the GPS receiver was turned off, start a new Track Segment
    /// for each continuous span of track data.
    pub segments: Vec<TrackSegment>,

    /* pub number: u8,*/
    /* extensions */
    /* trkSeg */
}

impl Track {
    /// Gives the multi-linestring that this track represents, which is multiple
    /// linestrings.
    pub fn multilinestring(&self) -> MultiLineString<f64> {
        self.segments.iter().map(|seg| seg.linestring()).collect()
    }

    /// Creates a new Track with default values.
    ///
    /// ```
    /// use gpx::{Track, TrackSegment};
    ///
    /// let mut track: Track = Track::new();
    ///
    /// let segment = TrackSegment::new();
    /// track.segments.push(segment);
    pub fn new() -> Track {
        Default::default()
    }
}

impl ToGeo<f64> for Track {
    fn to_geo(&self) -> Geometry<f64> {
        Geometry::MultiLineString(self.multilinestring())
    }
}

/// TrackSegment represents a list of track points.
///
/// This TrackSegment holds a list of Track Points which are logically
/// connected in order. To represent a single GPS track where GPS reception
/// was lost, or the GPS receiver was turned off, start a new Track Segment
/// for each continuous span of track data.
#[derive(Clone, Default, Debug)]
pub struct TrackSegment {
    /// Each Waypoint holds the coordinates, elevation, timestamp, and metadata
    /// for a single point in a track.
    pub points: Vec<Waypoint>,
    /* extensions */
}

impl TrackSegment {
    /// Gives the linestring of the segment's points, the sequence of points that
    /// comprises the track segment.
    pub fn linestring(&self) -> LineString<f64> {
        self.points.iter().map(|wpt| wpt.point()).collect()
    }

    /// Creates a new TrackSegment with default values.
    ///
    /// ```
    /// extern crate gpx;
    /// extern crate geo;
    ///
    /// use gpx::{TrackSegment, Waypoint};
    /// use geo::Point;
    ///
    /// fn main() {
    ///     let mut trkseg: TrackSegment = TrackSegment::new();
    ///
    ///     let point = Waypoint::new(Point::new(-121.97, 37.24));
    ///     trkseg.points.push(point);
    /// }
    pub fn new() -> TrackSegment {
        Default::default()
    }
}

impl ToGeo<f64> for TrackSegment {
    fn to_geo(&self) -> Geometry<f64> {
        Geometry::LineString(self.linestring())
    }
}

/// Waypoint represents a waypoint, point of interest, or named feature on a
/// map.
#[derive(Clone, Debug)]
pub struct Waypoint {
    /// The geographical point.
    point: Point<f64>,

    /// Elevation (in meters) of the point.
    pub elevation: Option<f64>,

    /// Creation/modification timestamp for element. Date and time in are in
    /// Univeral Coordinated Time (UTC), not local time! Conforms to ISO 8601
    /// specification for date/time representation. Fractional seconds are
    /// allowed for millisecond timing in tracklogs.
    pub time: Option<DateTime<Utc>>,

    /// The GPS name of the waypoint. This field will be transferred to and
    /// from the GPS. GPX does not place restrictions on the length of this
    /// field or the characters contained in it. It is up to the receiving
    /// application to validate the field before sending it to the GPS.
    pub name: Option<String>,

    /// GPS waypoint comment. Sent to GPS as comment.
    pub comment: Option<String>,

    /// A text description of the element. Holds additional information about
    /// the element intended for the user, not the GPS.
    pub description: Option<String>,

    /// Source of data. Included to give user some idea of reliability and
    /// accuracy of data. "Garmin eTrex", "USGS quad Boston North", e.g.
    pub source: Option<String>,

    /// Links to additional information about the waypoint.
    pub links: Vec<Link>,

    /// Text of GPS symbol name. For interchange with other programs, use the
    /// exact spelling of the symbol as displayed on the GPS. If the GPS
    /// abbreviates words, spell them out.
    pub symbol: Option<String>,

    /// Type (classification) of the waypoint.
    pub _type: Option<String>,

    // <magvar> degreesType </magvar> [0..1] ?
    // <geoidheight> xsd:decimal </geoidheight> [0..1] ?

    /// Type of GPS fix. `none` means GPS had no fix. To signify "the fix info
    /// is unknown", leave out `fix` entirely. Value comes from the list
    /// `{'none'|'2d'|'3d'|'dgps'|'pps'}`, where `pps` means that the military
    /// signal was used.
    pub fix: Option<Fix>,

    /// Number of satellites used to calculate the GPX fix.
    pub sat: Option<u64>,

    /// Horizontal dilution of precision.
    pub hdop: Option<f64>,

    /// Vertical dilution of precision.
    pub vdop: Option<f64>,

    /// Positional dilution of precision.
    pub pdop: Option<f64>,

    /// Number of seconds since last DGPS update, from the <ageofgpsdata> element.
    pub age: Option<f64>,

    /// ID of DGPS station used in differential correction, in the range [0, 1023].
    pub dgpsid: Option<u16>,

    // <extensions> extensionsType </extensions> [0..1] ?
}

impl Waypoint {
    /// Gives the geographical point of the waypoint.
    ///
    /// ```
    /// extern crate geo;
    /// extern crate gpx;
    ///
    /// use gpx::Waypoint;
    /// use geo::Point;
    ///
    /// fn main() {
    ///     // Kind of useless, but it shows the point.
    ///     let wpt = Waypoint::new(Point::new(-121.97, 37.24));
    ///     let point = wpt.point();
    ///
    ///     println!("waypoint latitude: {}, longitude: {}", point.lat(), point.lng());
    /// }
    /// ```
    pub fn point(&self) -> Point<f64> {
        self.point
    }

    /// Creates a new Waypoint from a given geographical point.
    ///
    /// ```
    /// extern crate geo;
    /// extern crate gpx;
    ///
    /// use gpx::Waypoint;
    /// use geo::Point;
    ///
    /// fn main() {
    ///     let point = Point::new(-121.97, 37.24);
    ///
    ///     let mut wpt = Waypoint::new(point);
    ///     wpt.elevation = Some(553.21);
    /// }
    /// ```
    pub fn new(point: Point<f64>) -> Waypoint {
        // Unfortunately we don't have an easy way to write this.
        Waypoint {
            point: point,
            elevation: None,
            time: None,
            name: None,
            comment: None,
            description: None,
            source: None,
            links: vec![],
            symbol: None,
            _type: None,
            fix: None,
            sat: None,
            hdop: None,
            vdop: None,
            pdop: None,
            age: None,
            dgpsid: None
        }
    }
}

impl ToGeo<f64> for Waypoint {
    fn to_geo(&self) -> Geometry<f64> {
        Geometry::Point(self.point())
    }
}

/// Person represents a person or organization.
#[derive(Clone, Default, Debug)]
pub struct Person {
    /// Name of person or organization.
    pub name: Option<String>,

    /// Email address.
    pub email: Option<String>,

    /// Link to Web site or other external information about person.
    pub link: Option<Link>,
}

/// Link represents a link to an external resource.
///
/// An external resource could be a web page, digital photo,
/// video clip, etc., with additional information.
#[derive(Clone, Default, Debug)]
pub struct Link {
    /// URL of hyperlink.
    pub href: String,

    /// Text of hyperlink.
    pub text: Option<String>,

    /// Mime type of content (image/jpeg)
    pub _type: Option<String>,
}

/// Type of the GPS fix.
#[derive(Clone, Debug, PartialEq)]
pub enum Fix {
    /// The GPS had no fix. To signify "the fix info is unknown", leave out the Fix entirely.
    None,
    /// 2D fix gives only longitude and latitude. It needs a minimum of 3 satellites.
    TwoDimensional,
    /// 3D fix gives longitude, latitude and altitude. It needs a minimum of 4 satellites.
    ThreeDimensional,
    /// Differential Global Positioning System.
    DGPS,
    /// Military signal.
    PPS,
    /// Other values that are not in the specification.
    Other(String)
}
<|endoftext|>