<commit_before>// Copyright © 2018, Canal TP and/or its affiliates. All rights reserved.
//
// This file is part of Navitia,
//     the software to build cool stuff with public transport.
//
// Hope you'll enjoy and contribute to this project,
//     powered by Canal TP (www.canaltp.fr).
// Help us simplify mobility and open public transport:
//     a non ending quest to the responsive locomotion way of traveling!
//
// LICENCE: This program is free software; you can redistribute it
// and/or modify it under the terms of the GNU Affero General Public
// License as published by the Free Software Foundation, either
// version 3 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public
// License along with this program. If not, see
// <http://www.gnu.org/licenses/>.
//
// Stay tuned using
// twitter @navitia
// IRC #navitia on freenode
// https://groups.google.com/d/forum/navitia
// www.navitia.io

use cosmogony::{Zone, ZoneIndex};
use failure::{format_err, Error};
use futures::stream::{Stream, StreamExt};
use mimir2::{
    adapters::secondary::elasticsearch::{
        self,
        internal::{IndexConfiguration, IndexMappings, IndexParameters, IndexSettings},
        ElasticsearchStorage,
    },
    domain::{
        model::{configuration::Configuration, document::Document, index::IndexVisibility},
        ports::remote::Remote,
        usecases::{
            generate_index::{GenerateIndex, GenerateIndexParameters},
            UseCase,
        },
    },
};
use mimirsbrunn::osm_reader::admin;
use mimirsbrunn::osm_reader::osm_utils;
use mimirsbrunn::utils;
use places::admin::Admin;
use serde::Serialize;
use slog_scope::{info, warn};
use std::collections::BTreeMap;
use std::collections::HashMap;
use std::sync::Arc;
use structopt::StructOpt;

trait IntoAdmin {
    fn into_admin(
        self,
        _: &BTreeMap<ZoneIndex, (String, Option<String>)>,
        langs: &[String],
        retrocompat_on_french_id: bool,
        max_weight: f64,
        all_admins: Option<&HashMap<String, Arc<Admin>>>,
    ) -> Admin;
}

// We use a new type to wrap around Addr and implement the Document trait.
#[derive(Serialize)]
struct AdminDoc(Admin);

impl AdminDoc {
    const DOC_TYPE: &'static str = "admin";
}

impl Document for AdminDoc {
    fn doc_type(&self) -> &'static str {
        Self::DOC_TYPE
    }
    fn id(&self) -> String {
        self.0.id.clone()
    }
}

async fn import_admins<S>(
    client: ElasticsearchStorage,
    config: IndexConfiguration,
    admins: S,
) -> Result<(), Error>
where
    S: Stream<Item = Admin> + Send + Sync + Unpin + 'static,
{
    let config = serde_json::to_string(&config).map_err(|err| {
        format_err!(
            "could not serialize index configuration: {}",
            err.to_string()
        )
    })?;
    let admins = admins.map(AdminDoc);
    let generate_index = GenerateIndex::new(Box::new(client));
    let parameters = GenerateIndexParameters {
        config: Configuration { value: config },
        documents: Box::new(admins),
        doc_type: String::from(AdminDoc::DOC_TYPE),
        visibility: IndexVisibility::Public,
    };
    generate_index
        .execute(parameters)
        .await
        .map_err(|err| format_err!("could not generate index: {}", err.to_string()))?;

    Ok(())
}

fn get_weight(tags: &osmpbfreader::Tags, center_tags: &osmpbfreader::Tags) -> f64 {
    // to have an admin weight we use the osm 'population' tag to priorize
    // the big zones over the small one.
    // Note: this tags is not often filled , so only some zones
    // will have a weight (but the main cities have it).
    tags.get("population")
        .and_then(|p| p.parse().ok())
        .or_else(|| center_tags.get("population")?.parse().ok())
        .unwrap_or(0.)
}

impl IntoAdmin for Zone {
    fn into_admin(
        self,
        zones_osm_id: &BTreeMap<ZoneIndex, (String, Option<String>)>,
        langs: &[String],
        french_id_retrocompatibility: bool,
        max_weight: f64,
        all_admins: Option<&HashMap<String, Arc<Admin>>>,
    ) -> Admin {
        let insee = admin::read_insee(&self.tags).map(|s| s.to_owned());
        let zip_codes = admin::read_zip_codes(&self.tags);
        let label = self.label;
        let weight = get_weight(&self.tags, &self.center_tags);
        let center = self.center.map_or(places::coord::Coord::default(), |c| {
            places::coord::Coord::new(c.lng(), c.lat())
        });
        let format_id = |id, insee| {
            // for retrocompatibity reasons, Navitia needs the
            // french admins to have an id with the insee for cities
            match insee {
                Some(insee) if french_id_retrocompatibility => format!("admin:fr:{}", insee),
                _ => format!("admin:osm:{}", id),
            }
        };
        let parent_osm_id = self
            .parent
            .and_then(|id| zones_osm_id.get(&id))
            .map(|(id, insee)| format_id(id, insee.as_ref()));
        let codes = osm_utils::get_osm_codes_from_tags(&self.tags);
        let mut admin = Admin {
            id: zones_osm_id
                .get(&self.id)
                .map(|(id, insee)| format_id(id, insee.as_ref()))
                .expect("unable to find zone id in zones_osm_id"),
            insee: insee.unwrap_or_else(|| "".to_owned()),
            level: self.admin_level.unwrap_or(0),
            label,
            name: self.name,
            zip_codes,
            weight: utils::normalize_weight(weight, max_weight),
            bbox: self.bbox,
            boundary: self.boundary,
            coord: center,
            approx_coord: Some(center.into()),
            zone_type: self.zone_type,
            parent_id: parent_osm_id,
            // Note: Since we do not really attach an admin to its hierarchy, for the moment an admin only have it's own coutry code,
            // not the country code of it's country from the hierarchy
            // (so it has a country code mainly if it is a country)
            country_codes: utils::get_country_code(&codes).into_iter().collect(),
            codes,
            names: osm_utils::get_names_from_tags(&self.tags, &langs),
            labels: self
                .international_labels
                .into_iter()
                .filter(|(k, _)| langs.contains(&k))
                .collect(),
            distance: None,
            context: None,
            administrative_regions: Vec::new(),
        };
        if let Some(ref admins) = all_admins {
            // Get a list of encompassing parent ids, which will be used as the get
            // administrative_regions.
            let mut parent_ids = Vec::new();
            let mut current = &admin;
            while current.parent_id.is_some() {
                parent_ids.push(current.parent_id.clone().unwrap());
                if let Some(par) = admins.get(parent_ids.last().unwrap()) {
                    current = par;
                } else {
                    break;
                }
            }
            admin.administrative_regions = parent_ids
                .into_iter()
                .filter_map(|a| admins.get(&a))
                .map(|x| Arc::clone(x))
                .collect::<Vec<_>>();
        }
        admin
    }
}

fn read_zones(input: &str) -> Result<impl Iterator<Item = Zone>, Error> {
    Ok(cosmogony::read_zones_from_file(input)?
        .filter_map(|r| r.map_err(|e| warn!("impossible to read zone: {}", e)).ok()))
}

async fn index_cosmogony(args: Args) -> Result<(), Error> {
    let pool = elasticsearch::remote::connection_pool_url(&args.connection_string)
        .await
        .map_err(|err| {
            format_err!(
                "could not create elasticsearch connection pool: {}",
                err.to_string()
            )
        })?;

    let client = pool
        .conn()
        .await
        .map_err(|err| format_err!("could not connect elasticsearch pool: {}", err.to_string()))?;

    let config = IndexConfiguration {
        name: args.dataset.clone(),
        parameters: IndexParameters {
            timeout: String::from("10s"),
            wait_for_active_shards: String::from("1"), // only the primary shard
        },
        settings: IndexSettings {
            base: serde_json::from_str(include_str!("../../config/admin/settings.json")).expect("invalid JSON file"),
            nb_shards: args.nb_shards,
            nb_replicas: args.nb_replicas,
        },
        mappings: IndexMappings {
            value: String::from(include_str!("../../config/admin/mappings.json")),
        },
    };

    info!("building maps");
    use cosmogony::ZoneType::City;

    let mut cosmogony_id_to_osm_id = BTreeMap::new();
    let max_weight = utils::ADMIN_MAX_WEIGHT;
    for z in read_zones(&args.input)? {
        let insee = match z.zone_type {
            Some(City) => admin::read_insee(&z.tags).map(|s| s.to_owned()),
            _ => None,
        };
        cosmogony_id_to_osm_id.insert(z.id, (z.osm_id.clone(), insee));
    }
    let cosmogony_id_to_osm_id = cosmogony_id_to_osm_id;

    info!("building admins hierarchy");
    let admins_without_boundaries = read_zones(&args.input)?
        .map(|mut zone| {
            zone.boundary = None;
            let admin = zone.into_admin(
                &cosmogony_id_to_osm_id,
                &args.langs,
                args.french_id_retrocompatibility,
                max_weight,
                None,
            );
            (admin.id.clone(), Arc::new(admin))
        })
        .collect::<HashMap<_, _>>();

    info!("importing cosmogony into Mimir");

    let admins = read_zones(&args.input)?.map(move |z| {
        z.into_admin(
            &cosmogony_id_to_osm_id,
            &args.langs,
            args.french_id_retrocompatibility,
            max_weight,
            Some(&admins_without_boundaries),
        )
    });
    import_admins(client, config, futures::stream::iter(admins)).await
}

#[derive(StructOpt, Debug)]
struct Args {
    /// cosmogony file
    #[structopt(short = "i", long = "input")]
    input: String,
    /// Elasticsearch parameters.
    #[structopt(
        short = "c",
        long = "connection-string",
        default_value = "http://localhost:9200/munin"
    )]
    connection_string: String,
    /// Name of the dataset.
    #[structopt(short = "d", long = "dataset", default_value = "fr")]
    dataset: String,
    /// Number of shards for the es index
    #[structopt(short = "s", long = "nb-shards", default_value = "1")]
    nb_shards: usize,
    /// Number of replicas for the es index
    #[structopt(short = "r", long = "nb-replicas", default_value = "1")]
    nb_replicas: usize,
    /// Languages codes, used to build i18n names and labels
    #[structopt(name = "lang", short, long)]
    langs: Vec<String>,
    /// Retrocompatibiilty on french admin id
    /// if activated, the french administrative regions will have an id like 'admin:fr:{insee}'
    /// instead of 'admin:osm:{osm_id}'
    #[structopt(long = "french-id-retrocompatibility")]
    french_id_retrocompatibility: bool,
}

#[tokio::main]
async fn main() {
    mimirsbrunn::utils::launch_async(index_cosmogony).await;
}
<commit_msg>remove retrocompatibility admin insee<commit_after>// Copyright © 2018, Canal TP and/or its affiliates. All rights reserved.
//
// This file is part of Navitia,
//     the software to build cool stuff with public transport.
//
// Hope you'll enjoy and contribute to this project,
//     powered by Canal TP (www.canaltp.fr).
// Help us simplify mobility and open public transport:
//     a non ending quest to the responsive locomotion way of traveling!
//
// LICENCE: This program is free software; you can redistribute it
// and/or modify it under the terms of the GNU Affero General Public
// License as published by the Free Software Foundation, either
// version 3 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public
// License along with this program. If not, see
// <http://www.gnu.org/licenses/>.
//
// Stay tuned using
// twitter @navitia
// IRC #navitia on freenode
// https://groups.google.com/d/forum/navitia
// www.navitia.io

use cosmogony::{Zone, ZoneIndex};
use failure::{format_err, Error};
use futures::stream::{Stream, StreamExt};
use mimir2::{
    adapters::secondary::elasticsearch::{
        self,
        internal::{IndexConfiguration, IndexMappings, IndexParameters, IndexSettings},
        ElasticsearchStorage,
    },
    domain::{
        model::{configuration::Configuration, document::Document, index::IndexVisibility},
        ports::remote::Remote,
        usecases::{
            generate_index::{GenerateIndex, GenerateIndexParameters},
            UseCase,
        },
    },
};
use mimirsbrunn::osm_reader::admin;
use mimirsbrunn::osm_reader::osm_utils;
use mimirsbrunn::utils;
use places::admin::Admin;
use serde::Serialize;
use slog_scope::{info, warn};
use std::collections::BTreeMap;
use std::collections::HashMap;
use std::sync::Arc;
use structopt::StructOpt;

trait IntoAdmin {
    fn into_admin(
        self,
        _: &BTreeMap<ZoneIndex, (String, Option<String>)>,
        langs: &[String],
        max_weight: f64,
        all_admins: Option<&HashMap<String, Arc<Admin>>>,
    ) -> Admin;
}

// We use a new type to wrap around Addr and implement the Document trait.
#[derive(Serialize)]
struct AdminDoc(Admin);

impl AdminDoc {
    const DOC_TYPE: &'static str = "admin";
}

impl Document for AdminDoc {
    fn doc_type(&self) -> &'static str {
        Self::DOC_TYPE
    }
    fn id(&self) -> String {
        self.0.id.clone()
    }
}

async fn import_admins<S>(
    client: ElasticsearchStorage,
    config: IndexConfiguration,
    admins: S,
) -> Result<(), Error>
where
    S: Stream<Item = Admin> + Send + Sync + Unpin + 'static,
{
    let config = serde_json::to_string(&config).map_err(|err| {
        format_err!(
            "could not serialize index configuration: {}",
            err.to_string()
        )
    })?;
    let admins = admins.map(AdminDoc);
    let generate_index = GenerateIndex::new(Box::new(client));
    let parameters = GenerateIndexParameters {
        config: Configuration { value: config },
        documents: Box::new(admins),
        doc_type: String::from(AdminDoc::DOC_TYPE),
        visibility: IndexVisibility::Public,
    };
    generate_index
        .execute(parameters)
        .await
        .map_err(|err| format_err!("could not generate index: {}", err.to_string()))?;

    Ok(())
}

fn get_weight(tags: &osmpbfreader::Tags, center_tags: &osmpbfreader::Tags) -> f64 {
    // to have an admin weight we use the osm 'population' tag to priorize
    // the big zones over the small one.
    // Note: this tags is not often filled , so only some zones
    // will have a weight (but the main cities have it).
    tags.get("population")
        .and_then(|p| p.parse().ok())
        .or_else(|| center_tags.get("population")?.parse().ok())
        .unwrap_or(0.)
}

impl IntoAdmin for Zone {
    fn into_admin(
        self,
        zones_osm_id: &BTreeMap<ZoneIndex, (String, Option<String>)>,
        langs: &[String],
        max_weight: f64,
        all_admins: Option<&HashMap<String, Arc<Admin>>>,
    ) -> Admin {
        let insee = admin::read_insee(&self.tags).map(|s| s.to_owned());
        let zip_codes = admin::read_zip_codes(&self.tags);
        let label = self.label;
        let weight = get_weight(&self.tags, &self.center_tags);
        let center = self.center.map_or(places::coord::Coord::default(), |c| {
            places::coord::Coord::new(c.lng(), c.lat())
        });
        let format_id = |id, _insee| format!("admin:osm:{}", id);
        let parent_osm_id = self
            .parent
            .and_then(|id| zones_osm_id.get(&id))
            .map(|(id, insee)| format_id(id, insee.as_ref()));
        let codes = osm_utils::get_osm_codes_from_tags(&self.tags);
        let mut admin = Admin {
            id: zones_osm_id
                .get(&self.id)
                .map(|(id, insee)| format_id(id, insee.as_ref()))
                .expect("unable to find zone id in zones_osm_id"),
            insee: insee.unwrap_or_else(|| "".to_owned()),
            level: self.admin_level.unwrap_or(0),
            label,
            name: self.name,
            zip_codes,
            weight: utils::normalize_weight(weight, max_weight),
            bbox: self.bbox,
            boundary: self.boundary,
            coord: center,
            approx_coord: Some(center.into()),
            zone_type: self.zone_type,
            parent_id: parent_osm_id,
            // Note: Since we do not really attach an admin to its hierarchy, for the moment an admin only have it's own coutry code,
            // not the country code of it's country from the hierarchy
            // (so it has a country code mainly if it is a country)
            country_codes: utils::get_country_code(&codes).into_iter().collect(),
            codes,
            names: osm_utils::get_names_from_tags(&self.tags, &langs),
            labels: self
                .international_labels
                .into_iter()
                .filter(|(k, _)| langs.contains(&k))
                .collect(),
            distance: None,
            context: None,
            administrative_regions: Vec::new(),
        };
        if let Some(ref admins) = all_admins {
            // Get a list of encompassing parent ids, which will be used as the get
            // administrative_regions.
            let mut parent_ids = Vec::new();
            let mut current = &admin;
            while current.parent_id.is_some() {
                parent_ids.push(current.parent_id.clone().unwrap());
                if let Some(par) = admins.get(parent_ids.last().unwrap()) {
                    current = par;
                } else {
                    break;
                }
            }
            admin.administrative_regions = parent_ids
                .into_iter()
                .filter_map(|a| admins.get(&a))
                .map(|x| Arc::clone(x))
                .collect::<Vec<_>>();
        }
        admin
    }
}

fn read_zones(input: &str) -> Result<impl Iterator<Item = Zone>, Error> {
    Ok(cosmogony::read_zones_from_file(input)?
        .filter_map(|r| r.map_err(|e| warn!("impossible to read zone: {}", e)).ok()))
}

async fn index_cosmogony(args: Args) -> Result<(), Error> {
    let pool = elasticsearch::remote::connection_pool_url(&args.connection_string)
        .await
        .map_err(|err| {
            format_err!(
                "could not create elasticsearch connection pool: {}",
                err.to_string()
            )
        })?;

    let client = pool
        .conn()
        .await
        .map_err(|err| format_err!("could not connect elasticsearch pool: {}", err.to_string()))?;

    let config = IndexConfiguration {
        name: args.dataset.clone(),
        parameters: IndexParameters {
            timeout: String::from("10s"),
            wait_for_active_shards: String::from("1"), // only the primary shard
        },
        settings: IndexSettings {
            base: serde_json::from_str(include_str!("../../config/admin/settings.json"))
                .expect("invalid JSON file"),
            nb_shards: args.nb_shards,
            nb_replicas: args.nb_replicas,
        },
        mappings: IndexMappings {
            value: String::from(include_str!("../../config/admin/mappings.json")),
        },
    };

    info!("building maps");
    use cosmogony::ZoneType::City;

    let mut cosmogony_id_to_osm_id = BTreeMap::new();
    let max_weight = utils::ADMIN_MAX_WEIGHT;
    for z in read_zones(&args.input)? {
        let insee = match z.zone_type {
            Some(City) => admin::read_insee(&z.tags).map(|s| s.to_owned()),
            _ => None,
        };
        cosmogony_id_to_osm_id.insert(z.id, (z.osm_id.clone(), insee));
    }
    let cosmogony_id_to_osm_id = cosmogony_id_to_osm_id;

    info!("building admins hierarchy");
    let admins_without_boundaries = read_zones(&args.input)?
        .map(|mut zone| {
            zone.boundary = None;
            let admin = zone.into_admin(&cosmogony_id_to_osm_id, &args.langs, max_weight, None);
            (admin.id.clone(), Arc::new(admin))
        })
        .collect::<HashMap<_, _>>();

    info!("importing cosmogony into Mimir");

    let admins = read_zones(&args.input)?.map(move |z| {
        z.into_admin(
            &cosmogony_id_to_osm_id,
            &args.langs,
            max_weight,
            Some(&admins_without_boundaries),
        )
    });
    import_admins(client, config, futures::stream::iter(admins)).await
}

#[derive(StructOpt, Debug)]
struct Args {
    /// cosmogony file
    #[structopt(short = "i", long = "input")]
    input: String,
    /// Elasticsearch parameters.
    #[structopt(
        short = "c",
        long = "connection-string",
        default_value = "http://localhost:9200/munin"
    )]
    connection_string: String,
    /// Name of the dataset.
    #[structopt(short = "d", long = "dataset", default_value = "fr")]
    dataset: String,
    /// Number of shards for the es index
    #[structopt(short = "s", long = "nb-shards", default_value = "1")]
    nb_shards: usize,
    /// Number of replicas for the es index
    #[structopt(short = "r", long = "nb-replicas", default_value = "1")]
    nb_replicas: usize,
    /// Languages codes, used to build i18n names and labels
    #[structopt(name = "lang", short, long)]
    langs: Vec<String>,
}

#[tokio::main]
async fn main() {
    mimirsbrunn::utils::launch_async(index_cosmogony).await;
}
<|endoftext|>


<commit_before>//! Abstraction over blocking and unblocking the current thread.
//!
//! Provides an abstraction over blocking the current thread. This is similar to
//! the park / unpark constructs provided by [`std`] but made generic. This
//! allows embedding custom functionality to perform when the thread is blocked.
//!
//! A blocked [`Park`][p] instance is unblocked by calling [`unpark`] on its
//! [`Unpark`][up] handle.
//!
//! The [`ParkThread`] struct implements [`Park`][p] using
//! [`thread::park`][`std`] to put the thread to sleep. The Tokio reactor also
//! implements park, but uses [`mio::Poll`][mio] to block the thread instead.
//!
//! The [`Park`][p] trait is composable. A timer implementation might decorate a
//! [`Park`][p] implementation by checking if any timeouts have elapsed after
//! the inner [`Park`][p] implementation unblocks.
//!
//! # Model
//!
//! Conceptually, each [`Park`][p] instance has an associated token, which is
//! initially not present:
//!
//! * The [`park`] method blocks the current thread unless or until the token
//!   is available, at which point it atomically consumes the token.
//! * The [`unpark`] method atomically makes the token available if it wasn't
//!   already.
//!
//! Some things to note:
//!
//! * If [`unpark`] is called before [`park`], the next call to [`park`] will
//! **not** block the thread.
//! * **Spurious** wakeups are permitted, i.e., the [`park`] method may unblock
//!   even if [`unpark`] was not called.
//! * [`park_timeout`] does the same as [`park`] but allows specifying a maximum
//!   time to block the thread for.
//!
//! [`std`]: https://doc.rust-lang.org/std/thread/fn.park.html
//! [`thread::park`]: https://doc.rust-lang.org/std/thread/fn.park.html
//! [`ParkThread`]: struct.ParkThread.html
//! [p]: trait.Park.html
//! [`park`]: trait.Park.html#tymethod.park
//! [`park_timeout`]: trait.Park.html#tymethod.park_timeout
//! [`unpark`]: trait.Unpark.html#tymethod.unpark
//! [up]: trait.Unpark.html
//! [mio]: https://docs.rs/mio/0.6/mio/struct.Poll.html

use std::marker::PhantomData;
use std::rc::Rc;
use std::sync::{Arc, Mutex, Condvar};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::time::Duration;

/// Block the current thread.
///
/// See [module documentation][mod] for more details.
///
/// [mod]: ../index.html
pub trait Park {
    /// Unpark handle type for the `Park` implementation.
    type Unpark: Unpark;

    /// Error returned by `park`
    type Error;

    /// Get a new `Unpark` handle associated with this `Park` instance.
    fn unpark(&self) -> Self::Unpark;

    /// Block the current thread unless or until the token is available.
    ///
    /// A call to `park` does not guarantee that the thread will remain blocked
    /// forever, and callers should be prepared for this possibility. This
    /// function may wakeup spuriously for any reason.
    ///
    /// See [module documentation][mod] for more details.
    ///
    /// # Panics
    ///
    /// This function **should** not panic, but ultimately, panics are left as
    /// an implementation detail. Refer to the documentation for the specific
    /// `Park` implementation
    ///
    /// [mod]: ../index.html
    fn park(&mut self) -> Result<(), Self::Error>;

    /// Park the current thread for at most `duration`.
    ///
    /// This function is the same as `park` but allows specifying a maximum time
    /// to block the thread for.
    ///
    /// Same as `park`, there is no guarantee that the thread will remain
    /// blocked for any amount of time. Spurious wakeups are permitted for any
    /// reason.
    ///
    /// See [module documentation][mod] for more details.
    ///
    /// # Panics
    ///
    /// This function **should** not panic, but ultimately, panics are left as
    /// an implementation detail. Refer to the documentation for the specific
    /// `Park` implementation
    ///
    /// [mod]: ../index.html
    fn park_timeout(&mut self, duration: Duration) -> Result<(), Self::Error>;
}

/// Unblock a thread blocked by the associated [`Park`] instance.
///
/// See [module documentation][mod] for more details.
///
/// [mod]: ../index.html
/// [`Park`]: trait.Park.html
pub trait Unpark: Sync + Send + 'static {
    /// Unblock a thread that is blocked by the associated `Park` handle.
    ///
    /// Calling `unpark` atomically makes available the unpark token, if it is
    /// not already available.
    ///
    /// See [module documentation][mod] for more details.
    ///
    /// # Panics
    ///
    /// This function **should** not panic, but ultimately, panics are left as
    /// an implementation detail. Refer to the documentation for the specific
    /// `Unpark` implementation
    ///
    /// [mod]: ../index.html
    fn unpark(&self);
}

impl Unpark for Box<Unpark> {
    fn unpark(&self) {
        (**self).unpark()
    }
}

/// Blocks the current thread using a condition variable.
///
/// Implements the [`Park`] functionality by using a condition variable. An
/// atomic variable is also used to avoid using the condition variable if
/// possible.
///
/// The condition variable is cached in a thread-local variable and is shared
/// across all `ParkThread` instances created on the same thread. This also
/// means that an instance of `ParkThread` might be unblocked by a handle
/// associated with a different `ParkThread` instance.
#[derive(Debug)]
pub struct ParkThread {
    _anchor: PhantomData<Rc<()>>,
}

/// Error returned by [`ParkThread`]
///
/// This currently is never returned, but might at some point in the future.
///
/// [`ParkThread`]: struct.ParkThread.html
#[derive(Debug)]
pub struct ParkError {
    _p: (),
}

/// Unblocks a thread that was blocked by `ParkThread`.
#[derive(Clone, Debug)]
pub struct UnparkThread {
    inner: Arc<Inner>,
}

#[derive(Debug)]
struct Inner {
    state: AtomicUsize,
    mutex: Mutex<()>,
    condvar: Condvar,
}

const IDLE: usize = 0;
const NOTIFY: usize = 1;
const SLEEP: usize = 2;

thread_local! {
    static CURRENT_PARK_THREAD: Arc<Inner> = Arc::new(Inner {
        state: AtomicUsize::new(IDLE),
        mutex: Mutex::new(()),
        condvar: Condvar::new(),
    });
}

// ===== impl ParkThread =====

impl ParkThread {
    /// Create a new `ParkThread` handle for the current thread.
    ///
    /// This type cannot be moved to other threads, so it should be created on
    /// the thread that the caller intends to park.
    pub fn new() -> ParkThread {
        ParkThread {
            _anchor: PhantomData,
        }
    }

    /// Get a reference to the `ParkThread` handle for this thread.
    fn with_current<F, R>(&self, f: F) -> R
        where F: FnOnce(&Arc<Inner>) -> R,
    {
        CURRENT_PARK_THREAD.with(|inner| f(inner))
    }
}

impl Park for ParkThread {
    type Unpark = UnparkThread;
    type Error = ParkError;

    fn unpark(&self) -> Self::Unpark {
        let inner = self.with_current(|inner| inner.clone());
        UnparkThread { inner }
    }

    fn park(&mut self) -> Result<(), Self::Error> {
        self.with_current(|inner| inner.park(None))
    }

    fn park_timeout(&mut self, duration: Duration) -> Result<(), Self::Error> {
        self.with_current(|inner| inner.park(Some(duration)))
    }
}

// ===== impl UnparkThread =====

impl Unpark for UnparkThread {
    fn unpark(&self) {
        self.inner.unpark();
    }
}

// ===== impl Inner =====

impl Inner {
    /// Park the current thread for at most `dur`.
    fn park(&self, timeout: Option<Duration>) -> Result<(), ParkError> {
        // If currently notified, then we skip sleeping. This is checked outside
        // of the lock to avoid acquiring a mutex if not necessary.
        match self.state.compare_and_swap(NOTIFY, IDLE, Ordering::SeqCst) {
            NOTIFY => return Ok(()),
            IDLE => {},
            _ => unreachable!(),
        }

        // The state is currently idle, so obtain the lock and then try to
        // transition to a sleeping state.
        let mut m = self.mutex.lock().unwrap();

        // Transition to sleeping
        match self.state.compare_and_swap(IDLE, SLEEP, Ordering::SeqCst) {
            NOTIFY => {
                // Notified before we could sleep, consume the notification and
                // exit
                self.state.store(IDLE, Ordering::SeqCst);
                return Ok(());
            }
            IDLE => {},
            _ => unreachable!(),
        }

        m = match timeout {
            Some(timeout) => self.condvar.wait_timeout(m, timeout).unwrap().0,
            None => self.condvar.wait(m).unwrap(),
        };

        // Transition back to idle. If the state has transitioned to `NOTIFY`,
        // this will consume that notification
        self.state.store(IDLE, Ordering::SeqCst);

        // Explicitly drop the mutex guard. There is no real point in doing it
        // except that I find it helpful to make it explicit where we want the
        // mutex to unlock.
        drop(m);

        Ok(())
    }

    fn unpark(&self) {
        // First, try transitioning from IDLE -> NOTIFY, this does not require a
        // lock.
        match self.state.compare_and_swap(IDLE, NOTIFY, Ordering::SeqCst) {
            IDLE | NOTIFY => return,
            SLEEP => {}
            _ => unreachable!(),
        }

        // The other half is sleeping, this requires a lock
        let _m = self.mutex.lock().unwrap();

        // Transition from SLEEP -> NOTIFY
        match self.state.compare_and_swap(SLEEP, NOTIFY, Ordering::SeqCst) {
            SLEEP => {}
            _ => return,
        }

        // Wakeup the sleeper
        self.condvar.notify_one();
    }
}
<commit_msg>Fix a race in thread wakeup (#507)<commit_after>//! Abstraction over blocking and unblocking the current thread.
//!
//! Provides an abstraction over blocking the current thread. This is similar to
//! the park / unpark constructs provided by [`std`] but made generic. This
//! allows embedding custom functionality to perform when the thread is blocked.
//!
//! A blocked [`Park`][p] instance is unblocked by calling [`unpark`] on its
//! [`Unpark`][up] handle.
//!
//! The [`ParkThread`] struct implements [`Park`][p] using
//! [`thread::park`][`std`] to put the thread to sleep. The Tokio reactor also
//! implements park, but uses [`mio::Poll`][mio] to block the thread instead.
//!
//! The [`Park`][p] trait is composable. A timer implementation might decorate a
//! [`Park`][p] implementation by checking if any timeouts have elapsed after
//! the inner [`Park`][p] implementation unblocks.
//!
//! # Model
//!
//! Conceptually, each [`Park`][p] instance has an associated token, which is
//! initially not present:
//!
//! * The [`park`] method blocks the current thread unless or until the token
//!   is available, at which point it atomically consumes the token.
//! * The [`unpark`] method atomically makes the token available if it wasn't
//!   already.
//!
//! Some things to note:
//!
//! * If [`unpark`] is called before [`park`], the next call to [`park`] will
//! **not** block the thread.
//! * **Spurious** wakeups are permitted, i.e., the [`park`] method may unblock
//!   even if [`unpark`] was not called.
//! * [`park_timeout`] does the same as [`park`] but allows specifying a maximum
//!   time to block the thread for.
//!
//! [`std`]: https://doc.rust-lang.org/std/thread/fn.park.html
//! [`thread::park`]: https://doc.rust-lang.org/std/thread/fn.park.html
//! [`ParkThread`]: struct.ParkThread.html
//! [p]: trait.Park.html
//! [`park`]: trait.Park.html#tymethod.park
//! [`park_timeout`]: trait.Park.html#tymethod.park_timeout
//! [`unpark`]: trait.Unpark.html#tymethod.unpark
//! [up]: trait.Unpark.html
//! [mio]: https://docs.rs/mio/0.6/mio/struct.Poll.html

use std::marker::PhantomData;
use std::rc::Rc;
use std::sync::{Arc, Mutex, Condvar};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::time::Duration;

/// Block the current thread.
///
/// See [module documentation][mod] for more details.
///
/// [mod]: ../index.html
pub trait Park {
    /// Unpark handle type for the `Park` implementation.
    type Unpark: Unpark;

    /// Error returned by `park`
    type Error;

    /// Get a new `Unpark` handle associated with this `Park` instance.
    fn unpark(&self) -> Self::Unpark;

    /// Block the current thread unless or until the token is available.
    ///
    /// A call to `park` does not guarantee that the thread will remain blocked
    /// forever, and callers should be prepared for this possibility. This
    /// function may wakeup spuriously for any reason.
    ///
    /// See [module documentation][mod] for more details.
    ///
    /// # Panics
    ///
    /// This function **should** not panic, but ultimately, panics are left as
    /// an implementation detail. Refer to the documentation for the specific
    /// `Park` implementation
    ///
    /// [mod]: ../index.html
    fn park(&mut self) -> Result<(), Self::Error>;

    /// Park the current thread for at most `duration`.
    ///
    /// This function is the same as `park` but allows specifying a maximum time
    /// to block the thread for.
    ///
    /// Same as `park`, there is no guarantee that the thread will remain
    /// blocked for any amount of time. Spurious wakeups are permitted for any
    /// reason.
    ///
    /// See [module documentation][mod] for more details.
    ///
    /// # Panics
    ///
    /// This function **should** not panic, but ultimately, panics are left as
    /// an implementation detail. Refer to the documentation for the specific
    /// `Park` implementation
    ///
    /// [mod]: ../index.html
    fn park_timeout(&mut self, duration: Duration) -> Result<(), Self::Error>;
}

/// Unblock a thread blocked by the associated [`Park`] instance.
///
/// See [module documentation][mod] for more details.
///
/// [mod]: ../index.html
/// [`Park`]: trait.Park.html
pub trait Unpark: Sync + Send + 'static {
    /// Unblock a thread that is blocked by the associated `Park` handle.
    ///
    /// Calling `unpark` atomically makes available the unpark token, if it is
    /// not already available.
    ///
    /// See [module documentation][mod] for more details.
    ///
    /// # Panics
    ///
    /// This function **should** not panic, but ultimately, panics are left as
    /// an implementation detail. Refer to the documentation for the specific
    /// `Unpark` implementation
    ///
    /// [mod]: ../index.html
    fn unpark(&self);
}

impl Unpark for Box<Unpark> {
    fn unpark(&self) {
        (**self).unpark()
    }
}

/// Blocks the current thread using a condition variable.
///
/// Implements the [`Park`] functionality by using a condition variable. An
/// atomic variable is also used to avoid using the condition variable if
/// possible.
///
/// The condition variable is cached in a thread-local variable and is shared
/// across all `ParkThread` instances created on the same thread. This also
/// means that an instance of `ParkThread` might be unblocked by a handle
/// associated with a different `ParkThread` instance.
#[derive(Debug)]
pub struct ParkThread {
    _anchor: PhantomData<Rc<()>>,
}

/// Error returned by [`ParkThread`]
///
/// This currently is never returned, but might at some point in the future.
///
/// [`ParkThread`]: struct.ParkThread.html
#[derive(Debug)]
pub struct ParkError {
    _p: (),
}

/// Unblocks a thread that was blocked by `ParkThread`.
#[derive(Clone, Debug)]
pub struct UnparkThread {
    inner: Arc<Inner>,
}

#[derive(Debug)]
struct Inner {
    state: AtomicUsize,
    mutex: Mutex<()>,
    condvar: Condvar,
}

const IDLE: usize = 0;
const NOTIFY: usize = 1;
const SLEEP: usize = 2;

thread_local! {
    static CURRENT_PARK_THREAD: Arc<Inner> = Arc::new(Inner {
        state: AtomicUsize::new(IDLE),
        mutex: Mutex::new(()),
        condvar: Condvar::new(),
    });
}

// ===== impl ParkThread =====

impl ParkThread {
    /// Create a new `ParkThread` handle for the current thread.
    ///
    /// This type cannot be moved to other threads, so it should be created on
    /// the thread that the caller intends to park.
    pub fn new() -> ParkThread {
        ParkThread {
            _anchor: PhantomData,
        }
    }

    /// Get a reference to the `ParkThread` handle for this thread.
    fn with_current<F, R>(&self, f: F) -> R
        where F: FnOnce(&Arc<Inner>) -> R,
    {
        CURRENT_PARK_THREAD.with(|inner| f(inner))
    }
}

impl Park for ParkThread {
    type Unpark = UnparkThread;
    type Error = ParkError;

    fn unpark(&self) -> Self::Unpark {
        let inner = self.with_current(|inner| inner.clone());
        UnparkThread { inner }
    }

    fn park(&mut self) -> Result<(), Self::Error> {
        self.with_current(|inner| inner.park(None))
    }

    fn park_timeout(&mut self, duration: Duration) -> Result<(), Self::Error> {
        self.with_current(|inner| inner.park(Some(duration)))
    }
}

// ===== impl UnparkThread =====

impl Unpark for UnparkThread {
    fn unpark(&self) {
        self.inner.unpark();
    }
}

// ===== impl Inner =====

impl Inner {
    /// Park the current thread for at most `dur`.
    fn park(&self, timeout: Option<Duration>) -> Result<(), ParkError> {
        // If currently notified, then we skip sleeping. This is checked outside
        // of the lock to avoid acquiring a mutex if not necessary.
        match self.state.compare_and_swap(NOTIFY, IDLE, Ordering::SeqCst) {
            NOTIFY => return Ok(()),
            IDLE => {},
            _ => unreachable!(),
        }

        // The state is currently idle, so obtain the lock and then try to
        // transition to a sleeping state.
        let mut m = self.mutex.lock().unwrap();

        // Transition to sleeping
        match self.state.compare_and_swap(IDLE, SLEEP, Ordering::SeqCst) {
            NOTIFY => {
                // Notified before we could sleep, consume the notification and
                // exit
                self.state.store(IDLE, Ordering::SeqCst);
                return Ok(());
            }
            IDLE => {},
            _ => unreachable!(),
        }

        m = match timeout {
            Some(timeout) => self.condvar.wait_timeout(m, timeout).unwrap().0,
            None => self.condvar.wait(m).unwrap(),
        };

        // Transition back to idle. If the state has transitioned to `NOTIFY`,
        // this will consume that notification
        self.state.store(IDLE, Ordering::SeqCst);

        // Explicitly drop the mutex guard. There is no real point in doing it
        // except that I find it helpful to make it explicit where we want the
        // mutex to unlock.
        drop(m);

        Ok(())
    }

    fn unpark(&self) {
        // First, try transitioning from IDLE -> NOTIFY, this does not require a
        // lock.
        match self.state.compare_and_swap(IDLE, NOTIFY, Ordering::SeqCst) {
            IDLE | NOTIFY => return,
            SLEEP => {}
            _ => unreachable!(),
        }

        // The other half is sleeping, this requires a lock
        let _m = self.mutex.lock().unwrap();

        // Transition to NOTIFY
        match self.state.swap(NOTIFY, Ordering::SeqCst) {
            SLEEP => {}
            NOTIFY => return,
            IDLE => return,
            _ => unreachable!(),
        }

        // Wakeup the sleeper
        self.condvar.notify_one();
    }
}
<|endoftext|>


<commit_before>use crate::future::poll_fn;
use crate::time::{delay_until, Delay, Duration, Instant};

use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};

/// Creates new `Interval` that yields with interval of `duration`. The first
/// tick completes immediately.
///
/// An interval will tick indefinitely. At any time, the `Interval` value can be
/// dropped. This cancels the interval.
///
/// This function is equivalent to `interval_at(Instant::now(), period)`.
///
/// # Panics
///
/// This function panics if `period` is zero.
///
/// # Examples
///
/// ```
/// use tokio::time::{self, Duration};
///
/// #[tokio::main]
/// async fn main() {
///     let mut interval = time::interval(Duration::from_millis(10));
///
///     interval.tick().await;
///     interval.tick().await;
///     interval.tick().await;
///
///     // approximately 20ms have elapsed.
/// }
/// ```
///
/// A simple example using `interval` to execute a task every two seconds.
///
/// The difference between `interval` and [`delay_for`] is that an `interval`
/// measures the time since the last tick, which means that `.tick().await`
/// may wait for a shorter time than the duration specified for the interval
/// if some time has passed between calls to `.tick().await`.
///
/// If the tick in the example below was replaced with [`delay_for`], the task
/// would only be executed once every three seconds, and not every two
/// seconds.
///
/// ```
/// use tokio::time;
///
/// async fn task_that_takes_a_second() {
///     println!("hello");
///     time::delay_for(time::Duration::from_secs(1)).await
/// }
///
/// #[tokio::main]
/// async fn main() {
///     let mut interval = time::interval(time::Duration::from_secs(2));
///     for _i in 0..5 {
///         interval.tick().await;
///         task_that_takes_a_second().await;
///     }
/// }
/// ```
///
/// [`delay_for`]: crate::time::delay_for()
pub fn interval(period: Duration) -> Interval {
    assert!(period > Duration::new(0, 0), "`period` must be non-zero.");

    interval_at(Instant::now(), period)
}

/// Creates new `Interval` that yields with interval of `period` with the
/// first tick completing at `start`.
///
/// An interval will tick indefinitely. At any time, the `Interval` value can be
/// dropped. This cancels the interval.
///
/// # Panics
///
/// This function panics if `period` is zero.
///
/// # Examples
///
/// ```
/// use tokio::time::{interval_at, Duration, Instant};
///
/// #[tokio::main]
/// async fn main() {
///     let start = Instant::now() + Duration::from_millis(50);
///     let mut interval = interval_at(start, Duration::from_millis(10));
///
///     interval.tick().await;
///     interval.tick().await;
///     interval.tick().await;
///
///     // approximately 70ms have elapsed.
/// }
/// ```
pub fn interval_at(start: Instant, period: Duration) -> Interval {
    assert!(period > Duration::new(0, 0), "`period` must be non-zero.");

    Interval {
        delay: delay_until(start),
        period,
    }
}

/// Stream returned by [`interval`](interval) and [`interval_at`](interval_at).
///
/// This type only implements the [`Stream`] trait if the "stream" feature is
/// enabled.
///
/// [`Stream`]: trait@crate::stream::Stream
#[derive(Debug)]
pub struct Interval {
    /// Future that completes the next time the `Interval` yields a value.
    delay: Delay,

    /// The duration between values yielded by `Interval`.
    period: Duration,
}

impl Interval {
    #[doc(hidden)] // TODO: document
    pub fn poll_tick(&mut self, cx: &mut Context<'_>) -> Poll<Instant> {
        // Wait for the delay to be done
        ready!(Pin::new(&mut self.delay).poll(cx));

        // Get the `now` by looking at the `delay` deadline
        let now = self.delay.deadline();

        // The next interval value is `duration` after the one that just
        // yielded.
        let next = now + self.period;
        self.delay.reset(next);

        // Return the current instant
        Poll::Ready(now)
    }

    /// Completes when the next instant in the interval has been reached.
    ///
    /// # Examples
    ///
    /// ```
    /// use tokio::time;
    ///
    /// use std::time::Duration;
    ///
    /// #[tokio::main]
    /// async fn main() {
    ///     let mut interval = time::interval(Duration::from_millis(10));
    ///
    ///     interval.tick().await;
    ///     interval.tick().await;
    ///     interval.tick().await;
    ///
    ///     // approximately 20ms have elapsed.
    /// }
    /// ```
    #[allow(clippy::should_implement_trait)] // TODO: rename (tokio-rs/tokio#1261)
    pub async fn tick(&mut self) -> Instant {
        poll_fn(|cx| self.poll_tick(cx)).await
    }
}

#[cfg(feature = "stream")]
impl crate::stream::Stream for Interval {
    type Item = Instant;

    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Instant>> {
        Poll::Ready(Some(ready!(self.poll_tick(cx))))
    }
}
<commit_msg>time: remove outdated todo comment (#2848)<commit_after>use crate::future::poll_fn;
use crate::time::{delay_until, Delay, Duration, Instant};

use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};

/// Creates new `Interval` that yields with interval of `duration`. The first
/// tick completes immediately.
///
/// An interval will tick indefinitely. At any time, the `Interval` value can be
/// dropped. This cancels the interval.
///
/// This function is equivalent to `interval_at(Instant::now(), period)`.
///
/// # Panics
///
/// This function panics if `period` is zero.
///
/// # Examples
///
/// ```
/// use tokio::time::{self, Duration};
///
/// #[tokio::main]
/// async fn main() {
///     let mut interval = time::interval(Duration::from_millis(10));
///
///     interval.tick().await;
///     interval.tick().await;
///     interval.tick().await;
///
///     // approximately 20ms have elapsed.
/// }
/// ```
///
/// A simple example using `interval` to execute a task every two seconds.
///
/// The difference between `interval` and [`delay_for`] is that an `interval`
/// measures the time since the last tick, which means that `.tick().await`
/// may wait for a shorter time than the duration specified for the interval
/// if some time has passed between calls to `.tick().await`.
///
/// If the tick in the example below was replaced with [`delay_for`], the task
/// would only be executed once every three seconds, and not every two
/// seconds.
///
/// ```
/// use tokio::time;
///
/// async fn task_that_takes_a_second() {
///     println!("hello");
///     time::delay_for(time::Duration::from_secs(1)).await
/// }
///
/// #[tokio::main]
/// async fn main() {
///     let mut interval = time::interval(time::Duration::from_secs(2));
///     for _i in 0..5 {
///         interval.tick().await;
///         task_that_takes_a_second().await;
///     }
/// }
/// ```
///
/// [`delay_for`]: crate::time::delay_for()
pub fn interval(period: Duration) -> Interval {
    assert!(period > Duration::new(0, 0), "`period` must be non-zero.");

    interval_at(Instant::now(), period)
}

/// Creates new `Interval` that yields with interval of `period` with the
/// first tick completing at `start`.
///
/// An interval will tick indefinitely. At any time, the `Interval` value can be
/// dropped. This cancels the interval.
///
/// # Panics
///
/// This function panics if `period` is zero.
///
/// # Examples
///
/// ```
/// use tokio::time::{interval_at, Duration, Instant};
///
/// #[tokio::main]
/// async fn main() {
///     let start = Instant::now() + Duration::from_millis(50);
///     let mut interval = interval_at(start, Duration::from_millis(10));
///
///     interval.tick().await;
///     interval.tick().await;
///     interval.tick().await;
///
///     // approximately 70ms have elapsed.
/// }
/// ```
pub fn interval_at(start: Instant, period: Duration) -> Interval {
    assert!(period > Duration::new(0, 0), "`period` must be non-zero.");

    Interval {
        delay: delay_until(start),
        period,
    }
}

/// Stream returned by [`interval`](interval) and [`interval_at`](interval_at).
///
/// This type only implements the [`Stream`] trait if the "stream" feature is
/// enabled.
///
/// [`Stream`]: trait@crate::stream::Stream
#[derive(Debug)]
pub struct Interval {
    /// Future that completes the next time the `Interval` yields a value.
    delay: Delay,

    /// The duration between values yielded by `Interval`.
    period: Duration,
}

impl Interval {
    #[doc(hidden)] // TODO: document
    pub fn poll_tick(&mut self, cx: &mut Context<'_>) -> Poll<Instant> {
        // Wait for the delay to be done
        ready!(Pin::new(&mut self.delay).poll(cx));

        // Get the `now` by looking at the `delay` deadline
        let now = self.delay.deadline();

        // The next interval value is `duration` after the one that just
        // yielded.
        let next = now + self.period;
        self.delay.reset(next);

        // Return the current instant
        Poll::Ready(now)
    }

    /// Completes when the next instant in the interval has been reached.
    ///
    /// # Examples
    ///
    /// ```
    /// use tokio::time;
    ///
    /// use std::time::Duration;
    ///
    /// #[tokio::main]
    /// async fn main() {
    ///     let mut interval = time::interval(Duration::from_millis(10));
    ///
    ///     interval.tick().await;
    ///     interval.tick().await;
    ///     interval.tick().await;
    ///
    ///     // approximately 20ms have elapsed.
    /// }
    /// ```
    pub async fn tick(&mut self) -> Instant {
        poll_fn(|cx| self.poll_tick(cx)).await
    }
}

#[cfg(feature = "stream")]
impl crate::stream::Stream for Interval {
    type Item = Instant;

    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Instant>> {
        Poll::Ready(Some(ready!(self.poll_tick(cx))))
    }
}
<|endoftext|>


<commit_before>// likes snakeCase naming
fn using_let(some_var: i32) {
    // let expression is a pattern rather than being a variable
    let a = multiply(2,5);
    let (b,c) = (1,2); //inferential static type system
    let d : i32 = 3; //though can specify type if required
    // by default bindings are immutable so now can't do
    // d = 33;
    let mut e = 4; // need to ask for mutable association
    e = some_var + e;
    // will give warning if ```let x i32``` but never initialized and used
    // gives error if never initialized but used

    println!("{} {} {} {} {}", a, b, c, d, e)
    // formatting rules that can be used: http://doc.rust-lang.org/std/fmt/
}

fn multiply(x: i32, y: i32) -> i32 {
    // return x*y; // could have been used but poor style; only good for early returns
    x * y //here a semicolon will give error
}

/*
Rust is an expression based language with just two types of statements. Declaration and Expression statements.
`let` can only begin a statement not an expression.
let x = (let y = 5); // ERRONEOUS

Here value of an assignment is an empty tuple as assigned value can have just one owner.
let mut y = 5;
let x = (y = 7); //makes y =7 and x =()

Expression statement turns any expression to a statement. Semicolon separated expressions.
*/

fn oops() -> ! {
/*
   its return type is ! (called 'diverges')
   cuz it will crash and never return hence is a diverging function
*/
    panic!("oye bye")
}

fn primitive_types(){
    let sach = true
    let jhoot: bool = false

    let chr = 'c'
    let unicode_chr = '💕'
}

fn main() {
    println!("Hello, Rust!");

    using_let(40);

    primitive_types();

    // let xyz: String = oops(); // a diverging function can be used as any type
    oops();
}
<commit_msg>[misc:rust] rusty till primitive types<commit_after>// likes snakeCase naming
fn using_let(some_var: i32) {
    // let expression is a pattern rather than being a variable
    let a = multiply(2,5);
    let (b,c) = (1,2); //inferential static type system
    let d : i32 = 3; //though can specify type if required
    // by default bindings are immutable so now can't do
    // d = 33;
    let mut e = 4; // need to ask for mutable association
    e = some_var + e;
    // will give warning if ```let x i32``` but never initialized and used
    // gives error if never initialized but used

    println!("{} {} {} {} {}", a, b, c, d, e)
    // formatting rules that can be used: http://doc.rust-lang.org/std/fmt/
}

fn multiply(x: i32, y: i32) -> i32 {
    // return x*y; // could have been used but poor style; only good for early returns
    x * y //here a semicolon will give error
}

/*
Rust is an expression based language with just two types of statements. Declaration and Expression statements.
`let` can only begin a statement not an expression.
let x = (let y = 5); // ERRONEOUS

Here value of an assignment is an empty tuple as assigned value can have just one owner.
let mut y = 5;
let x = (y = 7); //makes y =7 and x =()

Expression statement turns any expression to a statement. Semicolon separated expressions.
*/

fn oops() -> ! {
/*
   its return type is ! (called 'diverges')
   cuz it will crash and never return hence is a diverging function
*/
    panic!("oye bye")
}

fn primitive_types() -> fn(i32) -> i32 {
    let sach = true;
    let jhoot: bool = false;

    let chr = 'c';
    let unicode_chr = '💕';
    println!("{} {} {} {}", sach, jhoot, chr, unicode_chr);

    //arrays
    let arr = [1, 10, 10000, 1000, 100];
    let all = [7; 20]; // a: [i32; 20] //20 elements, all init to 7
    println!("{}\n{} {}", all.len(), all[0], all[19]); //doesn't interpolate array
    for x in &all {
        print!("{}, ", x);
    }

    //slice
    let middle_arr = &arr[1..3];
    let full_arr = &arr[..];
    println!("len {} :@0 {}\nlen {} :@0 {}", middle_arr.len(), middle_arr[0], full_arr.len(), full_arr[0]);
    // coercing an array to a slice
    let str_slice: &[&str] = &["one", "two", "three"];
    // above were shared slice, mutable slice is following
    let mut_slice = &mut ["oh", "ok"];
    mut_slice[1] = "no";
    println!("len {} :@1 {}\nlen {} :@1 {}", str_slice.len(), str_slice[1], mut_slice.len(), mut_slice[1]);
    print!("window of 2: ");
    for x in str_slice.windows(2) {
        print!("{:?}, ", x);
    }
    print!("\nchunk of 2: ");
    for x in str_slice.chunks(2) {
        print!("{:?}, ", x);
    }

    let tada = "tada";
    let nada: &str = "nada";
    println!("\n{} <> {}", tada, nada);

    //tuples
    let tupl01 = (1, "ohk");
    let tupl02: (i32, &str) = (1, "oh ok");
    let tupl03: (i32, &str) = (1, "ohk");
    if tupl01 == tupl02 {
      println!("don't compare it")
    } else if tupl01 == tupl03 {
      println!("just compare it; tuple indexes {}, {}", tupl01.0, tupl03.1)
    }

    fn some_foo(x: i32) -> i32 { x+1 }
    return some_foo
}

/// * rustdoc for main
fn main() {
    // rust got 2 types of comments: line-comment and doc-comment
    // this is line-comment; doc-comment got /// or //! and supports Markdowno

    //! these **//!** are used for non-method rustdoc
    //! * rustdoc can be used to generate document from doc-comment
    //! usage:
    //! ```
    //! rustdoc -w html -o a.html src/main.rs
    //! ```

    println!("Hello, Rust!");

    using_let(40);

    let some_fn: fn(i32) -> i32 = primitive_types(); //function primitive and others
    println!("increment 1 to {}", some_fn(1));

    // let xyz: String = oops(); // a diverging function can be used as any type
    oops();
}
<|endoftext|>


<commit_before>#![deny(
    anonymous_parameters,
    clippy::all,
    const_err,
    illegal_floating_point_literal_pattern,
    late_bound_lifetime_arguments,
    path_statements,
    patterns_in_fns_without_body,
    rust_2018_idioms,
    trivial_casts,
    trivial_numeric_casts,
    unreachable_pub,
    unsafe_code,
    unused_extern_crates
)]
#![warn(
    clippy::dbg_macro,
    clippy::decimal_literal_representation,
    clippy::get_unwrap,
    clippy::nursery,
    clippy::print_stdout,
    clippy::todo,
    clippy::unimplemented,
    clippy::unnested_or_patterns,
    clippy::unwrap_used,
    clippy::use_debug,
    single_use_lifetimes,
    unused_qualifications,
    variant_size_differences
)]
#![allow(clippy::missing_const_for_fn, clippy::redundant_pub_crate)]
#![recursion_limit = "256"]

#[macro_use]
mod quote;

mod date;
mod datetime;
mod error;
mod format_description;
mod helpers;
mod offset;
mod time;
mod to_tokens;

use std::iter::FromIterator;

use proc_macro::{TokenStream, TokenTree};

use self::error::Error;

macro_rules! impl_macros {
    ($($name:ident)*) => {$(
        #[proc_macro]
        pub fn $name(input: TokenStream) -> TokenStream {
            use crate::to_tokens::ToTokens;

            let mut iter = input.into_iter().peekable();
            match $name::parse(&mut iter) {
                Ok(value) => match iter.peek() {
                    Some(tree) => Error::UnexpectedToken { tree: tree.clone() }.to_compile_error(),
                    None => value.into_token_stream(),
                },
                Err(err) => err.to_compile_error(),
            }
        }
    )*};
}

impl_macros![date datetime offset time];

// TODO Gate this behind the the `formatting` or `parsing` feature flag when weak dependency
// features land.
#[proc_macro]
pub fn format_description(input: TokenStream) -> TokenStream {
    let (span, string) = match helpers::get_string_literal(input) {
        Ok(val) => val,
        Err(err) => return err.to_compile_error(),
    };

    let items = match format_description::parse(&string, span) {
        Ok(items) => items,
        Err(err) => return err.to_compile_error(),
    };

    quote! {{
        const DESCRIPTION: &[::time::format_description::FormatItem<'_>] = &[#(
            items
                .into_iter()
                .map(|item| quote! { #(item), })
                .collect::<TokenStream>()
        )];
        DESCRIPTION
    }}
}

fn make_serde_serializer_module(
    mod_name: proc_macro::Ident,
    items: impl to_tokens::ToTokens,
    formattable: TokenStream,
    format_string: &str,
) -> TokenStream {
    let visitor_struct = quote! {
        struct Visitor<T: ?Sized>(::core::marker::PhantomData<T>);

        impl<'a> ::serde::de::Visitor<'a> for Visitor<#(formattable.clone())> {
            type Value = #(formattable.clone());

            fn expecting(&self, formatter: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
                // `write!` macro confuses `quote!` so format our message manually
                formatter.write_str("a(n) `")?;
                formatter.write_str(#(formattable.to_string()))?;
                formatter.write_str("` in the format \"")?;
                formatter.write_str(&FORMAT_STRING)?;
                formatter.write_str("\"")
            }

            fn visit_str<E: ::serde::de::Error>(
                self,
                value: &str
            ) -> Result<Self::Value, E> {
                #(formattable.clone())::parse(value, &DESCRIPTION).map_err(E::custom)
            }
        }

        impl<'a> ::serde::de::Visitor<'a> for Visitor<Option<#(formattable.clone())>> {
            type Value = Option<#(formattable.clone())>;

            fn expecting(&self, formatter: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
                // `write!` macro confuses `quote!` so format our message manually
                formatter.write_str("an `Option<")?;
                formatter.write_str(#(formattable.to_string()))?;
                formatter.write_str(">` in the format \"")?;
                formatter.write_str(&FORMAT_STRING)?;
                formatter.write_str("\"")
            }

            fn visit_some<D: ::serde::de::Deserializer<'a>>(
                self,
                deserializer: D
            ) -> Result<Self::Value, D::Error> {
                let visitor = Visitor::<#(formattable.clone())>(::core::marker::PhantomData);
                deserializer
                    .deserialize_any(visitor)
                    .map(Some)
            }

            fn visit_none<E: ::serde::de::Error>(
                self
            ) -> Result<Option<#(formattable.clone())>, E> {
                Ok(None)
            }
        }

    };
    let serialize_fns = quote! {
        pub fn serialize<S: ::serde::Serializer>(
            datetime: &#(formattable.clone()),
            serializer: S,
        ) -> Result<S::Ok, S::Error> {
            use ::serde::Serialize;
            datetime
                .format(&DESCRIPTION)
                .map_err(::time::error::Format::into_invalid_serde_value::<S>)?
                .serialize(serializer)
        }

        pub fn deserialize<'a, D: ::serde::Deserializer<'a>>(
            deserializer: D
        ) -> Result<#(formattable.clone()), D::Error> {
            use ::serde::Deserialize;
            let visitor = Visitor::<#(formattable.clone())>(::core::marker::PhantomData);
            deserializer.deserialize_any(visitor)
        }
    };
    let option_serialize_fns = quote! {
        pub fn serialize<S: ::serde::Serializer>(
            option: &Option<#(formattable.clone())>,
            serializer: S,
        ) -> Result<S::Ok, S::Error> {
            use ::serde::Serialize;
            option.map(|datetime| datetime.format(&DESCRIPTION))
                    .transpose()
                    .map_err(::time::error::Format::into_invalid_serde_value::<S>)?
                    .serialize(serializer)
        }

        pub fn deserialize<'a, D: ::serde::Deserializer<'a>>(
            deserializer: D
        ) -> Result<Option<#(formattable.clone())>, D::Error> {
            use ::serde::Deserialize;
            let visitor = Visitor::<Option<#(formattable.clone())>>(::core::marker::PhantomData);
            deserializer.deserialize_option(visitor)
        }
    };

    quote! {mod #(mod_name) {
            use ::time::#(formattable.clone());

            const DESCRIPTION: &[::time::format_description::FormatItem<'_>] = &[#(items)];
            const FORMAT_STRING: &str = #(format_string);

            #(visitor_struct)

            #(serialize_fns)

            pub mod option {
                use super::{DESCRIPTION, #(formattable), Visitor};

                #(option_serialize_fns)
            }
        }
    }
}

#[proc_macro]
pub fn declare_format_string(input: TokenStream) -> TokenStream {
    let mut tokens = input.into_iter().peekable();
    // First, an identifier (the desired module name)
    let mod_name = match tokens.next() {
        Some(TokenTree::Ident(ident)) => ident,
        Some(tree) => {
            return Error::UnexpectedToken { tree }.to_compile_error_standalone();
        }
        None => return Error::UnexpectedEndOfInput.to_compile_error_standalone(),
    };
    // Followed by a comma
    if let Err(err) = helpers::consume_punct(',', &mut tokens) {
        return err.to_compile_error_standalone();
    }
    // Then, the type to create serde serializers for (e.g., `OffsetDateTime`).
    let formattable = match tokens.next() {
        Some(tree @ TokenTree::Ident(_)) => tree,
        Some(tree) => return Error::UnexpectedToken { tree }.to_compile_error_standalone(),
        None => return Error::UnexpectedEndOfInput.to_compile_error_standalone(),
    };
    // Another comma
    if let Err(err) = helpers::consume_punct(',', &mut tokens) {
        return err.to_compile_error_standalone();
    }
    // Then, a string literal.
    let input = TokenStream::from_iter(tokens);
    let (span, format_string) = match helpers::get_string_literal(input) {
        Ok(val) => val,
        Err(err) => return err.to_compile_error_standalone(),
    };

    let items = match format_description::parse(&format_string, span) {
        Ok(items) => items,
        Err(err) => return err.to_compile_error_standalone(),
    };
    let items: TokenStream = items.into_iter().map(|item| quote! { #(item), }).collect();

    make_serde_serializer_module(
        mod_name,
        items,
        formattable.into(),
        std::str::from_utf8(&format_string).unwrap(),
    )
}
<commit_msg>Combine error handling<commit_after>#![deny(
    anonymous_parameters,
    clippy::all,
    const_err,
    illegal_floating_point_literal_pattern,
    late_bound_lifetime_arguments,
    path_statements,
    patterns_in_fns_without_body,
    rust_2018_idioms,
    trivial_casts,
    trivial_numeric_casts,
    unreachable_pub,
    unsafe_code,
    unused_extern_crates
)]
#![warn(
    clippy::dbg_macro,
    clippy::decimal_literal_representation,
    clippy::get_unwrap,
    clippy::nursery,
    clippy::print_stdout,
    clippy::todo,
    clippy::unimplemented,
    clippy::unnested_or_patterns,
    clippy::unwrap_used,
    clippy::use_debug,
    single_use_lifetimes,
    unused_qualifications,
    variant_size_differences
)]
#![allow(clippy::missing_const_for_fn, clippy::redundant_pub_crate)]
#![recursion_limit = "256"]

#[macro_use]
mod quote;

mod date;
mod datetime;
mod error;
mod format_description;
mod helpers;
mod offset;
mod time;
mod to_tokens;

use std::iter::FromIterator;

use proc_macro::{TokenStream, TokenTree};

use self::error::Error;

macro_rules! impl_macros {
    ($($name:ident)*) => {$(
        #[proc_macro]
        pub fn $name(input: TokenStream) -> TokenStream {
            use crate::to_tokens::ToTokens;

            let mut iter = input.into_iter().peekable();
            match $name::parse(&mut iter) {
                Ok(value) => match iter.peek() {
                    Some(tree) => Error::UnexpectedToken { tree: tree.clone() }.to_compile_error(),
                    None => value.into_token_stream(),
                },
                Err(err) => err.to_compile_error(),
            }
        }
    )*};
}

impl_macros![date datetime offset time];

// TODO Gate this behind the the `formatting` or `parsing` feature flag when weak dependency
// features land.
#[proc_macro]
pub fn format_description(input: TokenStream) -> TokenStream {
    (|| {
        let (span, string) = helpers::get_string_literal(input)?;
        let items = format_description::parse(&string, span)?;

        Ok(quote! {{
            const DESCRIPTION: &[::time::format_description::FormatItem<'_>] = &[#(
                items
                    .into_iter()
                    .map(|item| quote! { #(item), })
                    .collect::<TokenStream>()
            )];
            DESCRIPTION
        }})
    })()
    .unwrap_or_else(|err: Error| err.to_compile_error())
}

fn make_serde_serializer_module(
    mod_name: proc_macro::Ident,
    items: impl to_tokens::ToTokens,
    formattable: TokenStream,
    format_string: &str,
) -> TokenStream {
    let visitor_struct = quote! {
        struct Visitor<T: ?Sized>(::core::marker::PhantomData<T>);

        impl<'a> ::serde::de::Visitor<'a> for Visitor<#(formattable.clone())> {
            type Value = #(formattable.clone());

            fn expecting(&self, formatter: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
                // `write!` macro confuses `quote!` so format our message manually
                formatter.write_str("a(n) `")?;
                formatter.write_str(#(formattable.to_string()))?;
                formatter.write_str("` in the format \"")?;
                formatter.write_str(&FORMAT_STRING)?;
                formatter.write_str("\"")
            }

            fn visit_str<E: ::serde::de::Error>(
                self,
                value: &str
            ) -> Result<Self::Value, E> {
                #(formattable.clone())::parse(value, &DESCRIPTION).map_err(E::custom)
            }
        }

        impl<'a> ::serde::de::Visitor<'a> for Visitor<Option<#(formattable.clone())>> {
            type Value = Option<#(formattable.clone())>;

            fn expecting(&self, formatter: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
                // `write!` macro confuses `quote!` so format our message manually
                formatter.write_str("an `Option<")?;
                formatter.write_str(#(formattable.to_string()))?;
                formatter.write_str(">` in the format \"")?;
                formatter.write_str(&FORMAT_STRING)?;
                formatter.write_str("\"")
            }

            fn visit_some<D: ::serde::de::Deserializer<'a>>(
                self,
                deserializer: D
            ) -> Result<Self::Value, D::Error> {
                let visitor = Visitor::<#(formattable.clone())>(::core::marker::PhantomData);
                deserializer
                    .deserialize_any(visitor)
                    .map(Some)
            }

            fn visit_none<E: ::serde::de::Error>(
                self
            ) -> Result<Option<#(formattable.clone())>, E> {
                Ok(None)
            }
        }

    };
    let serialize_fns = quote! {
        pub fn serialize<S: ::serde::Serializer>(
            datetime: &#(formattable.clone()),
            serializer: S,
        ) -> Result<S::Ok, S::Error> {
            use ::serde::Serialize;
            datetime
                .format(&DESCRIPTION)
                .map_err(::time::error::Format::into_invalid_serde_value::<S>)?
                .serialize(serializer)
        }

        pub fn deserialize<'a, D: ::serde::Deserializer<'a>>(
            deserializer: D
        ) -> Result<#(formattable.clone()), D::Error> {
            use ::serde::Deserialize;
            let visitor = Visitor::<#(formattable.clone())>(::core::marker::PhantomData);
            deserializer.deserialize_any(visitor)
        }
    };
    let option_serialize_fns = quote! {
        pub fn serialize<S: ::serde::Serializer>(
            option: &Option<#(formattable.clone())>,
            serializer: S,
        ) -> Result<S::Ok, S::Error> {
            use ::serde::Serialize;
            option.map(|datetime| datetime.format(&DESCRIPTION))
                    .transpose()
                    .map_err(::time::error::Format::into_invalid_serde_value::<S>)?
                    .serialize(serializer)
        }

        pub fn deserialize<'a, D: ::serde::Deserializer<'a>>(
            deserializer: D
        ) -> Result<Option<#(formattable.clone())>, D::Error> {
            use ::serde::Deserialize;
            let visitor = Visitor::<Option<#(formattable.clone())>>(::core::marker::PhantomData);
            deserializer.deserialize_option(visitor)
        }
    };

    quote! {mod #(mod_name) {
            use ::time::#(formattable.clone());

            const DESCRIPTION: &[::time::format_description::FormatItem<'_>] = &[#(items)];
            const FORMAT_STRING: &str = #(format_string);

            #(visitor_struct)

            #(serialize_fns)

            pub mod option {
                use super::{DESCRIPTION, #(formattable), Visitor};

                #(option_serialize_fns)
            }
        }
    }
}

#[proc_macro]
pub fn declare_format_string(input: TokenStream) -> TokenStream {
    (|| {
        let mut tokens = input.into_iter().peekable();
        // First, an identifier (the desired module name)
        let mod_name = match tokens.next() {
            Some(TokenTree::Ident(ident)) => Ok(ident),
            Some(tree) => Err(Error::UnexpectedToken { tree }),
            None => Err(Error::UnexpectedEndOfInput),
        }?;

        // Followed by a comma
        helpers::consume_punct(',', &mut tokens)?;

        // Then, the type to create serde serializers for (e.g., `OffsetDateTime`).
        let formattable = match tokens.next() {
            Some(tree @ TokenTree::Ident(_)) => Ok(tree),
            Some(tree) => Err(Error::UnexpectedToken { tree }),
            None => Err(Error::UnexpectedEndOfInput),
        }?;

        // Another comma
        helpers::consume_punct(',', &mut tokens)?;

        // Then, a string literal.
        let input = TokenStream::from_iter(tokens);
        let (span, format_string) = helpers::get_string_literal(input)?;

        let items = format_description::parse(&format_string, span)?;
        let items: TokenStream = items.into_iter().map(|item| quote! { #(item), }).collect();

        Ok(make_serde_serializer_module(
            mod_name,
            items,
            formattable.into(),
            std::str::from_utf8(&format_string).unwrap(),
        ))
    })()
    .unwrap_or_else(|err: Error| err.to_compile_error_standalone())
}
<|endoftext|>