<commit_before>extern crate rand;
extern crate nalgebra;
extern crate abomonation;

use rand::random;
use abomonation::{Abomonation, encode, decode};
use nalgebra::{DMatrix, Matrix3x4, Point3, Translation3, Rotation3};

#[test]
fn abomonate_matrix3x4() {
    assert_encode_and_decode(&random::<Matrix3x4<f32>>());
}

#[test]
fn abomonate_point3() {
    assert_encode_and_decode(&random::<Point3<f64>>());
}

#[test]
fn abomonate_dmatrix() {
    assert_encode_and_decode(&DMatrix::<f32>::new_random(3, 5));
}

#[test]
fn abomonate_translation3() {
    assert_encode_and_decode(&random::<Translation3<f32>>());
}

#[test]
fn abomonate_rotation3() {
    assert_encode_and_decode(&random::<Rotation3<f64>>());
}

fn assert_encode_and_decode<T: Abomonation + PartialEq>(data: &T) {
    let mut bytes = Vec::new();
    unsafe { encode(data, &mut bytes); }

    if let Some((result, rest)) = unsafe { decode::<T>(&mut bytes) } {
        assert!(result == data);
        assert!(rest.len() == 0, "binary data was not decoded completely");
    }
}
<commit_msg>Refactor abomonation test suite using macro<commit_after>extern crate rand;
extern crate nalgebra;
extern crate abomonation;

use rand::random;
use abomonation::{Abomonation, encode, decode};
use nalgebra::{DMatrix, Matrix3x4, Point3, Translation3, Rotation3};

#[test]
fn abomonate_dmatrix() {
    assert_encode_and_decode(&DMatrix::<f32>::new_random(3, 5));
}

macro_rules! test_abomonation(
    ($($test: ident, $ty: ty);* $(;)*) => {$(
        #[test]
        fn $test() {
            assert_encode_and_decode(&random::<$ty>());
        }
    )*}
);

test_abomonation! {
    abomonate_matrix3x4, Matrix3x4<f32>;
    abomonate_point3, Point3<f32>;
    abomonate_translation3, Translation3<f64>;
    abomonate_rotation3, Rotation3<f64>;
}

fn assert_encode_and_decode<T: Abomonation + PartialEq>(data: &T) {
    let mut bytes = Vec::new();
    unsafe { encode(data, &mut bytes); }

    if let Some((result, rest)) = unsafe { decode::<T>(&mut bytes) } {
        assert!(result == data);
        assert!(rest.len() == 0, "binary data was not decoded completely");
    }
}
<|endoftext|>


<commit_before>//! The OTLP Exporter supports exporting trace and metric data in the OTLP
//! format to the OpenTelemetry collector or other compatible backend.
//!
//! The OpenTelemetry Collector offers a vendor-agnostic implementation on how
//! to receive, process, and export telemetry data. In addition, it removes
//! the need to run, operate, and maintain multiple agents/collectors in
//! order to support open-source telemetry data formats (e.g. Jaeger,
//! Prometheus, etc.) sending to multiple open-source or commercial back-ends.
//!
//! Currently, this crate only support sending tracing data or metrics in OTLP
//! via grpc and http(in binary format). Supports for other format and protocol
//! will be added in the future. The details of what's currently offering in this
//! crate can be found in this doc.
//!
//! # Quickstart
//!
//! First make sure you have a running version of the opentelemetry collector
//! you want to send data to:
//!
//! ```shell
//! $ docker run -p 4317:4317 otel/opentelemetry-collector-dev:latest
//! ```
//!
//! Then install a new pipeline with the recommended defaults to start exporting
//! telemetry. You will have to build a OTLP exporter first.
//!
//! To start a OTLP tracing pipeline. Use `new_pipeline().tracing()` function.
//!
//! To start a OTLP metrics pipeline. Use `new_pipeline().metrics()` function.
//!
//! ```no_run
//! use opentelemetry::trace::Tracer;
//!
//! fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
//!     // First, create a OTLP exporter builder. Configure it as you need.
//!     let otlp_exporter = opentelemetry_otlp::new_exporter().tonic();
//!     // Then pass it into pipeline builder
//!     let tracer = opentelemetry_otlp::new_pipeline()
//!             .tracing()
//!             .with_exporter(otlp_exporter)
//!             .install_simple()?;
//!
//!     tracer.in_span("doing_work", |cx| {
//!         // Traced app logic here...
//!     });
//!
//!     Ok(())
//! }
//! ```
//!
//! ## Performance
//!
//! For optimal performance, a batch exporter is recommended as the simple
//! exporter will export each span synchronously on dropping. You can enable the
//! [`rt-tokio`], [`rt-tokio-current-thread`] or [`rt-async-std`] features and
//! specify a runtime on the pipeline builder to have a batch exporter
//! configured for you automatically.
//!
//! ```toml
//! [dependencies]
//! opentelemetry = { version = "*", features = ["async-std"] }
//! opentelemetry-otlp = { version = "*", features = ["grpc-sys"] }
//! ```
//!
//! ```no_run
//! # fn main() -> Result<(), opentelemetry::trace::TraceError> {
//! let tracer = opentelemetry_otlp::new_pipeline()
//!     .tracing()
//!     .with_exporter(opentelemetry_otlp::new_exporter().tonic())
//!     .install_batch(opentelemetry::runtime::AsyncStd)?;
//! # Ok(())
//! # }
//! ```
//!
//! [`tokio`]: https://tokio.rs
//! [`async-std`]: https://async.rs
//!
//! # Kitchen Sink Full Configuration
//!
//! Example showing how to override all configuration options.
//!
//! Generally there are two parts of configuration. One is metrics config
//! or tracing config. Users can config it via [`OtlpTracePipeline`]
//! or [`OtlpMetricPipeline`]. The other is exporting configuration.
//! Users can set those configurations using [`OtlpExporterPipeline`] based
//! on the choice of exporters.
//!
//! ```no_run
//! use opentelemetry::{KeyValue, trace::Tracer};
//! use opentelemetry::sdk::{trace::{self, IdGenerator, Sampler}, Resource};
//! use opentelemetry::sdk::metrics::{selectors, PushController};
//! use opentelemetry::util::tokio_interval_stream;
//! use opentelemetry_otlp::{Protocol, WithExportConfig, ExportConfig};
//! use std::time::Duration;
//! use tonic::metadata::*;
//!
//! fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
//!     let mut map = MetadataMap::with_capacity(3);
//!
//!     map.insert("x-host", "example.com".parse().unwrap());
//!     map.insert("x-number", "123".parse().unwrap());
//!     map.insert_bin("trace-proto-bin", MetadataValue::from_bytes(b"[binary data]"));
//!
//!     let tracer = opentelemetry_otlp::new_pipeline()
//!         .tracing()
//!         .with_exporter(
//!             opentelemetry_otlp::new_exporter()
//!             .tonic()
//!             .with_endpoint("http://localhost:4317")
//!             .with_timeout(Duration::from_secs(3))
//!             .with_metadata(map)
//!          )
//!         .with_trace_config(
//!             trace::config()
//!                 .with_sampler(Sampler::AlwaysOn)
//!                 .with_id_generator(IdGenerator::default())
//!                 .with_max_events_per_span(64)
//!                 .with_max_attributes_per_span(16)
//!                 .with_max_events_per_span(16)
//!                 .with_resource(Resource::new(vec![KeyValue::new("service.name", "example")])),
//!         )
//!         .install_batch(opentelemetry::runtime::Tokio)?;
//!
//!     let export_config = ExportConfig {
//!         endpoint: "http://localhost:4317".to_string(),
//!         timeout: Duration::from_secs(3),
//!         protocol: Protocol::Grpc
//!     };
//!
//!     let meter = opentelemetry_otlp::new_pipeline()
//!         .metrics(tokio::spawn, tokio_interval_stream)
//!         .with_exporter(
//!             opentelemetry_otlp::new_exporter()
//!                 .tonic()
//!                 .with_export_config(export_config),
//!                 // can also config it using with_* functions like the tracing part above.
//!         )
//!         .with_stateful(true)
//!         .with_period(Duration::from_secs(3))
//!         .with_timeout(Duration::from_secs(10))
//!         .with_aggregator_selector(selectors::simple::Selector::Exact)
//!         .build();
//!
//!     tracer.in_span("doing_work", |cx| {
//!         // Traced app logic here...
//!     });
//!
//!     Ok(())
//! }
//! ```
//!
//! # Grpc libraries comparison
//!
//! The table below provides a short comparison between `grpcio` and `tonic`, two
//! of the most popular grpc libraries in Rust. Users can choose between them when
//! working with otlp and grpc.
//!
//! | Project | [hyperium/tonic](https://github.com/hyperium/tonic) | [tikv/grpc-rs](https://github.com/tikv/grpc-rs) |
//! |---|---|---|
//! | Feature name | --features=default | --features=grpc-sys |
//! | gRPC library | [`tonic`](https://crates.io/crates/tonic) | [`grpcio`](https://crates.io/crates/grpcio) |
//! | Transport | [hyperium/hyper](https://github.com/hyperium/hyper) (Rust) | [grpc/grpc](https://github.com/grpc/grpc) (C++ binding) |
//! | TLS support | yes | yes |
//! | TLS optional | yes | yes |
//! | TLS library | rustls | OpenSSL |
//! | Supported .proto generator | [`prost`](https://crates.io/crates/prost) | [`prost`](https://crates.io/crates/prost), [`protobuf`](https://crates.io/crates/protobuf) |
#![warn(
    future_incompatible,
    missing_debug_implementations,
    missing_docs,
    nonstandard_style,
    rust_2018_idioms,
    unreachable_pub,
    unused
)]
#![allow(elided_lifetimes_in_paths)]
#![cfg_attr(docsrs, feature(doc_cfg), deny(rustdoc::broken_intra_doc_links))]
#![cfg_attr(test, deny(warnings))]

// proto mod contains file generated by protobuf or other build tools.
// we should manually change it. Thus skip format and lint check.
#[cfg(
not(feature = "integration-testing")
)]
#[rustfmt::skip]
#[allow(warnings)]
mod proto;

#[cfg(feature = "integration-testing")]
#[rustfmt::skip]
#[allow(warnings)]
#[doc(hidden)]
pub mod proto;

mod exporter;
#[cfg(feature = "metrics")]
mod metric;
mod span;
mod transform;

pub use crate::exporter::ExportConfig;
pub use crate::span::{OtlpTracePipeline, SpanExporter};

#[cfg(feature = "metrics")]
pub use crate::metric::{MetricsExporter, OtlpMetricPipeline};

pub use crate::exporter::{
    HasExportConfig, WithExportConfig, OTEL_EXPORTER_OTLP_ENDPOINT,
    OTEL_EXPORTER_OTLP_ENDPOINT_DEFAULT, OTEL_EXPORTER_OTLP_TIMEOUT,
    OTEL_EXPORTER_OTLP_TIMEOUT_DEFAULT, OTEL_EXPORTER_OTLP_TRACES_ENDPOINT,
    OTEL_EXPORTER_OTLP_TRACES_TIMEOUT,
};

use opentelemetry::sdk::export::ExportError;

#[cfg(feature = "grpc-sys")]
pub use crate::exporter::grpcio::{Compression, Credentials, GrpcioExporterBuilder};
#[cfg(feature = "http-proto")]
pub use crate::exporter::http::HttpExporterBuilder;
#[cfg(feature = "tonic")]
pub use crate::exporter::tonic::TonicExporterBuilder;

#[cfg(feature = "serialize")]
use serde::{Deserialize, Serialize};

/// General builder for both tracing and metrics.
#[derive(Debug)]
pub struct OtlpPipeline;

/// Build a OTLP metrics or tracing exporter builder. See functions below to understand
/// what's currently supported.
#[derive(Debug)]
pub struct OtlpExporterPipeline;

impl OtlpExporterPipeline {
    /// Use tonic as grpc layer, return a `TonicExporterBuilder` to config tonic and build the exporter.
    ///
    /// This exporter can be used in both `tracing` and `metrics` pipeline.
    #[cfg(feature = "tonic")]
    pub fn tonic(self) -> TonicExporterBuilder {
        TonicExporterBuilder::default()
    }

    /// Use grpcio as grpc layer, return a `GrpcioExporterBuilder` to config the grpcio and build the exporter.
    ///
    /// This exporter can only be used in `tracing` pipeline. Support for `metrics` pipeline will be
    /// added in the future.
    #[cfg(feature = "grpc-sys")]
    pub fn grpcio(self) -> GrpcioExporterBuilder {
        GrpcioExporterBuilder::default()
    }

    /// Use HTTP as transport layer, return a `HttpExporterBuilder` to config the http transport
    /// and build the exporter.
    ///
    /// This exporter can only be used in `tracing` pipeline. Support for `metrics` pipeline will be
    /// added in the future.
    #[cfg(feature = "http-proto")]
    pub fn http(self) -> HttpExporterBuilder {
        HttpExporterBuilder::default()
    }
}

/// Create a new pipeline builder with the recommended configuration.
///
/// ## Examples
///
/// ```no_run
/// fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
///     let tracing_builder = opentelemetry_otlp::new_pipeline()
///                    .tracing();
///
///     Ok(())
/// }
/// ```
pub fn new_pipeline() -> OtlpPipeline {
    OtlpPipeline
}

/// Create a builder to build OTLP metrics exporter or tracing exporter.
pub fn new_exporter() -> OtlpExporterPipeline {
    OtlpExporterPipeline
}

/// Wrap type for errors from this crate.
#[derive(thiserror::Error, Debug)]
pub enum Error {
    /// Wrap error from [`tonic::transport::Error`]
    #[cfg(feature = "tonic")]
    #[error("transport error {0}")]
    Transport(#[from] tonic::transport::Error),

    /// Wrap the [`tonic::codegen::http::uri::InvalidUri`] error
    #[cfg(any(feature = "tonic", feature = "http-proto"))]
    #[error("invalid URI {0}")]
    InvalidUri(#[from] http::uri::InvalidUri),

    /// Wrap type for [`tonic::Status`]
    #[cfg(feature = "tonic")]
    #[error("the grpc server returns error {code}")]
    Status {
        /// grpc status code
        code: tonic::Code,
        /// error message
        message: String,
    },

    /// Wrap errors from grpcio.
    #[cfg(feature = "grpc-sys")]
    #[error("grpcio error {0}")]
    Grpcio(#[from] grpcio::Error),

    /// Http requests failed because no http client is provided.
    #[cfg(feature = "http-proto")]
    #[error("no http client, you must select one")]
    NoHttpClient,

    /// Http requests failed.
    #[cfg(feature = "http-proto")]
    #[error("http request failed with {0}")]
    RequestFailed(#[from] http::Error),

    /// The provided value is invalid in HTTP headers.
    #[cfg(feature = "http-proto")]
    #[error("http header value error {0}")]
    InvalidHeaderValue(#[from] http::header::InvalidHeaderValue),

    /// The provided name is invalid in HTTP headers.
    #[cfg(feature = "http-proto")]
    #[error("http header name error {0}")]
    InvalidHeaderName(#[from] http::header::InvalidHeaderName),

    /// Prost encode failed
    #[cfg(feature = "http-proto")]
    #[error("prost encoding error {0}")]
    EncodeError(#[from] prost::EncodeError),

    /// The lock in exporters has been poisoned.
    #[cfg(feature = "metrics")]
    #[error("the lock of the {0} has been poisoned")]
    PoisonedLock(&'static str),

    /// The pipeline will need a exporter to complete setup. Throw this error if none is provided.
    #[error("no exporter builder is provided, please provide one using with_exporter() method")]
    NoExporterBuilder,
}

#[cfg(feature = "tonic")]
impl From<tonic::Status> for Error {
    fn from(status: tonic::Status) -> Error {
        Error::Status {
            code: status.code(),
            message: {
                if !status.message().is_empty() {
                    ", detailed error message: ".to_string() + status.message()
                } else {
                    "".to_string()
                }
            },
        }
    }
}

impl ExportError for Error {
    fn exporter_name(&self) -> &'static str {
        "otlp"
    }
}

/// The communication protocol to use when exporting data.
#[cfg_attr(feature = "serialize", derive(Deserialize, Serialize))]
#[derive(Clone, Copy, Debug)]
pub enum Protocol {
    /// GRPC protocol
    Grpc,
    // TODO add support for other protocols
    // HttpJson,
    /// HTTP protocol with binary protobuf
    HttpBinary,
}
<commit_msg>[OTLP] Changed tonic error message to include the server's message in the error (#642)<commit_after>//! The OTLP Exporter supports exporting trace and metric data in the OTLP
//! format to the OpenTelemetry collector or other compatible backend.
//!
//! The OpenTelemetry Collector offers a vendor-agnostic implementation on how
//! to receive, process, and export telemetry data. In addition, it removes
//! the need to run, operate, and maintain multiple agents/collectors in
//! order to support open-source telemetry data formats (e.g. Jaeger,
//! Prometheus, etc.) sending to multiple open-source or commercial back-ends.
//!
//! Currently, this crate only support sending tracing data or metrics in OTLP
//! via grpc and http(in binary format). Supports for other format and protocol
//! will be added in the future. The details of what's currently offering in this
//! crate can be found in this doc.
//!
//! # Quickstart
//!
//! First make sure you have a running version of the opentelemetry collector
//! you want to send data to:
//!
//! ```shell
//! $ docker run -p 4317:4317 otel/opentelemetry-collector-dev:latest
//! ```
//!
//! Then install a new pipeline with the recommended defaults to start exporting
//! telemetry. You will have to build a OTLP exporter first.
//!
//! To start a OTLP tracing pipeline. Use `new_pipeline().tracing()` function.
//!
//! To start a OTLP metrics pipeline. Use `new_pipeline().metrics()` function.
//!
//! ```no_run
//! use opentelemetry::trace::Tracer;
//!
//! fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
//!     // First, create a OTLP exporter builder. Configure it as you need.
//!     let otlp_exporter = opentelemetry_otlp::new_exporter().tonic();
//!     // Then pass it into pipeline builder
//!     let tracer = opentelemetry_otlp::new_pipeline()
//!             .tracing()
//!             .with_exporter(otlp_exporter)
//!             .install_simple()?;
//!
//!     tracer.in_span("doing_work", |cx| {
//!         // Traced app logic here...
//!     });
//!
//!     Ok(())
//! }
//! ```
//!
//! ## Performance
//!
//! For optimal performance, a batch exporter is recommended as the simple
//! exporter will export each span synchronously on dropping. You can enable the
//! [`rt-tokio`], [`rt-tokio-current-thread`] or [`rt-async-std`] features and
//! specify a runtime on the pipeline builder to have a batch exporter
//! configured for you automatically.
//!
//! ```toml
//! [dependencies]
//! opentelemetry = { version = "*", features = ["async-std"] }
//! opentelemetry-otlp = { version = "*", features = ["grpc-sys"] }
//! ```
//!
//! ```no_run
//! # fn main() -> Result<(), opentelemetry::trace::TraceError> {
//! let tracer = opentelemetry_otlp::new_pipeline()
//!     .tracing()
//!     .with_exporter(opentelemetry_otlp::new_exporter().tonic())
//!     .install_batch(opentelemetry::runtime::AsyncStd)?;
//! # Ok(())
//! # }
//! ```
//!
//! [`tokio`]: https://tokio.rs
//! [`async-std`]: https://async.rs
//!
//! # Kitchen Sink Full Configuration
//!
//! Example showing how to override all configuration options.
//!
//! Generally there are two parts of configuration. One is metrics config
//! or tracing config. Users can config it via [`OtlpTracePipeline`]
//! or [`OtlpMetricPipeline`]. The other is exporting configuration.
//! Users can set those configurations using [`OtlpExporterPipeline`] based
//! on the choice of exporters.
//!
//! ```no_run
//! use opentelemetry::{KeyValue, trace::Tracer};
//! use opentelemetry::sdk::{trace::{self, IdGenerator, Sampler}, Resource};
//! use opentelemetry::sdk::metrics::{selectors, PushController};
//! use opentelemetry::util::tokio_interval_stream;
//! use opentelemetry_otlp::{Protocol, WithExportConfig, ExportConfig};
//! use std::time::Duration;
//! use tonic::metadata::*;
//!
//! fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
//!     let mut map = MetadataMap::with_capacity(3);
//!
//!     map.insert("x-host", "example.com".parse().unwrap());
//!     map.insert("x-number", "123".parse().unwrap());
//!     map.insert_bin("trace-proto-bin", MetadataValue::from_bytes(b"[binary data]"));
//!
//!     let tracer = opentelemetry_otlp::new_pipeline()
//!         .tracing()
//!         .with_exporter(
//!             opentelemetry_otlp::new_exporter()
//!             .tonic()
//!             .with_endpoint("http://localhost:4317")
//!             .with_timeout(Duration::from_secs(3))
//!             .with_metadata(map)
//!          )
//!         .with_trace_config(
//!             trace::config()
//!                 .with_sampler(Sampler::AlwaysOn)
//!                 .with_id_generator(IdGenerator::default())
//!                 .with_max_events_per_span(64)
//!                 .with_max_attributes_per_span(16)
//!                 .with_max_events_per_span(16)
//!                 .with_resource(Resource::new(vec![KeyValue::new("service.name", "example")])),
//!         )
//!         .install_batch(opentelemetry::runtime::Tokio)?;
//!
//!     let export_config = ExportConfig {
//!         endpoint: "http://localhost:4317".to_string(),
//!         timeout: Duration::from_secs(3),
//!         protocol: Protocol::Grpc
//!     };
//!
//!     let meter = opentelemetry_otlp::new_pipeline()
//!         .metrics(tokio::spawn, tokio_interval_stream)
//!         .with_exporter(
//!             opentelemetry_otlp::new_exporter()
//!                 .tonic()
//!                 .with_export_config(export_config),
//!                 // can also config it using with_* functions like the tracing part above.
//!         )
//!         .with_stateful(true)
//!         .with_period(Duration::from_secs(3))
//!         .with_timeout(Duration::from_secs(10))
//!         .with_aggregator_selector(selectors::simple::Selector::Exact)
//!         .build();
//!
//!     tracer.in_span("doing_work", |cx| {
//!         // Traced app logic here...
//!     });
//!
//!     Ok(())
//! }
//! ```
//!
//! # Grpc libraries comparison
//!
//! The table below provides a short comparison between `grpcio` and `tonic`, two
//! of the most popular grpc libraries in Rust. Users can choose between them when
//! working with otlp and grpc.
//!
//! | Project | [hyperium/tonic](https://github.com/hyperium/tonic) | [tikv/grpc-rs](https://github.com/tikv/grpc-rs) |
//! |---|---|---|
//! | Feature name | --features=default | --features=grpc-sys |
//! | gRPC library | [`tonic`](https://crates.io/crates/tonic) | [`grpcio`](https://crates.io/crates/grpcio) |
//! | Transport | [hyperium/hyper](https://github.com/hyperium/hyper) (Rust) | [grpc/grpc](https://github.com/grpc/grpc) (C++ binding) |
//! | TLS support | yes | yes |
//! | TLS optional | yes | yes |
//! | TLS library | rustls | OpenSSL |
//! | Supported .proto generator | [`prost`](https://crates.io/crates/prost) | [`prost`](https://crates.io/crates/prost), [`protobuf`](https://crates.io/crates/protobuf) |
#![warn(
    future_incompatible,
    missing_debug_implementations,
    missing_docs,
    nonstandard_style,
    rust_2018_idioms,
    unreachable_pub,
    unused
)]
#![allow(elided_lifetimes_in_paths)]
#![cfg_attr(docsrs, feature(doc_cfg), deny(rustdoc::broken_intra_doc_links))]
#![cfg_attr(test, deny(warnings))]

// proto mod contains file generated by protobuf or other build tools.
// we should manually change it. Thus skip format and lint check.
#[cfg(
not(feature = "integration-testing")
)]
#[rustfmt::skip]
#[allow(warnings)]
mod proto;

#[cfg(feature = "integration-testing")]
#[rustfmt::skip]
#[allow(warnings)]
#[doc(hidden)]
pub mod proto;

mod exporter;
#[cfg(feature = "metrics")]
mod metric;
mod span;
mod transform;

pub use crate::exporter::ExportConfig;
pub use crate::span::{OtlpTracePipeline, SpanExporter};

#[cfg(feature = "metrics")]
pub use crate::metric::{MetricsExporter, OtlpMetricPipeline};

pub use crate::exporter::{
    HasExportConfig, WithExportConfig, OTEL_EXPORTER_OTLP_ENDPOINT,
    OTEL_EXPORTER_OTLP_ENDPOINT_DEFAULT, OTEL_EXPORTER_OTLP_TIMEOUT,
    OTEL_EXPORTER_OTLP_TIMEOUT_DEFAULT, OTEL_EXPORTER_OTLP_TRACES_ENDPOINT,
    OTEL_EXPORTER_OTLP_TRACES_TIMEOUT,
};

use opentelemetry::sdk::export::ExportError;

#[cfg(feature = "grpc-sys")]
pub use crate::exporter::grpcio::{Compression, Credentials, GrpcioExporterBuilder};
#[cfg(feature = "http-proto")]
pub use crate::exporter::http::HttpExporterBuilder;
#[cfg(feature = "tonic")]
pub use crate::exporter::tonic::TonicExporterBuilder;

#[cfg(feature = "serialize")]
use serde::{Deserialize, Serialize};

/// General builder for both tracing and metrics.
#[derive(Debug)]
pub struct OtlpPipeline;

/// Build a OTLP metrics or tracing exporter builder. See functions below to understand
/// what's currently supported.
#[derive(Debug)]
pub struct OtlpExporterPipeline;

impl OtlpExporterPipeline {
    /// Use tonic as grpc layer, return a `TonicExporterBuilder` to config tonic and build the exporter.
    ///
    /// This exporter can be used in both `tracing` and `metrics` pipeline.
    #[cfg(feature = "tonic")]
    pub fn tonic(self) -> TonicExporterBuilder {
        TonicExporterBuilder::default()
    }

    /// Use grpcio as grpc layer, return a `GrpcioExporterBuilder` to config the grpcio and build the exporter.
    ///
    /// This exporter can only be used in `tracing` pipeline. Support for `metrics` pipeline will be
    /// added in the future.
    #[cfg(feature = "grpc-sys")]
    pub fn grpcio(self) -> GrpcioExporterBuilder {
        GrpcioExporterBuilder::default()
    }

    /// Use HTTP as transport layer, return a `HttpExporterBuilder` to config the http transport
    /// and build the exporter.
    ///
    /// This exporter can only be used in `tracing` pipeline. Support for `metrics` pipeline will be
    /// added in the future.
    #[cfg(feature = "http-proto")]
    pub fn http(self) -> HttpExporterBuilder {
        HttpExporterBuilder::default()
    }
}

/// Create a new pipeline builder with the recommended configuration.
///
/// ## Examples
///
/// ```no_run
/// fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
///     let tracing_builder = opentelemetry_otlp::new_pipeline()
///                    .tracing();
///
///     Ok(())
/// }
/// ```
pub fn new_pipeline() -> OtlpPipeline {
    OtlpPipeline
}

/// Create a builder to build OTLP metrics exporter or tracing exporter.
pub fn new_exporter() -> OtlpExporterPipeline {
    OtlpExporterPipeline
}

/// Wrap type for errors from this crate.
#[derive(thiserror::Error, Debug)]
pub enum Error {
    /// Wrap error from [`tonic::transport::Error`]
    #[cfg(feature = "tonic")]
    #[error("transport error {0}")]
    Transport(#[from] tonic::transport::Error),

    /// Wrap the [`tonic::codegen::http::uri::InvalidUri`] error
    #[cfg(any(feature = "tonic", feature = "http-proto"))]
    #[error("invalid URI {0}")]
    InvalidUri(#[from] http::uri::InvalidUri),

    /// Wrap type for [`tonic::Status`]
    #[cfg(feature = "tonic")]
    #[error("the grpc server returns error ({code}): {message}")]
    Status {
        /// grpc status code
        code: tonic::Code,
        /// error message
        message: String,
    },

    /// Wrap errors from grpcio.
    #[cfg(feature = "grpc-sys")]
    #[error("grpcio error {0}")]
    Grpcio(#[from] grpcio::Error),

    /// Http requests failed because no http client is provided.
    #[cfg(feature = "http-proto")]
    #[error("no http client, you must select one")]
    NoHttpClient,

    /// Http requests failed.
    #[cfg(feature = "http-proto")]
    #[error("http request failed with {0}")]
    RequestFailed(#[from] http::Error),

    /// The provided value is invalid in HTTP headers.
    #[cfg(feature = "http-proto")]
    #[error("http header value error {0}")]
    InvalidHeaderValue(#[from] http::header::InvalidHeaderValue),

    /// The provided name is invalid in HTTP headers.
    #[cfg(feature = "http-proto")]
    #[error("http header name error {0}")]
    InvalidHeaderName(#[from] http::header::InvalidHeaderName),

    /// Prost encode failed
    #[cfg(feature = "http-proto")]
    #[error("prost encoding error {0}")]
    EncodeError(#[from] prost::EncodeError),

    /// The lock in exporters has been poisoned.
    #[cfg(feature = "metrics")]
    #[error("the lock of the {0} has been poisoned")]
    PoisonedLock(&'static str),

    /// The pipeline will need a exporter to complete setup. Throw this error if none is provided.
    #[error("no exporter builder is provided, please provide one using with_exporter() method")]
    NoExporterBuilder,
}

#[cfg(feature = "tonic")]
impl From<tonic::Status> for Error {
    fn from(status: tonic::Status) -> Error {
        Error::Status {
            code: status.code(),
            message: {
                if !status.message().is_empty() {
                    ", detailed error message: ".to_string() + status.message()
                } else {
                    "".to_string()
                }
            },
        }
    }
}

impl ExportError for Error {
    fn exporter_name(&self) -> &'static str {
        "otlp"
    }
}

/// The communication protocol to use when exporting data.
#[cfg_attr(feature = "serialize", derive(Deserialize, Serialize))]
#[derive(Clone, Copy, Debug)]
pub enum Protocol {
    /// GRPC protocol
    Grpc,
    // TODO add support for other protocols
    // HttpJson,
    /// HTTP protocol with binary protobuf
    HttpBinary,
}
<|endoftext|>


<commit_before>extern crate rusqlite;
use self::rusqlite::Connection;

pub fn create_connection() -> bool{

    Connection::open_in_memory().unwrap();
    return false;
}

pub fn hello_from_dao() -> String {
    "Hello, I am bookmark dao!".to_string()
}
<commit_msg>change main<commit_after>extern crate rusqlite;
use self::rusqlite::Connection;

pub fn create_connection() -> bool {
    Connection::open_in_memory().unwrap();
    return false;
}

pub fn hello_from_dao() -> String {
    "Hello, I am bookmark dao!".to_string()
}
<|endoftext|>


<commit_before>//! Utilities for game state management.

use super::timing::Duration;

/// Types of state transitions.
pub enum Trans {
    /// Continue as normal.
    None,
    /// Remove the active state and resume the next state on the stack (if any).
    Pop,
    /// Pause the active state and push a new state onto the stack.
    Push(Box<State>),
    /// Remove the current state on the stack and insert a different one.
    Switch(Box<State>),
    /// Stop and remove all states and shut down the engine.
    Quit,
}

/// A trait which defines game states that can be used by the state machine.
pub trait State {
    /// Executed when the game state begins.
    fn on_start(&mut self) {}

    /// Executed when the game state exits.
    fn on_stop(&mut self) {}

    /// Executed when a different game state is pushed onto the stack.
    fn on_pause(&mut self) {}

    /// Executed when the application returns to this game state once again.
    fn on_resume(&mut self) {}

    /// Executed on every frame before updating, for use in reacting to events.
    // TODO: Replace i32 with an actual Event type of some kind.
    fn handle_events(&mut self, _events: &Vec<i32>) {}

    /// Executed repeatedly at stable, predictable intervals (1/60th of a second
    /// by default).
    fn fixed_update(&mut self, _delta: Duration) -> Trans { Trans::None }

    /// Executed on every frame immediately, as fast as the engine will allow.
    fn update(&mut self, _delta: Duration) -> Trans { Trans::Pop }
}

/// A simple stack-based state machine (pushdown automaton).
pub struct StateMachine {
    running: bool,
    state_stack: Vec<Box<State>>,
}

impl StateMachine {
    pub fn new<T: 'static>(initial_state: T) -> StateMachine
        where T: State
    {
        StateMachine {
            running: false,
            state_stack: vec![Box::new(initial_state)],
        }
    }

    /// Checks whether the state machine is running.
    pub fn is_running(&self) -> bool {
        self.running
    }

    /// Initializes the state machine.
    pub fn start(&mut self) {
        if !self.running {
            self.state_stack.last_mut().unwrap().on_start();
            self.running = true;
        }
    }

    /// Passes a vector of events to the active state to handle.
    // TODO: Replace i32 with an actual Event type of some kind.
    pub fn handle_events(&mut self, events: &Vec<i32>) {
        if self.running {
            if let Some(state) = self.state_stack.last_mut() {
                state.handle_events(events);
            }
        }
    }

    /// Updates the currently active state at a steady, fixed interval.
    pub fn fixed_update(&mut self, delta_time: Duration) {
        if self.running {
            let mut trans = Trans::None;
            if let Some(state) = self.state_stack.last_mut() {
                trans = state.fixed_update(delta_time);
            }
            self.transition(trans);
        }
    }

    /// Updates the currently active state immediately.
    pub fn update(&mut self, delta_time: Duration) {
        if self.running {
            let mut trans = Trans::None;
            if let Some(state) = self.state_stack.last_mut() {
                trans = state.update(delta_time);
            }
            self.transition(trans);
        }
    }

    /// Performs a state transition, if requested by either update() or
    /// fixed_update().
    fn transition(&mut self, request: Trans) {
        if self.running {
            match request {
                Trans::None => (),
                Trans::Pop => self.pop(),
                Trans::Push(state) => self.push(state),
                Trans::Switch(state) => self.switch(state),
                Trans::Quit => self.stop(),
            }
        }
    }

    /// Removes the current state on the stack and inserts a different one.
    fn switch(&mut self, state: Box<State>) {
        if self.running {
            if let Some(mut state) = self.state_stack.pop() {
                state.on_stop();
            }

            self.state_stack.push(state);
            self.state_stack.last_mut().unwrap().on_start();
        }
    }

    /// Pauses the active state and pushes a new state onto the state stack.
    fn push(&mut self, state: Box<State>) {
        if self.running {
            if let Some(state) = self.state_stack.last_mut() {
                state.on_pause();
            }

            self.state_stack.push(state);
            self.state_stack.last_mut().unwrap().on_start();
        }
    }

    /// Stops and removes the active state and un-pauses the next state on the
    /// stack (if any).
    fn pop(&mut self) {
        if self.running {
            if let Some(mut state) = self.state_stack.pop() {
                state.on_stop();
            }

            if let Some(state) = self.state_stack.last_mut() {
                state.on_resume();
            } else {
                self.running = false;
            }
        }
    }

    /// Shuts the state machine down.
    fn stop(&mut self) {
        if self.running {
            while let Some(mut state) = self.state_stack.pop() {
                state.on_stop();
            }

            self.running = false;
        }
    }
}
<commit_msg>use slices instead of vectors<commit_after>//! Utilities for game state management.

use super::timing::Duration;

/// Types of state transitions.
pub enum Trans {
    /// Continue as normal.
    None,
    /// Remove the active state and resume the next state on the stack (if any).
    Pop,
    /// Pause the active state and push a new state onto the stack.
    Push(Box<State>),
    /// Remove the current state on the stack and insert a different one.
    Switch(Box<State>),
    /// Stop and remove all states and shut down the engine.
    Quit,
}

/// A trait which defines game states that can be used by the state machine.
pub trait State {
    /// Executed when the game state begins.
    fn on_start(&mut self) {}

    /// Executed when the game state exits.
    fn on_stop(&mut self) {}

    /// Executed when a different game state is pushed onto the stack.
    fn on_pause(&mut self) {}

    /// Executed when the application returns to this game state once again.
    fn on_resume(&mut self) {}

    /// Executed on every frame before updating, for use in reacting to events.
    // TODO: Replace i32 with an actual Event type of some kind.
    fn handle_events(&mut self, _events: &[i32]) {}

    /// Executed repeatedly at stable, predictable intervals (1/60th of a second
    /// by default).
    fn fixed_update(&mut self, _delta: Duration) -> Trans { Trans::None }

    /// Executed on every frame immediately, as fast as the engine will allow.
    fn update(&mut self, _delta: Duration) -> Trans { Trans::Pop }
}

/// A simple stack-based state machine (pushdown automaton).
pub struct StateMachine {
    running: bool,
    state_stack: Vec<Box<State>>,
}

impl StateMachine {
    pub fn new<T: 'static>(initial_state: T) -> StateMachine
        where T: State
    {
        StateMachine {
            running: false,
            state_stack: vec![Box::new(initial_state)],
        }
    }

    /// Checks whether the state machine is running.
    pub fn is_running(&self) -> bool {
        self.running
    }

    /// Initializes the state machine.
    pub fn start(&mut self) {
        if !self.running {
            self.state_stack.last_mut().unwrap().on_start();
            self.running = true;
        }
    }

    /// Passes a vector of events to the active state to handle.
    // TODO: Replace i32 with an actual Event type of some kind.
    pub fn handle_events(&mut self, events: &[i32]) {
        if self.running {
            if let Some(state) = self.state_stack.last_mut() {
                state.handle_events(events);
            }
        }
    }

    /// Updates the currently active state at a steady, fixed interval.
    pub fn fixed_update(&mut self, delta_time: Duration) {
        if self.running {
            let mut trans = Trans::None;
            if let Some(state) = self.state_stack.last_mut() {
                trans = state.fixed_update(delta_time);
            }
            self.transition(trans);
        }
    }

    /// Updates the currently active state immediately.
    pub fn update(&mut self, delta_time: Duration) {
        if self.running {
            let mut trans = Trans::None;
            if let Some(state) = self.state_stack.last_mut() {
                trans = state.update(delta_time);
            }
            self.transition(trans);
        }
    }

    /// Performs a state transition, if requested by either update() or
    /// fixed_update().
    fn transition(&mut self, request: Trans) {
        if self.running {
            match request {
                Trans::None => (),
                Trans::Pop => self.pop(),
                Trans::Push(state) => self.push(state),
                Trans::Switch(state) => self.switch(state),
                Trans::Quit => self.stop(),
            }
        }
    }

    /// Removes the current state on the stack and inserts a different one.
    fn switch(&mut self, state: Box<State>) {
        if self.running {
            if let Some(mut state) = self.state_stack.pop() {
                state.on_stop();
            }

            self.state_stack.push(state);
            self.state_stack.last_mut().unwrap().on_start();
        }
    }

    /// Pauses the active state and pushes a new state onto the state stack.
    fn push(&mut self, state: Box<State>) {
        if self.running {
            if let Some(state) = self.state_stack.last_mut() {
                state.on_pause();
            }

            self.state_stack.push(state);
            self.state_stack.last_mut().unwrap().on_start();
        }
    }

    /// Stops and removes the active state and un-pauses the next state on the
    /// stack (if any).
    fn pop(&mut self) {
        if self.running {
            if let Some(mut state) = self.state_stack.pop() {
                state.on_stop();
            }

            if let Some(state) = self.state_stack.last_mut() {
                state.on_resume();
            } else {
                self.running = false;
            }
        }
    }

    /// Shuts the state machine down.
    fn stop(&mut self) {
        if self.running {
            while let Some(mut state) = self.state_stack.pop() {
                state.on_stop();
            }

            self.running = false;
        }
    }
}
<|endoftext|>


<commit_before>pub fn build_suffix_array(s: &str) -> String {
    String::new()
}
<commit_msg>Added vector init steps<commit_after>pub fn build_suffix_array(s: &str) -> Vec<usize> {
    let n: usize = s.len();
    let mut t = vec![true;n];
    let mut s1 = vec![0usize;n];
    let mut p1 = vec![0usize;n];
    Vec::new()
}
<|endoftext|>


<commit_before>use super::SlotClock;
use std::time::{Duration, SystemTime};

pub use std::time::SystemTimeError;

#[derive(Debug, PartialEq)]
pub enum Error {
    SlotDurationIsZero,
    SystemTimeError(String),
}

/// Determines the present slot based upon the present system time.
pub struct SystemTimeSlotClock {
    genesis_seconds: u64,
    slot_duration_seconds: u64,
}

impl SystemTimeSlotClock {
    /// Create a new `SystemTimeSlotClock`.
    ///
    /// Returns an Error if `slot_duration_seconds == 0`.
    pub fn new(
        genesis_seconds: u64,
        slot_duration_seconds: u64,
    ) -> Result<SystemTimeSlotClock, Error> {
        if slot_duration_seconds == 0 {
            Err(Error::SlotDurationIsZero)
        } else {
            Ok(Self {
                genesis_seconds,
                slot_duration_seconds,
            })
        }
    }
}

impl SlotClock for SystemTimeSlotClock {
    type Error = Error;

    fn present_slot(&self) -> Result<Option<u64>, Error> {
        let syslot_time = SystemTime::now();
        let duration_since_epoch = syslot_time.duration_since(SystemTime::UNIX_EPOCH)?;
        let duration_since_genesis =
            duration_since_epoch.checked_sub(Duration::from_secs(self.genesis_seconds));
        match duration_since_genesis {
            None => Ok(None),
            Some(d) => Ok(slot_from_duration(self.slot_duration_seconds, d)),
        }
    }
}

impl From<SystemTimeError> for Error {
    fn from(e: SystemTimeError) -> Error {
        Error::SystemTimeError(format!("{:?}", e))
    }
}

fn slot_from_duration(slot_duration_seconds: u64, duration: Duration) -> Option<u64> {
    duration.as_secs().checked_div(slot_duration_seconds)
}

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

    /*
     * Note: these tests are using actual system times and could fail if they are executed on a
     * very slow machine.
     */
    #[test]
    fn test_slot_now() {
        let slot_time = 100;

        let now = SystemTime::now();
        let since_epoch = now.duration_since(SystemTime::UNIX_EPOCH).unwrap();

        let genesis = since_epoch.as_secs() - slot_time * 89;

        let clock = SystemTimeSlotClock {
            genesis_seconds: genesis,
            slot_duration_seconds: slot_time,
        };
        assert_eq!(clock.present_slot().unwrap(), Some(89));

        let clock = SystemTimeSlotClock {
            genesis_seconds: since_epoch.as_secs(),
            slot_duration_seconds: slot_time,
        };
        assert_eq!(clock.present_slot().unwrap(), Some(0));

        let clock = SystemTimeSlotClock {
            genesis_seconds: since_epoch.as_secs() - slot_time * 42 - 5,
            slot_duration_seconds: slot_time,
        };
        assert_eq!(clock.present_slot().unwrap(), Some(42));
    }

    #[test]
    fn test_slot_from_duration() {
        let slot_time = 100;

        assert_eq!(
            slot_from_duration(slot_time, Duration::from_secs(0)),
            Some(0)
        );
        assert_eq!(
            slot_from_duration(slot_time, Duration::from_secs(10)),
            Some(0)
        );
        assert_eq!(
            slot_from_duration(slot_time, Duration::from_secs(100)),
            Some(1)
        );
        assert_eq!(
            slot_from_duration(slot_time, Duration::from_secs(101)),
            Some(1)
        );
        assert_eq!(
            slot_from_duration(slot_time, Duration::from_secs(1000)),
            Some(10)
        );
    }

    #[test]
    fn test_slot_from_duration_slot_time_zero() {
        let slot_time = 0;

        assert_eq!(slot_from_duration(slot_time, Duration::from_secs(0)), None);
        assert_eq!(slot_from_duration(slot_time, Duration::from_secs(10)), None);
        assert_eq!(
            slot_from_duration(slot_time, Duration::from_secs(1000)),
            None
        );
    }
}
<commit_msg>Derive Clone for SystemTimeSlotClock<commit_after>use super::SlotClock;
use std::time::{Duration, SystemTime};

pub use std::time::SystemTimeError;

#[derive(Debug, PartialEq)]
pub enum Error {
    SlotDurationIsZero,
    SystemTimeError(String),
}

/// Determines the present slot based upon the present system time.
#[derive(Clone)]
pub struct SystemTimeSlotClock {
    genesis_seconds: u64,
    slot_duration_seconds: u64,
}

impl SystemTimeSlotClock {
    /// Create a new `SystemTimeSlotClock`.
    ///
    /// Returns an Error if `slot_duration_seconds == 0`.
    pub fn new(
        genesis_seconds: u64,
        slot_duration_seconds: u64,
    ) -> Result<SystemTimeSlotClock, Error> {
        if slot_duration_seconds == 0 {
            Err(Error::SlotDurationIsZero)
        } else {
            Ok(Self {
                genesis_seconds,
                slot_duration_seconds,
            })
        }
    }
}

impl SlotClock for SystemTimeSlotClock {
    type Error = Error;

    fn present_slot(&self) -> Result<Option<u64>, Error> {
        let syslot_time = SystemTime::now();
        let duration_since_epoch = syslot_time.duration_since(SystemTime::UNIX_EPOCH)?;
        let duration_since_genesis =
            duration_since_epoch.checked_sub(Duration::from_secs(self.genesis_seconds));
        match duration_since_genesis {
            None => Ok(None),
            Some(d) => Ok(slot_from_duration(self.slot_duration_seconds, d)),
        }
    }
}

impl From<SystemTimeError> for Error {
    fn from(e: SystemTimeError) -> Error {
        Error::SystemTimeError(format!("{:?}", e))
    }
}

fn slot_from_duration(slot_duration_seconds: u64, duration: Duration) -> Option<u64> {
    duration.as_secs().checked_div(slot_duration_seconds)
}

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

    /*
     * Note: these tests are using actual system times and could fail if they are executed on a
     * very slow machine.
     */
    #[test]
    fn test_slot_now() {
        let slot_time = 100;

        let now = SystemTime::now();
        let since_epoch = now.duration_since(SystemTime::UNIX_EPOCH).unwrap();

        let genesis = since_epoch.as_secs() - slot_time * 89;

        let clock = SystemTimeSlotClock {
            genesis_seconds: genesis,
            slot_duration_seconds: slot_time,
        };
        assert_eq!(clock.present_slot().unwrap(), Some(89));

        let clock = SystemTimeSlotClock {
            genesis_seconds: since_epoch.as_secs(),
            slot_duration_seconds: slot_time,
        };
        assert_eq!(clock.present_slot().unwrap(), Some(0));

        let clock = SystemTimeSlotClock {
            genesis_seconds: since_epoch.as_secs() - slot_time * 42 - 5,
            slot_duration_seconds: slot_time,
        };
        assert_eq!(clock.present_slot().unwrap(), Some(42));
    }

    #[test]
    fn test_slot_from_duration() {
        let slot_time = 100;

        assert_eq!(
            slot_from_duration(slot_time, Duration::from_secs(0)),
            Some(0)
        );
        assert_eq!(
            slot_from_duration(slot_time, Duration::from_secs(10)),
            Some(0)
        );
        assert_eq!(
            slot_from_duration(slot_time, Duration::from_secs(100)),
            Some(1)
        );
        assert_eq!(
            slot_from_duration(slot_time, Duration::from_secs(101)),
            Some(1)
        );
        assert_eq!(
            slot_from_duration(slot_time, Duration::from_secs(1000)),
            Some(10)
        );
    }

    #[test]
    fn test_slot_from_duration_slot_time_zero() {
        let slot_time = 0;

        assert_eq!(slot_from_duration(slot_time, Duration::from_secs(0)), None);
        assert_eq!(slot_from_duration(slot_time, Duration::from_secs(10)), None);
        assert_eq!(
            slot_from_duration(slot_time, Duration::from_secs(1000)),
            None
        );
    }
}
<|endoftext|>


<commit_before>//
//   Copyright 2016 Andrew Hunter
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//

//!
//! # Symbol range
//!
//! It's quite common to want to match ranges of symbols (most particularly, 'all' symbols but also things like all upper-case
//! characters). It would be inefficient to store all of the transitions represented by one of these transitions on a per-symbol
//! basis, and impossible for some symbol sets (consider a state machine working on u32s). Instead, transitions are stored as
//! symbol ranges.
//!

use std::cmp::*;

///
/// Represents a range of symbols
///
/// Symbols must be ordered in order to use them with a range-based state machine. Symbol ranges are inclusive unlike standard
/// Rust ranges - this allows them to represent the entire range of symbols instead of having a maximum coverage of 'all symbols
/// except the last one'.
///
#[derive(Clone, Eq, PartialEq)]
pub struct SymbolRange<Symbol: PartialOrd> {
    ///
    /// Lowest symbol in the range
    ///
    pub lowest: Symbol,

    ///
    /// Highest symbol in the range
    ///
    /// This is inclusive, so the highest symbol is always included in the range (this makes it differ from Rust's
    /// built-in Range struct, and is important for supporting uncountable symbols)
    ///
    pub highest: Symbol
}

impl<Symbol: PartialOrd> PartialOrd for SymbolRange<Symbol> {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        let lower = self.lowest.partial_cmp(&other.lowest);

        if lower == Some(Ordering::Equal) {
            self.highest.partial_cmp(&other.highest)
        } else {
            lower
        }
    }
}

impl<Symbol: PartialOrd> SymbolRange<Symbol> {
    ///
    /// Creates a new range covering everything between the specified two symbols
    ///
    #[inline]
    pub fn new(lowest: Symbol, highest: Symbol) -> SymbolRange<Symbol> {
        if lowest > highest {
            SymbolRange { lowest: highest, highest: lowest }
        } else {
            SymbolRange { lowest: lowest, highest: highest }
        }
    }

    ///
    /// True if this range overlaps another
    ///
    #[inline]
    pub fn overlaps(&self, with: &SymbolRange<Symbol>) -> bool {
        if self.highest < with.lowest {
            false
        } else if self.lowest > with.highest {
            false
        } else {
            true
        }
    }

    ///
    /// True if this range contains a symbol
    ///
    #[inline]
    pub fn includes(&self, symbol: &Symbol) -> bool {
        self.lowest < *symbol && *symbol <= self.highest
    }
}

impl<Symbol: PartialOrd+Clone> SymbolRange<Symbol> {
    ///
    /// Joins this range with another
    ///
    /// This creates a new range that covers all the symbols of both. If `overlaps()` is false for these two ranges, then
    /// the new range may cover additional symbols that are not in either range.
    ///
    pub fn join(&self, with: &SymbolRange<Symbol>) -> SymbolRange<Symbol> {
        SymbolRange {
            lowest:  if with.lowest<self.lowest   { with.lowest.clone()  } else { self.lowest.clone()  },
            highest: if with.highest<self.highest { self.highest.clone() } else { with.highest.clone() }
        }
    }
}

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

    #[test]
    fn can_create_range() {
        let range = SymbolRange::new(1, 2);

        assert!(range.lowest == 1);
        assert!(range.highest == 2);
    }

    #[test]
    fn can_create_range_reversed() {
        let range = SymbolRange::new(5, 1);

        assert!(range.lowest == 1);
        assert!(range.highest == 5);
    }

    #[test]
    fn overlaps_when_within() {
        assert!(SymbolRange::new(1, 4).overlaps(&SymbolRange::new(2, 3)));
    }

    #[test]
    fn overlaps_when_within_float() {
        assert!(SymbolRange::new(1.0, 4.0).overlaps(&SymbolRange::new(2.0, 3.0)));
    }

    #[test]
    fn overlaps_when_without() {
        assert!(SymbolRange::new(2, 3).overlaps(&SymbolRange::new(1, 4)));
    }

    #[test]
    fn overlaps_when_lower() {
        assert!(SymbolRange::new(1, 3).overlaps(&SymbolRange::new(2, 4)));
    }

    #[test]
    fn overlaps_when_higher() {
        assert!(SymbolRange::new(2, 4).overlaps(&SymbolRange::new(1, 3)));
    }

    #[test]
    fn overlaps_when_same() {
        assert!(SymbolRange::new(1, 4).overlaps(&SymbolRange::new(1, 4)));
    }

    #[test]
    fn does_not_overlap_lower() {
        assert!(!SymbolRange::new(1, 2).overlaps(&SymbolRange::new(4, 5)));
    }

    #[test]
    fn does_not_overlap_higher() {
        assert!(!SymbolRange::new(4, 5).overlaps(&SymbolRange::new(1, 2)));
    }

    #[test]
    fn join_left() {
        let joined = SymbolRange::new(1, 3).join(&SymbolRange::new(3, 4));

        assert!(joined.lowest == 1);
        assert!(joined.highest == 4);
    }

    #[test]
    fn join_right() {
        let joined = SymbolRange::new(3, 4).join(&SymbolRange::new(1, 3));

        assert!(joined.lowest == 1);
        assert!(joined.highest == 4);
    }

    #[test]
    fn join_overlap() {
        let joined = SymbolRange::new(1, 4).join(&SymbolRange::new(2, 3));

        assert!(joined.lowest == 1);
        assert!(joined.highest == 4);
    }
}
<commit_msg>Fix SymbolRange::includes and add tests<commit_after>//
//   Copyright 2016 Andrew Hunter
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//

//!
//! # Symbol range
//!
//! It's quite common to want to match ranges of symbols (most particularly, 'all' symbols but also things like all upper-case
//! characters). It would be inefficient to store all of the transitions represented by one of these transitions on a per-symbol
//! basis, and impossible for some symbol sets (consider a state machine working on u32s). Instead, transitions are stored as
//! symbol ranges.
//!

use std::cmp::*;

///
/// Represents a range of symbols
///
/// Symbols must be ordered in order to use them with a range-based state machine. Symbol ranges are inclusive unlike standard
/// Rust ranges - this allows them to represent the entire range of symbols instead of having a maximum coverage of 'all symbols
/// except the last one'.
///
#[derive(Clone, Eq, PartialEq)]
pub struct SymbolRange<Symbol: PartialOrd> {
    ///
    /// Lowest symbol in the range
    ///
    pub lowest: Symbol,

    ///
    /// Highest symbol in the range
    ///
    /// This is inclusive, so the highest symbol is always included in the range (this makes it differ from Rust's
    /// built-in Range struct, and is important for supporting uncountable symbols)
    ///
    pub highest: Symbol
}

impl<Symbol: PartialOrd> PartialOrd for SymbolRange<Symbol> {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        let lower = self.lowest.partial_cmp(&other.lowest);

        if lower == Some(Ordering::Equal) {
            self.highest.partial_cmp(&other.highest)
        } else {
            lower
        }
    }
}

impl<Symbol: PartialOrd> SymbolRange<Symbol> {
    ///
    /// Creates a new range covering everything between the specified two symbols
    ///
    #[inline]
    pub fn new(lowest: Symbol, highest: Symbol) -> SymbolRange<Symbol> {
        if lowest > highest {
            SymbolRange { lowest: highest, highest: lowest }
        } else {
            SymbolRange { lowest: lowest, highest: highest }
        }
    }

    ///
    /// True if this range overlaps another
    ///
    #[inline]
    pub fn overlaps(&self, with: &SymbolRange<Symbol>) -> bool {
        if self.highest < with.lowest {
            false
        } else if self.lowest > with.highest {
            false
        } else {
            true
        }
    }

    ///
    /// True if this range contains a symbol
    ///
    #[inline]
    pub fn includes(&self, symbol: &Symbol) -> bool {
        self.lowest <= *symbol && *symbol <= self.highest
    }
}

impl<Symbol: PartialOrd+Clone> SymbolRange<Symbol> {
    ///
    /// Joins this range with another
    ///
    /// This creates a new range that covers all the symbols of both. If `overlaps()` is false for these two ranges, then
    /// the new range may cover additional symbols that are not in either range.
    ///
    pub fn join(&self, with: &SymbolRange<Symbol>) -> SymbolRange<Symbol> {
        SymbolRange {
            lowest:  if with.lowest<self.lowest   { with.lowest.clone()  } else { self.lowest.clone()  },
            highest: if with.highest<self.highest { self.highest.clone() } else { with.highest.clone() }
        }
    }
}

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

    #[test]
    fn can_create_range() {
        let range = SymbolRange::new(1, 2);

        assert!(range.lowest == 1);
        assert!(range.highest == 2);
    }

    #[test]
    fn can_create_range_reversed() {
        let range = SymbolRange::new(5, 1);

        assert!(range.lowest == 1);
        assert!(range.highest == 5);
    }

    #[test]
    fn overlaps_when_within() {
        assert!(SymbolRange::new(1, 4).overlaps(&SymbolRange::new(2, 3)));
    }

    #[test]
    fn overlaps_when_within_float() {
        assert!(SymbolRange::new(1.0, 4.0).overlaps(&SymbolRange::new(2.0, 3.0)));
    }

    #[test]
    fn overlaps_when_without() {
        assert!(SymbolRange::new(2, 3).overlaps(&SymbolRange::new(1, 4)));
    }

    #[test]
    fn overlaps_when_lower() {
        assert!(SymbolRange::new(1, 3).overlaps(&SymbolRange::new(2, 4)));
    }

    #[test]
    fn overlaps_when_higher() {
        assert!(SymbolRange::new(2, 4).overlaps(&SymbolRange::new(1, 3)));
    }

    #[test]
    fn overlaps_when_same() {
        assert!(SymbolRange::new(1, 4).overlaps(&SymbolRange::new(1, 4)));
    }

    #[test]
    fn does_not_overlap_lower() {
        assert!(!SymbolRange::new(1, 2).overlaps(&SymbolRange::new(4, 5)));
    }

    #[test]
    fn does_not_overlap_higher() {
        assert!(!SymbolRange::new(4, 5).overlaps(&SymbolRange::new(1, 2)));
    }

    #[test]
    fn join_left() {
        let joined = SymbolRange::new(1, 3).join(&SymbolRange::new(3, 4));

        assert!(joined.lowest == 1);
        assert!(joined.highest == 4);
    }

    #[test]
    fn join_right() {
        let joined = SymbolRange::new(3, 4).join(&SymbolRange::new(1, 3));

        assert!(joined.lowest == 1);
        assert!(joined.highest == 4);
    }

    #[test]
    fn join_overlap() {
        let joined = SymbolRange::new(1, 4).join(&SymbolRange::new(2, 3));

        assert!(joined.lowest == 1);
        assert!(joined.highest == 4);
    }

    #[test]
    fn includes_single_item() {
        let just_zero = SymbolRange::new(0,0);
        assert!(just_zero.includes(&0));
    }

    #[test]
    fn includes_mid_item() {
        let just_zero = SymbolRange::new(1,4);
        assert!(just_zero.includes(&2));
    }

    #[test]
    fn includes_first_item() {
        let just_zero = SymbolRange::new(1,4);
        assert!(just_zero.includes(&1));
    }

    #[test]
    fn includes_last_item() {
        let just_zero = SymbolRange::new(1,4);
        assert!(just_zero.includes(&4));
    }

    #[test]
    fn excludes_lower_item() {
        let just_zero = SymbolRange::new(1,4);
        assert!(!just_zero.includes(&0));
    }
    #[test]
    fn excludes_higher_item() {
        let just_zero = SymbolRange::new(1,4);
        assert!(!just_zero.includes(&5));
    }
}
<|endoftext|>


<commit_before>//! Simple daemon
//!
//! This daemon will start web server, track new packages and build them

use std::{env, thread};
use std::panic::{catch_unwind, AssertUnwindSafe};
use std::process::exit;
use std::fs::File;
use std::io::Write;
use std::time::Duration;
use std::path::PathBuf;
use libc::fork;
use time;
use docbuilder::RustwideBuilder;
use DocBuilderOptions;
use DocBuilder;
use utils::{update_release_activity, github_updater, pubsubhubbub};
use db::{connect_db, update_search_index};

pub fn start_daemon() {
    // first check required environment variables
    for v in ["CRATESFYI_PREFIX",
              "CRATESFYI_PREFIX",
              "CRATESFYI_GITHUB_USERNAME",
              "CRATESFYI_GITHUB_ACCESSTOKEN"]
        .iter() {
        env::var(v).expect("Environment variable not found");
    }

    let dbopts = opts();

    // check paths once
    dbopts.check_paths().unwrap();

    // fork the process
    let pid = unsafe { fork() };
    if pid > 0 {
        let mut file = File::create(dbopts.prefix.join("cratesfyi.pid"))
            .expect("Failed to create pid file");
        writeln!(&mut file, "{}", pid).expect("Failed to write pid");

        info!("cratesfyi {} daemon started on: {}", ::BUILD_VERSION, pid);
        exit(0);
    }

    // check new crates every minute
    thread::Builder::new().name("crates.io reader".to_string()).spawn(move || {
        // space this out to prevent it from clashing against the queue-builder thread on launch
        thread::sleep(Duration::from_secs(30));
        loop {
            let opts = opts();
            let mut doc_builder = DocBuilder::new(opts);

            if doc_builder.is_locked() {
                debug!("Lock file exists, skipping checking new crates");
            } else {
                debug!("Checking new crates");
                match doc_builder.get_new_crates() {
                    Ok(n) => debug!("{} crates added to queue", n),
                    Err(e) => error!("Failed to get new crates: {}", e),
                }
            }

            thread::sleep(Duration::from_secs(60));
        }
    }).unwrap();

    // build new crates every minute
    thread::Builder::new().name("build queue reader".to_string()).spawn(move || {
        let opts = opts();
        let mut doc_builder = DocBuilder::new(opts);

        /// Represents the current state of the builder thread.
        enum BuilderState {
            /// The builder thread has just started, and hasn't built any crates yet.
            Fresh,
            /// The builder has just seen an empty build queue.
            EmptyQueue,
            /// The builder has just seen the lock file.
            Locked,
            /// The builder has just finished building a crate. The enclosed count is the number of
            /// crates built since the caches have been refreshed.
            QueueInProgress(usize),
        }

        let mut builder = RustwideBuilder::init().unwrap();

        let mut status = BuilderState::Fresh;

        loop {
            if !status.is_in_progress() {
                thread::sleep(Duration::from_secs(60));
            }

            // check lock file
            if doc_builder.is_locked() {
                warn!("Lock file exits, skipping building new crates");
                status = BuilderState::Locked;
                continue;
            }

            if status.count() >= 10 {
                // periodically, we need to flush our caches and ping the hubs
                debug!("10 builds in a row; flushing caches");
                status = BuilderState::QueueInProgress(0);

                match pubsubhubbub::ping_hubs() {
                    Err(e) => error!("Failed to ping hub: {}", e),
                    Ok(n) => debug!("Succesfully pinged {} hubs", n)
                }

                if let Err(e) = doc_builder.load_cache() {
                    error!("Failed to load cache: {}", e);
                }

                if let Err(e) = doc_builder.save_cache() {
                    error!("Failed to save cache: {}", e);
                }
            }

            // Only build crates if there are any to build
            debug!("Checking build queue");
            match doc_builder.get_queue_count() {
                Err(e) => {
                    error!("Failed to read the number of crates in the queue: {}", e);
                    continue;
                }
                Ok(0) => {
                    if status.count() > 0 {
                        // ping the hubs before continuing
                        match pubsubhubbub::ping_hubs() {
                            Err(e) => error!("Failed to ping hub: {}", e),
                            Ok(n) => debug!("Succesfully pinged {} hubs", n)
                        }

                        if let Err(e) = doc_builder.save_cache() {
                            error!("Failed to save cache: {}", e);
                        }
                    }
                    debug!("Queue is empty, going back to sleep");
                    status = BuilderState::EmptyQueue;
                    continue;
                }
                Ok(queue_count) => {
                    info!("Starting build with {} crates in queue (currently on a {} crate streak)",
                          queue_count, status.count());
                }
            }

            // if we're starting a new batch, reload our caches and sources
            if !status.is_in_progress() {
                if let Err(e) = doc_builder.load_cache() {
                    error!("Failed to load cache: {}", e);
                    continue;
                }
            }

            // Run build_packages_queue under `catch_unwind` to catch panics
            // This only panicked twice in the last 6 months but its just a better
            // idea to do this.
            let res = catch_unwind(AssertUnwindSafe(|| {
                match doc_builder.build_next_queue_package(&mut builder) {
                    Err(e) => error!("Failed to build crate from queue: {}", e),
                    Ok(crate_built) => if crate_built {
                        status.increment();
                    }

                }
            }));

            if let Err(e) = res {
                error!("GRAVE ERROR Building new crates panicked: {:?}", e);
            }
        }

        impl BuilderState {
            fn count(&self) -> usize {
                match *self {
                    BuilderState::QueueInProgress(n) => n,
                    _ => 0,
                }
            }

            fn is_in_progress(&self) -> bool {
                match *self {
                    BuilderState::QueueInProgress(_) => true,
                    _ => false,
                }
            }

            fn increment(&mut self) {
                *self = BuilderState::QueueInProgress(self.count() + 1);
            }
        }
    }).unwrap();

    // update release activity everyday at 23:55
    thread::Builder::new().name("release activity updater".to_string()).spawn(move || {
        loop {
            thread::sleep(Duration::from_secs(60));
            let now = time::now();
            if now.tm_hour == 23 && now.tm_min == 55 {
                info!("Updating release activity");
                if let Err(e) = update_release_activity() {
                    error!("Failed to update release activity: {}", e);
                }
            }
        }
    }).unwrap();

    // update search index every 3 hours
    thread::Builder::new().name("search index updater".to_string()).spawn(move || {
        loop {
            thread::sleep(Duration::from_secs(60 * 60 * 3));
            let conn = connect_db().expect("Failed to connect database");
            if let Err(e) = update_search_index(&conn) {
                error!("Failed to update search index: {}", e);
            }
        }
    }).unwrap();

    // update github stats every 6 hours
    thread::Builder::new().name("github stat updater".to_string()).spawn(move || {
        loop {
            thread::sleep(Duration::from_secs(60 * 60 * 6));
            if let Err(e) = github_updater() {
                error!("Failed to update github fields: {}", e);
            }
        }
    }).unwrap();

    // TODO: update ssl certificate every 3 months

    // at least start web server
    info!("Starting web server");
    ::start_web_server(None);
}

fn opts() -> DocBuilderOptions {
    let prefix = PathBuf::from(env::var("CRATESFYI_PREFIX")
        .expect("CRATESFYI_PREFIX environment variable not found"));
    DocBuilderOptions::from_prefix(prefix)
}
<commit_msg>show which environment variable is missing in the daemon<commit_after>//! Simple daemon
//!
//! This daemon will start web server, track new packages and build them

use std::{env, thread};
use std::panic::{catch_unwind, AssertUnwindSafe};
use std::process::exit;
use std::fs::File;
use std::io::Write;
use std::time::Duration;
use std::path::PathBuf;
use libc::fork;
use time;
use docbuilder::RustwideBuilder;
use DocBuilderOptions;
use DocBuilder;
use utils::{update_release_activity, github_updater, pubsubhubbub};
use db::{connect_db, update_search_index};

pub fn start_daemon() {
    // first check required environment variables
    for v in ["CRATESFYI_PREFIX",
              "CRATESFYI_PREFIX",
              "CRATESFYI_GITHUB_USERNAME",
              "CRATESFYI_GITHUB_ACCESSTOKEN"]
        .iter() {
        env::var(v).expect(&format!("Environment variable {} not found", v));
    }

    let dbopts = opts();

    // check paths once
    dbopts.check_paths().unwrap();

    // fork the process
    let pid = unsafe { fork() };
    if pid > 0 {
        let mut file = File::create(dbopts.prefix.join("cratesfyi.pid"))
            .expect("Failed to create pid file");
        writeln!(&mut file, "{}", pid).expect("Failed to write pid");

        info!("cratesfyi {} daemon started on: {}", ::BUILD_VERSION, pid);
        exit(0);
    }

    // check new crates every minute
    thread::Builder::new().name("crates.io reader".to_string()).spawn(move || {
        // space this out to prevent it from clashing against the queue-builder thread on launch
        thread::sleep(Duration::from_secs(30));
        loop {
            let opts = opts();
            let mut doc_builder = DocBuilder::new(opts);

            if doc_builder.is_locked() {
                debug!("Lock file exists, skipping checking new crates");
            } else {
                debug!("Checking new crates");
                match doc_builder.get_new_crates() {
                    Ok(n) => debug!("{} crates added to queue", n),
                    Err(e) => error!("Failed to get new crates: {}", e),
                }
            }

            thread::sleep(Duration::from_secs(60));
        }
    }).unwrap();

    // build new crates every minute
    thread::Builder::new().name("build queue reader".to_string()).spawn(move || {
        let opts = opts();
        let mut doc_builder = DocBuilder::new(opts);

        /// Represents the current state of the builder thread.
        enum BuilderState {
            /// The builder thread has just started, and hasn't built any crates yet.
            Fresh,
            /// The builder has just seen an empty build queue.
            EmptyQueue,
            /// The builder has just seen the lock file.
            Locked,
            /// The builder has just finished building a crate. The enclosed count is the number of
            /// crates built since the caches have been refreshed.
            QueueInProgress(usize),
        }

        let mut builder = RustwideBuilder::init().unwrap();

        let mut status = BuilderState::Fresh;

        loop {
            if !status.is_in_progress() {
                thread::sleep(Duration::from_secs(60));
            }

            // check lock file
            if doc_builder.is_locked() {
                warn!("Lock file exits, skipping building new crates");
                status = BuilderState::Locked;
                continue;
            }

            if status.count() >= 10 {
                // periodically, we need to flush our caches and ping the hubs
                debug!("10 builds in a row; flushing caches");
                status = BuilderState::QueueInProgress(0);

                match pubsubhubbub::ping_hubs() {
                    Err(e) => error!("Failed to ping hub: {}", e),
                    Ok(n) => debug!("Succesfully pinged {} hubs", n)
                }

                if let Err(e) = doc_builder.load_cache() {
                    error!("Failed to load cache: {}", e);
                }

                if let Err(e) = doc_builder.save_cache() {
                    error!("Failed to save cache: {}", e);
                }
            }

            // Only build crates if there are any to build
            debug!("Checking build queue");
            match doc_builder.get_queue_count() {
                Err(e) => {
                    error!("Failed to read the number of crates in the queue: {}", e);
                    continue;
                }
                Ok(0) => {
                    if status.count() > 0 {
                        // ping the hubs before continuing
                        match pubsubhubbub::ping_hubs() {
                            Err(e) => error!("Failed to ping hub: {}", e),
                            Ok(n) => debug!("Succesfully pinged {} hubs", n)
                        }

                        if let Err(e) = doc_builder.save_cache() {
                            error!("Failed to save cache: {}", e);
                        }
                    }
                    debug!("Queue is empty, going back to sleep");
                    status = BuilderState::EmptyQueue;
                    continue;
                }
                Ok(queue_count) => {
                    info!("Starting build with {} crates in queue (currently on a {} crate streak)",
                          queue_count, status.count());
                }
            }

            // if we're starting a new batch, reload our caches and sources
            if !status.is_in_progress() {
                if let Err(e) = doc_builder.load_cache() {
                    error!("Failed to load cache: {}", e);
                    continue;
                }
            }

            // Run build_packages_queue under `catch_unwind` to catch panics
            // This only panicked twice in the last 6 months but its just a better
            // idea to do this.
            let res = catch_unwind(AssertUnwindSafe(|| {
                match doc_builder.build_next_queue_package(&mut builder) {
                    Err(e) => error!("Failed to build crate from queue: {}", e),
                    Ok(crate_built) => if crate_built {
                        status.increment();
                    }

                }
            }));

            if let Err(e) = res {
                error!("GRAVE ERROR Building new crates panicked: {:?}", e);
            }
        }

        impl BuilderState {
            fn count(&self) -> usize {
                match *self {
                    BuilderState::QueueInProgress(n) => n,
                    _ => 0,
                }
            }

            fn is_in_progress(&self) -> bool {
                match *self {
                    BuilderState::QueueInProgress(_) => true,
                    _ => false,
                }
            }

            fn increment(&mut self) {
                *self = BuilderState::QueueInProgress(self.count() + 1);
            }
        }
    }).unwrap();

    // update release activity everyday at 23:55
    thread::Builder::new().name("release activity updater".to_string()).spawn(move || {
        loop {
            thread::sleep(Duration::from_secs(60));
            let now = time::now();
            if now.tm_hour == 23 && now.tm_min == 55 {
                info!("Updating release activity");
                if let Err(e) = update_release_activity() {
                    error!("Failed to update release activity: {}", e);
                }
            }
        }
    }).unwrap();

    // update search index every 3 hours
    thread::Builder::new().name("search index updater".to_string()).spawn(move || {
        loop {
            thread::sleep(Duration::from_secs(60 * 60 * 3));
            let conn = connect_db().expect("Failed to connect database");
            if let Err(e) = update_search_index(&conn) {
                error!("Failed to update search index: {}", e);
            }
        }
    }).unwrap();

    // update github stats every 6 hours
    thread::Builder::new().name("github stat updater".to_string()).spawn(move || {
        loop {
            thread::sleep(Duration::from_secs(60 * 60 * 6));
            if let Err(e) = github_updater() {
                error!("Failed to update github fields: {}", e);
            }
        }
    }).unwrap();

    // TODO: update ssl certificate every 3 months

    // at least start web server
    info!("Starting web server");
    ::start_web_server(None);
}

fn opts() -> DocBuilderOptions {
    let prefix = PathBuf::from(env::var("CRATESFYI_PREFIX")
        .expect("CRATESFYI_PREFIX environment variable not found"));
    DocBuilderOptions::from_prefix(prefix)
}
<|endoftext|>