<commit_before>use crate::iter::{DoubleEndedIterator, FusedIterator, Iterator, TrustedLen};
use crate::ops::Try;
use crate::usize;

/// An iterator that links two iterators together, in a chain.
///
/// This `struct` is created by [`Iterator::chain`]. See its documentation
/// for more.
#[derive(Clone, Debug)]
#[must_use = "iterators are lazy and do nothing unless consumed"]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct Chain<A, B> {
    // These are "fused" with `Option` so we don't need separate state to track which part is
    // already exhausted, and we may also get niche layout for `None`. We don't use the real `Fuse`
    // adapter because its specialization for `FusedIterator` unconditionally descends into the
    // iterator, and that could be expensive to keep revisiting stuff like nested chains. It also
    // hurts compiler performance to add more iterator layers to `Chain`.
    //
    // Only the "first" iterator is actually set `None` when exhausted, depending on whether you
    // iterate forward or backward. If you mix directions, then both sides may be `None`.
    a: Option<A>,
    b: Option<B>,
}
impl<A, B> Chain<A, B> {
    pub(in super::super) fn new(a: A, b: B) -> Chain<A, B> {
        Chain { a: Some(a), b: Some(b) }
    }
}

/// Fuse the iterator if the expression is `None`.
macro_rules! fuse {
    ($self:ident . $iter:ident . $($call:tt)+) => {
        match $self.$iter {
            Some(ref mut iter) => match iter.$($call)+ {
                None => {
                    $self.$iter = None;
                    None
                }
                item => item,
            },
            None => None,
        }
    };
}

/// Try an iterator method without fusing,
/// like an inline `.as_mut().and_then(...)`
macro_rules! maybe {
    ($self:ident . $iter:ident . $($call:tt)+) => {
        match $self.$iter {
            Some(ref mut iter) => iter.$($call)+,
            None => None,
        }
    };
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<A, B> Iterator for Chain<A, B>
where
    A: Iterator,
    B: Iterator<Item = A::Item>,
{
    type Item = A::Item;

    #[inline]
    fn next(&mut self) -> Option<A::Item> {
        match fuse!(self.a.next()) {
            None => maybe!(self.b.next()),
            item => item,
        }
    }

    #[inline]
    #[rustc_inherit_overflow_checks]
    fn count(self) -> usize {
        let a_count = match self.a {
            Some(a) => a.count(),
            None => 0,
        };
        let b_count = match self.b {
            Some(b) => b.count(),
            None => 0,
        };
        a_count + b_count
    }

    fn try_fold<Acc, F, R>(&mut self, mut acc: Acc, mut f: F) -> R
    where
        Self: Sized,
        F: FnMut(Acc, Self::Item) -> R,
        R: Try<Ok = Acc>,
    {
        if let Some(ref mut a) = self.a {
            acc = a.try_fold(acc, &mut f)?;
            self.a = None;
        }
        if let Some(ref mut b) = self.b {
            acc = b.try_fold(acc, f)?;
            // we don't fuse the second iterator
        }
        Try::from_ok(acc)
    }

    fn fold<Acc, F>(self, mut acc: Acc, mut f: F) -> Acc
    where
        F: FnMut(Acc, Self::Item) -> Acc,
    {
        if let Some(a) = self.a {
            acc = a.fold(acc, &mut f);
        }
        if let Some(b) = self.b {
            acc = b.fold(acc, f);
        }
        acc
    }

    #[inline]
    fn nth(&mut self, mut n: usize) -> Option<A::Item> {
        if let Some(ref mut a) = self.a {
            while let Some(x) = a.next() {
                if n == 0 {
                    return Some(x);
                }
                n -= 1;
            }
            self.a = None;
        }
        maybe!(self.b.nth(n))
    }

    #[inline]
    fn find<P>(&mut self, mut predicate: P) -> Option<Self::Item>
    where
        P: FnMut(&Self::Item) -> bool,
    {
        match fuse!(self.a.find(&mut predicate)) {
            None => maybe!(self.b.find(predicate)),
            item => item,
        }
    }

    #[inline]
    fn last(self) -> Option<A::Item> {
        // Must exhaust a before b.
        let a_last = match self.a {
            Some(a) => a.last(),
            None => None,
        };
        let b_last = match self.b {
            Some(b) => b.last(),
            None => None,
        };
        b_last.or(a_last)
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        match self {
            Chain { a: Some(a), b: Some(b) } => {
                let (a_lower, a_upper) = a.size_hint();
                let (b_lower, b_upper) = b.size_hint();

                let lower = a_lower.saturating_add(b_lower);

                let upper = match (a_upper, b_upper) {
                    (Some(x), Some(y)) => x.checked_add(y),
                    _ => None,
                };

                (lower, upper)
            }
            Chain { a: Some(a), b: None } => a.size_hint(),
            Chain { a: None, b: Some(b) } => b.size_hint(),
            Chain { a: None, b: None } => (0, Some(0)),
        }
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<A, B> DoubleEndedIterator for Chain<A, B>
where
    A: DoubleEndedIterator,
    B: DoubleEndedIterator<Item = A::Item>,
{
    #[inline]
    fn next_back(&mut self) -> Option<A::Item> {
        match fuse!(self.b.next_back()) {
            None => maybe!(self.a.next_back()),
            item => item,
        }
    }

    #[inline]
    fn nth_back(&mut self, mut n: usize) -> Option<A::Item> {
        if let Some(ref mut b) = self.b {
            while let Some(x) = b.next_back() {
                if n == 0 {
                    return Some(x);
                }
                n -= 1;
            }
            self.b = None;
        }
        maybe!(self.a.nth_back(n))
    }

    #[inline]
    fn rfind<P>(&mut self, mut predicate: P) -> Option<Self::Item>
    where
        P: FnMut(&Self::Item) -> bool,
    {
        match fuse!(self.b.rfind(&mut predicate)) {
            None => maybe!(self.a.rfind(predicate)),
            item => item,
        }
    }

    fn try_rfold<Acc, F, R>(&mut self, mut acc: Acc, mut f: F) -> R
    where
        Self: Sized,
        F: FnMut(Acc, Self::Item) -> R,
        R: Try<Ok = Acc>,
    {
        if let Some(ref mut b) = self.b {
            acc = b.try_rfold(acc, &mut f)?;
            self.b = None;
        }
        if let Some(ref mut a) = self.a {
            acc = a.try_rfold(acc, f)?;
            // we don't fuse the second iterator
        }
        Try::from_ok(acc)
    }

    fn rfold<Acc, F>(self, mut acc: Acc, mut f: F) -> Acc
    where
        F: FnMut(Acc, Self::Item) -> Acc,
    {
        if let Some(b) = self.b {
            acc = b.rfold(acc, &mut f);
        }
        if let Some(a) = self.a {
            acc = a.rfold(acc, f);
        }
        acc
    }
}

// Note: *both* must be fused to handle double-ended iterators.
#[stable(feature = "fused", since = "1.26.0")]
impl<A, B> FusedIterator for Chain<A, B>
where
    A: FusedIterator,
    B: FusedIterator<Item = A::Item>,
{
}

#[unstable(feature = "trusted_len", issue = "37572")]
unsafe impl<A, B> TrustedLen for Chain<A, B>
where
    A: TrustedLen,
    B: TrustedLen<Item = A::Item>,
{
}
<commit_msg>Implement iter::Chain::{advance_by, advance_back_by}<commit_after>use crate::iter::{DoubleEndedIterator, FusedIterator, Iterator, TrustedLen};
use crate::ops::Try;
use crate::usize;

/// An iterator that links two iterators together, in a chain.
///
/// This `struct` is created by [`Iterator::chain`]. See its documentation
/// for more.
#[derive(Clone, Debug)]
#[must_use = "iterators are lazy and do nothing unless consumed"]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct Chain<A, B> {
    // These are "fused" with `Option` so we don't need separate state to track which part is
    // already exhausted, and we may also get niche layout for `None`. We don't use the real `Fuse`
    // adapter because its specialization for `FusedIterator` unconditionally descends into the
    // iterator, and that could be expensive to keep revisiting stuff like nested chains. It also
    // hurts compiler performance to add more iterator layers to `Chain`.
    //
    // Only the "first" iterator is actually set `None` when exhausted, depending on whether you
    // iterate forward or backward. If you mix directions, then both sides may be `None`.
    a: Option<A>,
    b: Option<B>,
}
impl<A, B> Chain<A, B> {
    pub(in super::super) fn new(a: A, b: B) -> Chain<A, B> {
        Chain { a: Some(a), b: Some(b) }
    }
}

/// Fuse the iterator if the expression is `None`.
macro_rules! fuse {
    ($self:ident . $iter:ident . $($call:tt)+) => {
        match $self.$iter {
            Some(ref mut iter) => match iter.$($call)+ {
                None => {
                    $self.$iter = None;
                    None
                }
                item => item,
            },
            None => None,
        }
    };
}

/// Try an iterator method without fusing,
/// like an inline `.as_mut().and_then(...)`
macro_rules! maybe {
    ($self:ident . $iter:ident . $($call:tt)+) => {
        match $self.$iter {
            Some(ref mut iter) => iter.$($call)+,
            None => None,
        }
    };
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<A, B> Iterator for Chain<A, B>
where
    A: Iterator,
    B: Iterator<Item = A::Item>,
{
    type Item = A::Item;

    #[inline]
    fn next(&mut self) -> Option<A::Item> {
        match fuse!(self.a.next()) {
            None => maybe!(self.b.next()),
            item => item,
        }
    }

    #[inline]
    #[rustc_inherit_overflow_checks]
    fn count(self) -> usize {
        let a_count = match self.a {
            Some(a) => a.count(),
            None => 0,
        };
        let b_count = match self.b {
            Some(b) => b.count(),
            None => 0,
        };
        a_count + b_count
    }

    fn try_fold<Acc, F, R>(&mut self, mut acc: Acc, mut f: F) -> R
    where
        Self: Sized,
        F: FnMut(Acc, Self::Item) -> R,
        R: Try<Ok = Acc>,
    {
        if let Some(ref mut a) = self.a {
            acc = a.try_fold(acc, &mut f)?;
            self.a = None;
        }
        if let Some(ref mut b) = self.b {
            acc = b.try_fold(acc, f)?;
            // we don't fuse the second iterator
        }
        Try::from_ok(acc)
    }

    fn fold<Acc, F>(self, mut acc: Acc, mut f: F) -> Acc
    where
        F: FnMut(Acc, Self::Item) -> Acc,
    {
        if let Some(a) = self.a {
            acc = a.fold(acc, &mut f);
        }
        if let Some(b) = self.b {
            acc = b.fold(acc, f);
        }
        acc
    }

    #[inline]
    fn advance_by(&mut self, n: usize) -> Result<(), usize> {
        let mut rem = n;

        if let Some(ref mut a) = self.a {
            match a.advance_by(rem) {
                Ok(()) => return Ok(()),
                Err(k) => rem -= k,
            }
            self.a = None;
        }

        if let Some(ref mut b) = self.b {
            match b.advance_by(rem) {
                Ok(()) => return Ok(()),
                Err(k) => rem -= k,
            }
            // we don't fuse the second iterator
        }

        if rem == 0 { Ok(()) } else { Err(n - rem) }
    }

    #[inline]
    fn nth(&mut self, mut n: usize) -> Option<Self::Item> {
        if let Some(ref mut a) = self.a {
            match a.advance_by(n) {
                Ok(()) => match a.next() {
                    None => n = 0,
                    x => return x,
                },
                Err(k) => n -= k,
            }

            self.a = None;
        }

        maybe!(self.b.nth(n))
    }

    #[inline]
    fn find<P>(&mut self, mut predicate: P) -> Option<Self::Item>
    where
        P: FnMut(&Self::Item) -> bool,
    {
        match fuse!(self.a.find(&mut predicate)) {
            None => maybe!(self.b.find(predicate)),
            item => item,
        }
    }

    #[inline]
    fn last(self) -> Option<A::Item> {
        // Must exhaust a before b.
        let a_last = match self.a {
            Some(a) => a.last(),
            None => None,
        };
        let b_last = match self.b {
            Some(b) => b.last(),
            None => None,
        };
        b_last.or(a_last)
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        match self {
            Chain { a: Some(a), b: Some(b) } => {
                let (a_lower, a_upper) = a.size_hint();
                let (b_lower, b_upper) = b.size_hint();

                let lower = a_lower.saturating_add(b_lower);

                let upper = match (a_upper, b_upper) {
                    (Some(x), Some(y)) => x.checked_add(y),
                    _ => None,
                };

                (lower, upper)
            }
            Chain { a: Some(a), b: None } => a.size_hint(),
            Chain { a: None, b: Some(b) } => b.size_hint(),
            Chain { a: None, b: None } => (0, Some(0)),
        }
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<A, B> DoubleEndedIterator for Chain<A, B>
where
    A: DoubleEndedIterator,
    B: DoubleEndedIterator<Item = A::Item>,
{
    #[inline]
    fn next_back(&mut self) -> Option<A::Item> {
        match fuse!(self.b.next_back()) {
            None => maybe!(self.a.next_back()),
            item => item,
        }
    }

    #[inline]
    fn advance_back_by(&mut self, n: usize) -> Result<(), usize> {
        let mut rem = n;

        if let Some(ref mut b) = self.b {
            match b.advance_back_by(rem) {
                Ok(()) => return Ok(()),
                Err(k) => rem -= k,
            }
            self.b = None;
        }

        if let Some(ref mut a) = self.a {
            match a.advance_back_by(rem) {
                Ok(()) => return Ok(()),
                Err(k) => rem -= k,
            }
            // we don't fuse the second iterator
        }

        if rem == 0 { Ok(()) } else { Err(n - rem) }
    }

    #[inline]
    fn nth_back(&mut self, mut n: usize) -> Option<Self::Item> {
        if let Some(ref mut b) = self.b {
            match b.advance_back_by(n) {
                Ok(()) => match b.next_back() {
                    None => n = 0,
                    x => return x,
                },
                Err(k) => n -= k,
            }

            self.b = None;
        }

        maybe!(self.a.nth_back(n))
    }

    #[inline]
    fn rfind<P>(&mut self, mut predicate: P) -> Option<Self::Item>
    where
        P: FnMut(&Self::Item) -> bool,
    {
        match fuse!(self.b.rfind(&mut predicate)) {
            None => maybe!(self.a.rfind(predicate)),
            item => item,
        }
    }

    fn try_rfold<Acc, F, R>(&mut self, mut acc: Acc, mut f: F) -> R
    where
        Self: Sized,
        F: FnMut(Acc, Self::Item) -> R,
        R: Try<Ok = Acc>,
    {
        if let Some(ref mut b) = self.b {
            acc = b.try_rfold(acc, &mut f)?;
            self.b = None;
        }
        if let Some(ref mut a) = self.a {
            acc = a.try_rfold(acc, f)?;
            // we don't fuse the second iterator
        }
        Try::from_ok(acc)
    }

    fn rfold<Acc, F>(self, mut acc: Acc, mut f: F) -> Acc
    where
        F: FnMut(Acc, Self::Item) -> Acc,
    {
        if let Some(b) = self.b {
            acc = b.rfold(acc, &mut f);
        }
        if let Some(a) = self.a {
            acc = a.rfold(acc, f);
        }
        acc
    }
}

// Note: *both* must be fused to handle double-ended iterators.
#[stable(feature = "fused", since = "1.26.0")]
impl<A, B> FusedIterator for Chain<A, B>
where
    A: FusedIterator,
    B: FusedIterator<Item = A::Item>,
{
}

#[unstable(feature = "trusted_len", issue = "37572")]
unsafe impl<A, B> TrustedLen for Chain<A, B>
where
    A: TrustedLen,
    B: TrustedLen<Item = A::Item>,
{
}
<|endoftext|>


<commit_before>// shamelessly taken from https://github.com/nlopes/actix-web-prom
// we are unfortunatly not able to directly use this great crate,
// because  we want to use the name of the endpoint for retrocompatibility
// (and as a side effect we also added the 'in flight' queries (but for this we could have used the Registry))

use actix_service::{Service, Transform};
use actix_web::{
    dev::{Body, BodySize, MessageBody, ResponseBody, ServiceRequest, ServiceResponse},
    http::{Method, StatusCode},
    web::Bytes,
    Error,
};
use futures::future::{ok, FutureResult};
use futures::{Async, Future, Poll};
use prometheus::{self, Encoder, TextEncoder};
use std::marker::PhantomData;
use std::sync::Arc;
use std::time::SystemTime;

lazy_static::lazy_static! {
    static ref PATH_TO_NAME: std::collections::HashMap<&'static str, &'static str> = {
        let mut map = std::collections::HashMap::new();
        map.insert("/", "/");
        map.insert("/metrics", "metrics");
        map.insert("/status", "status");
        map.insert("/reverse", "reverse");
        map.insert("/autocomplete", "autocomplete");
        map
    };

    static ref FEATURES_ROUTE: &'static str = "features";
}

fn get_ressource_name(path: &str) -> String {
    // we can't use the ressource's name in the current actix version,
    // so we use an hardcoded associated table
    PATH_TO_NAME
        .get(path)
        .unwrap_or_else(|| {
            if path.starts_with("/features") {
                &FEATURES_ROUTE
            } else {
                &path
            }
        })
        .to_string()
}

lazy_static::lazy_static! {
    static ref HTTP_COUNTER: prometheus::CounterVec = prometheus::register_counter_vec!(
        "bragi_http_requests_total",
        "Total number of HTTP requests made.",
        &["handler", "method", "status"]
    )
    .unwrap();

    static ref HTTP_REQ_HISTOGRAM: prometheus::HistogramVec = prometheus::register_histogram_vec!(
        "bragi_http_request_duration_seconds",
        "The HTTP request latencies in seconds.",
        &["handler", "method"],
        prometheus::exponential_buckets(0.001, 1.5, 25).unwrap()
    )
    .unwrap();

    static ref HTTP_IN_FLIGHT: prometheus::Gauge = prometheus::register_gauge!(
        "bragi_http_requests_in_flight",
        "current number of http request being served"
    )
    .unwrap();
}

#[derive(Clone)]
#[must_use = "must be set up as middleware for actix-web"]
/// By default two metrics are tracked (this assumes the namespace `actix_web_prom`):
///
///   - `actix_web_prom_http_requests_total` (labels: endpoint, method, status): the total
///   number of HTTP requests handled by the actix HttpServer.
///
///   - `actix_web_prom_http_requests_duration_seconds` (labels: endpoint, method,
///    status): the request duration for all HTTP requests handled by the actix
///    HttpServer.
pub struct PrometheusMetrics {
    pub(crate) namespace: String,
    pub(crate) endpoint: String,
}

impl PrometheusMetrics {
    /// Create a new PrometheusMetrics. You set the namespace and the metrics endpoint
    /// through here.
    pub fn new(namespace: &str, endpoint: &str) -> Self {
        PrometheusMetrics {
            namespace: namespace.to_string(),
            endpoint: endpoint.to_string(),
        }
    }

    fn metrics(&self) -> String {
        let mut buffer = vec![];
        TextEncoder::new()
            .encode(&prometheus::gather(), &mut buffer)
            .unwrap();
        String::from_utf8(buffer).unwrap()
    }

    fn matches(&self, path: &str, method: &Method) -> bool {
        self.endpoint == path && method == Method::GET
    }

    fn update_metrics(
        &self,
        handler: &str,
        method: &Method,
        status: StatusCode,
        clock: SystemTime,
    ) {
        let method = method.to_string();
        let status = status.as_u16().to_string();

        if let Ok(elapsed) = clock.elapsed() {
            let duration =
                (elapsed.as_secs() as f64) + f64::from(elapsed.subsec_nanos()) / 1_000_000_000_f64;
            HTTP_REQ_HISTOGRAM
                .with_label_values(&[&handler, &method])
                .observe(duration);
        }

        HTTP_COUNTER
            .with_label_values(&[&handler, &method, &status])
            .inc();

        HTTP_IN_FLIGHT.dec();
    }
}

impl<S, B> Transform<S> for PrometheusMetrics
where
    B: MessageBody,
    S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
    S::Future: 'static,
{
    type Request = ServiceRequest;
    type Response = ServiceResponse<StreamLog<B>>;
    type Error = Error;
    type InitError = ();
    type Transform = PrometheusMetricsMiddleware<S>;
    type Future = FutureResult<Self::Transform, Self::InitError>;

    fn new_transform(&self, service: S) -> Self::Future {
        ok(PrometheusMetricsMiddleware {
            service,
            inner: Arc::new(self.clone()),
        })
    }
}

#[doc(hidden)]
/// Middleware service for PrometheusMetrics
pub struct PrometheusMetricsMiddleware<S> {
    service: S,
    inner: Arc<PrometheusMetrics>,
}

impl<S, B> Service for PrometheusMetricsMiddleware<S>
where
    S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
    S::Future: 'static,
    B: MessageBody,
{
    type Request = ServiceRequest;
    type Response = ServiceResponse<StreamLog<B>>;
    type Error = S::Error;
    type Future = MetricsResponse<S, B>;

    fn poll_ready(&mut self) -> Poll<(), Self::Error> {
        self.service.poll_ready()
    }

    fn call(&mut self, req: ServiceRequest) -> Self::Future {
        let clock = SystemTime::now();
        MetricsResponse {
            fut: self.service.call(req),
            clock: clock,
            inner: self.inner.clone(),
            _t: PhantomData,
        }
    }
}

#[doc(hidden)]
pub struct MetricsResponse<S, B>
where
    B: MessageBody,
    S: Service,
{
    fut: S::Future,
    clock: SystemTime,
    inner: Arc<PrometheusMetrics>,
    _t: PhantomData<(B,)>,
}

impl<S, B> Future for MetricsResponse<S, B>
where
    B: MessageBody,
    S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
{
    type Item = ServiceResponse<StreamLog<B>>;
    type Error = Error;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        HTTP_IN_FLIGHT.inc();
        let res = futures::try_ready!(self.fut.poll());

        let req = res.request();
        let inner = self.inner.clone();
        let method = req.method().clone();
        let path = req.path().to_string();
        let handler = get_ressource_name(&path);

        Ok(Async::Ready(res.map_body(move |mut head, mut body| {
            // We short circuit the response status and body to serve the endpoint
            // automagically. This way the user does not need to set the middleware *AND*
            // an endpoint to serve middleware results. The user is only required to set
            // the middleware and tell us what the endpoint should be.
            if inner.matches(&path, &method) {
                head.status = StatusCode::OK;
                body = ResponseBody::Other(Body::from_message(inner.metrics()));
            }
            ResponseBody::Body(StreamLog {
                body,
                size: 0,
                clock: self.clock,
                inner,
                status: head.status,
                handler,
                method,
            })
        })))
    }
}

#[doc(hidden)]
pub struct StreamLog<B> {
    body: ResponseBody<B>,
    size: usize,
    clock: SystemTime,
    inner: Arc<PrometheusMetrics>,
    status: StatusCode,
    handler: String,
    method: Method,
}

impl<B> Drop for StreamLog<B> {
    fn drop(&mut self) {
        // update the metrics for this request at the very end of responding
        self.inner
            .update_metrics(&self.handler, &self.method, self.status, self.clock);
    }
}

impl<B: MessageBody> MessageBody for StreamLog<B> {
    fn size(&self) -> BodySize {
        self.body.size()
    }

    fn poll_next(&mut self) -> Poll<Option<Bytes>, Error> {
        match self.body.poll_next()? {
            Async::Ready(Some(chunk)) => {
                self.size += chunk.len();
                Ok(Async::Ready(Some(chunk)))
            }
            val => Ok(val),
        }
    }
}
<commit_msg>bragi: Set empty handler as prometheus label for unknown paths<commit_after>// shamelessly taken from https://github.com/nlopes/actix-web-prom
// we are unfortunatly not able to directly use this great crate,
// because  we want to use the name of the endpoint for retrocompatibility
// (and as a side effect we also added the 'in flight' queries (but for this we could have used the Registry))

use actix_service::{Service, Transform};
use actix_web::{
    dev::{Body, BodySize, MessageBody, ResponseBody, ServiceRequest, ServiceResponse},
    http::{Method, StatusCode},
    web::Bytes,
    Error,
};
use futures::future::{ok, FutureResult};
use futures::{Async, Future, Poll};
use prometheus::{self, Encoder, TextEncoder};
use std::marker::PhantomData;
use std::sync::Arc;
use std::time::SystemTime;

lazy_static::lazy_static! {
    static ref PATH_TO_NAME: std::collections::HashMap<&'static str, &'static str> = {
        let mut map = std::collections::HashMap::new();
        map.insert("/", "/");
        map.insert("/metrics", "metrics");
        map.insert("/status", "status");
        map.insert("/reverse", "reverse");
        map.insert("/autocomplete", "autocomplete");
        map
    };

    static ref FEATURES_ROUTE: &'static str = "features";
}

fn get_ressource_name(path: &str) -> String {
    // we can't use the ressource's name in the current actix version,
    // so we use an hardcoded associated table
    PATH_TO_NAME
        .get(path)
        .copied()
        .unwrap_or_else(|| {
            if path.starts_with("/features") {
                &FEATURES_ROUTE
            } else {
                ""
            }
        })
        .to_string()
}

lazy_static::lazy_static! {
    static ref HTTP_COUNTER: prometheus::CounterVec = prometheus::register_counter_vec!(
        "bragi_http_requests_total",
        "Total number of HTTP requests made.",
        &["handler", "method", "status"]
    )
    .unwrap();

    static ref HTTP_REQ_HISTOGRAM: prometheus::HistogramVec = prometheus::register_histogram_vec!(
        "bragi_http_request_duration_seconds",
        "The HTTP request latencies in seconds.",
        &["handler", "method"],
        prometheus::exponential_buckets(0.001, 1.5, 25).unwrap()
    )
    .unwrap();

    static ref HTTP_IN_FLIGHT: prometheus::Gauge = prometheus::register_gauge!(
        "bragi_http_requests_in_flight",
        "current number of http request being served"
    )
    .unwrap();
}

#[derive(Clone)]
#[must_use = "must be set up as middleware for actix-web"]
/// By default two metrics are tracked (this assumes the namespace `actix_web_prom`):
///
///   - `actix_web_prom_http_requests_total` (labels: endpoint, method, status): the total
///   number of HTTP requests handled by the actix HttpServer.
///
///   - `actix_web_prom_http_requests_duration_seconds` (labels: endpoint, method,
///    status): the request duration for all HTTP requests handled by the actix
///    HttpServer.
pub struct PrometheusMetrics {
    pub(crate) namespace: String,
    pub(crate) endpoint: String,
}

impl PrometheusMetrics {
    /// Create a new PrometheusMetrics. You set the namespace and the metrics endpoint
    /// through here.
    pub fn new(namespace: &str, endpoint: &str) -> Self {
        PrometheusMetrics {
            namespace: namespace.to_string(),
            endpoint: endpoint.to_string(),
        }
    }

    fn metrics(&self) -> String {
        let mut buffer = vec![];
        TextEncoder::new()
            .encode(&prometheus::gather(), &mut buffer)
            .unwrap();
        String::from_utf8(buffer).unwrap()
    }

    fn matches(&self, path: &str, method: &Method) -> bool {
        self.endpoint == path && method == Method::GET
    }

    fn update_metrics(
        &self,
        handler: &str,
        method: &Method,
        status: StatusCode,
        clock: SystemTime,
    ) {
        let method = method.to_string();
        let status = status.as_u16().to_string();

        if let Ok(elapsed) = clock.elapsed() {
            let duration =
                (elapsed.as_secs() as f64) + f64::from(elapsed.subsec_nanos()) / 1_000_000_000_f64;
            HTTP_REQ_HISTOGRAM
                .with_label_values(&[&handler, &method])
                .observe(duration);
        }

        HTTP_COUNTER
            .with_label_values(&[&handler, &method, &status])
            .inc();

        HTTP_IN_FLIGHT.dec();
    }
}

impl<S, B> Transform<S> for PrometheusMetrics
where
    B: MessageBody,
    S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
    S::Future: 'static,
{
    type Request = ServiceRequest;
    type Response = ServiceResponse<StreamLog<B>>;
    type Error = Error;
    type InitError = ();
    type Transform = PrometheusMetricsMiddleware<S>;
    type Future = FutureResult<Self::Transform, Self::InitError>;

    fn new_transform(&self, service: S) -> Self::Future {
        ok(PrometheusMetricsMiddleware {
            service,
            inner: Arc::new(self.clone()),
        })
    }
}

#[doc(hidden)]
/// Middleware service for PrometheusMetrics
pub struct PrometheusMetricsMiddleware<S> {
    service: S,
    inner: Arc<PrometheusMetrics>,
}

impl<S, B> Service for PrometheusMetricsMiddleware<S>
where
    S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
    S::Future: 'static,
    B: MessageBody,
{
    type Request = ServiceRequest;
    type Response = ServiceResponse<StreamLog<B>>;
    type Error = S::Error;
    type Future = MetricsResponse<S, B>;

    fn poll_ready(&mut self) -> Poll<(), Self::Error> {
        self.service.poll_ready()
    }

    fn call(&mut self, req: ServiceRequest) -> Self::Future {
        let clock = SystemTime::now();
        MetricsResponse {
            fut: self.service.call(req),
            clock: clock,
            inner: self.inner.clone(),
            _t: PhantomData,
        }
    }
}

#[doc(hidden)]
pub struct MetricsResponse<S, B>
where
    B: MessageBody,
    S: Service,
{
    fut: S::Future,
    clock: SystemTime,
    inner: Arc<PrometheusMetrics>,
    _t: PhantomData<(B,)>,
}

impl<S, B> Future for MetricsResponse<S, B>
where
    B: MessageBody,
    S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
{
    type Item = ServiceResponse<StreamLog<B>>;
    type Error = Error;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        HTTP_IN_FLIGHT.inc();
        let res = futures::try_ready!(self.fut.poll());

        let req = res.request();
        let inner = self.inner.clone();
        let method = req.method().clone();
        let path = req.path().to_string();
        let handler = get_ressource_name(&path);

        Ok(Async::Ready(res.map_body(move |mut head, mut body| {
            // We short circuit the response status and body to serve the endpoint
            // automagically. This way the user does not need to set the middleware *AND*
            // an endpoint to serve middleware results. The user is only required to set
            // the middleware and tell us what the endpoint should be.
            if inner.matches(&path, &method) {
                head.status = StatusCode::OK;
                body = ResponseBody::Other(Body::from_message(inner.metrics()));
            }
            ResponseBody::Body(StreamLog {
                body,
                size: 0,
                clock: self.clock,
                inner,
                status: head.status,
                handler,
                method,
            })
        })))
    }
}

#[doc(hidden)]
pub struct StreamLog<B> {
    body: ResponseBody<B>,
    size: usize,
    clock: SystemTime,
    inner: Arc<PrometheusMetrics>,
    status: StatusCode,
    handler: String,
    method: Method,
}

impl<B> Drop for StreamLog<B> {
    fn drop(&mut self) {
        // update the metrics for this request at the very end of responding
        self.inner
            .update_metrics(&self.handler, &self.method, self.status, self.clock);
    }
}

impl<B: MessageBody> MessageBody for StreamLog<B> {
    fn size(&self) -> BodySize {
        self.body.size()
    }

    fn poll_next(&mut self) -> Poll<Option<Bytes>, Error> {
        match self.body.poll_next()? {
            Async::Ready(Some(chunk)) => {
                self.size += chunk.len();
                Ok(Async::Ready(Some(chunk)))
            }
            val => Ok(val),
        }
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! The [`@font-face`][ff] at-rule.
//!
//! [ff]: https://drafts.csswg.org/css-fonts/#at-font-face-rule

#![deny(missing_docs)]

#[cfg(feature = "gecko")]
use computed_values::{font_stretch, font_style, font_weight};
use cssparser::{AtRuleParser, DeclarationListParser, DeclarationParser, Parser};
use cssparser::{SourceLocation, CowRcStr};
use error_reporting::{ContextualParseError, ParseErrorReporter};
#[cfg(feature = "gecko")] use gecko_bindings::structs::CSSFontFaceDescriptors;
#[cfg(feature = "gecko")] use cssparser::UnicodeRange;
use parser::{ParserContext, ParserErrorContext, Parse};
#[cfg(feature = "gecko")]
use properties::longhands::font_language_override;
use selectors::parser::SelectorParseErrorKind;
use shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};
use std::fmt::{self, Write};
use str::CssStringWriter;
use style_traits::{Comma, CssWriter, OneOrMoreSeparated, ParseError};
use style_traits::{StyleParseErrorKind, ToCss};
use values::computed::font::FamilyName;
#[cfg(feature = "gecko")]
use values::specified::font::{SpecifiedFontFeatureSettings, FontVariationSettings};
use values::specified::url::SpecifiedUrl;

/// A source for a font-face rule.
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
#[derive(Clone, Debug, Eq, PartialEq, ToCss)]
pub enum Source {
    /// A `url()` source.
    Url(UrlSource),
    /// A `local()` source.
    #[css(function)]
    Local(FamilyName),
}

impl OneOrMoreSeparated for Source {
    type S = Comma;
}

/// A `UrlSource` represents a font-face source that has been specified with a
/// `url()` function.
///
/// <https://drafts.csswg.org/css-fonts/#src-desc>
#[derive(Clone, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
pub struct UrlSource {
    /// The specified url.
    pub url: SpecifiedUrl,
    /// The format hints specified with the `format()` function.
    pub format_hints: Vec<String>,
}

impl ToCss for UrlSource {
    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    where
        W: Write,
    {
        self.url.to_css(dest)
    }
}

/// A font-display value for a @font-face rule.
/// The font-display descriptor determines how a font face is displayed based
/// on whether and when it is downloaded and ready to use.
#[allow(missing_docs)]
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq)]
#[derive(ToComputedValue, ToCss)]
pub enum FontDisplay {
    Auto,
    Block,
    Swap,
    Fallback,
    Optional,
}

/// A font-weight value for a @font-face rule.
/// The font-weight CSS property specifies the weight or boldness of the font.
#[cfg(feature = "gecko")]
#[derive(Clone, Debug, Eq, PartialEq, ToCss)]
pub enum FontWeight {
    /// Numeric font weights for fonts that provide more than just normal and bold.
    Weight(font_weight::T),
    /// Normal font weight. Same as 400.
    Normal,
    /// Bold font weight. Same as 700.
    Bold,
}

#[cfg(feature = "gecko")]
impl Parse for FontWeight {
    fn parse<'i, 't>(_: &ParserContext, input: &mut Parser<'i, 't>)
        -> Result<FontWeight, ParseError<'i>> {
        let result = input.try(|input| {
            let ident = input.expect_ident().map_err(|_| ())?;
            match_ignore_ascii_case! { &ident,
                "normal" => Ok(FontWeight::Normal),
                "bold" => Ok(FontWeight::Bold),
                _ => Err(())
            }
        });
        result.or_else(|_| {
            font_weight::T::from_int(input.expect_integer()?)
                .map(FontWeight::Weight)
                .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
        })
    }
}

/// Parse the block inside a `@font-face` rule.
///
/// Note that the prelude parsing code lives in the `stylesheets` module.
pub fn parse_font_face_block<R>(context: &ParserContext,
                                error_context: &ParserErrorContext<R>,
                                input: &mut Parser,
                                location: SourceLocation)
                                -> FontFaceRuleData
    where R: ParseErrorReporter
{
    let mut rule = FontFaceRuleData::empty(location);
    {
        let parser = FontFaceRuleParser {
            context: context,
            rule: &mut rule,
        };
        let mut iter = DeclarationListParser::new(input, parser);
        while let Some(declaration) = iter.next() {
            if let Err((error, slice)) = declaration {
                let location = error.location;
                let error = ContextualParseError::UnsupportedFontFaceDescriptor(slice, error);
                context.log_css_error(error_context, location, error)
            }
        }
    }
    rule
}

/// A @font-face rule that is known to have font-family and src declarations.
#[cfg(feature = "servo")]
pub struct FontFace<'a>(&'a FontFaceRuleData);

/// A list of effective sources that we send over through IPC to the font cache.
#[cfg(feature = "servo")]
#[derive(Clone, Debug)]
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
pub struct EffectiveSources(Vec<Source>);

#[cfg(feature = "servo")]
impl<'a> FontFace<'a> {
    /// Returns the list of effective sources for that font-face, that is the
    /// sources which don't list any format hint, or the ones which list at
    /// least "truetype" or "opentype".
    pub fn effective_sources(&self) -> EffectiveSources {
        EffectiveSources(self.sources().iter().rev().filter(|source| {
            if let Source::Url(ref url_source) = **source {
                let hints = &url_source.format_hints;
                // We support only opentype fonts and truetype is an alias for
                // that format. Sources without format hints need to be
                // downloaded in case we support them.
                hints.is_empty() || hints.iter().any(|hint| {
                    hint == "truetype" || hint == "opentype" || hint == "woff"
                })
            } else {
                true
            }
        }).cloned().collect())
    }
}

#[cfg(feature = "servo")]
impl Iterator for EffectiveSources {
    type Item = Source;
    fn next(&mut self) -> Option<Source> {
        self.0.pop()
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        (self.0.len(), Some(self.0.len()))
    }
}

struct FontFaceRuleParser<'a, 'b: 'a> {
    context: &'a ParserContext<'b>,
    rule: &'a mut FontFaceRuleData,
}

/// Default methods reject all at rules.
impl<'a, 'b, 'i> AtRuleParser<'i> for FontFaceRuleParser<'a, 'b> {
    type PreludeNoBlock = ();
    type PreludeBlock = ();
    type AtRule = ();
    type Error = StyleParseErrorKind<'i>;
}

impl Parse for Source {
    fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
                     -> Result<Source, ParseError<'i>> {
        if input.try(|input| input.expect_function_matching("local")).is_ok() {
            return input.parse_nested_block(|input| {
                FamilyName::parse(context, input)
            }).map(Source::Local)
        }

        let url = SpecifiedUrl::parse(context, input)?;

        // Parsing optional format()
        let format_hints = if input.try(|input| input.expect_function_matching("format")).is_ok() {
            input.parse_nested_block(|input| {
                input.parse_comma_separated(|input| {
                    Ok(input.expect_string()?.as_ref().to_owned())
                })
            })?
        } else {
            vec![]
        };

        Ok(Source::Url(UrlSource {
            url: url,
            format_hints: format_hints,
        }))
    }
}

macro_rules! is_descriptor_enabled {
    ("font-display") => {
        unsafe {
            use gecko_bindings::structs::mozilla;
            mozilla::StylePrefs_sFontDisplayEnabled
        }
    };
    ("font-variation-settings") => {
        unsafe {
            use gecko_bindings::structs::mozilla;
            mozilla::StylePrefs_sFontVariationsEnabled
        }
    };
    ($name: tt) => { true }
}

macro_rules! font_face_descriptors_common {
    (
        $( #[$doc: meta] $name: tt $ident: ident / $gecko_ident: ident: $ty: ty, )*
    ) => {
        /// Data inside a `@font-face` rule.
        ///
        /// <https://drafts.csswg.org/css-fonts/#font-face-rule>
        #[derive(Clone, Debug, PartialEq)]
        pub struct FontFaceRuleData {
            $(
                #[$doc]
                pub $ident: Option<$ty>,
            )*
            /// Line and column of the @font-face rule source code.
            pub source_location: SourceLocation,
        }

        impl FontFaceRuleData {
            fn empty(location: SourceLocation) -> Self {
                FontFaceRuleData {
                    $(
                        $ident: None,
                    )*
                    source_location: location,
                }
            }

            /// Convert to Gecko types
            #[cfg(feature = "gecko")]
            pub fn set_descriptors(self, descriptors: &mut CSSFontFaceDescriptors) {
                $(
                    if let Some(value) = self.$ident {
                        descriptors.$gecko_ident.set_from(value)
                    }
                )*
                // Leave unset descriptors to eCSSUnit_Null,
                // FontFaceSet::FindOrCreateUserFontEntryFromFontFace does the defaulting
                // to initial values.
            }
        }

        impl ToCssWithGuard for FontFaceRuleData {
            // Serialization of FontFaceRule is not specced.
            fn to_css(&self, _guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
                dest.write_str("@font-face {\n")?;
                $(
                    if let Some(ref value) = self.$ident {
                        dest.write_str(concat!("  ", $name, ": "))?;
                        ToCss::to_css(value, &mut CssWriter::new(dest))?;
                        dest.write_str(";\n")?;
                    }
                )*
                dest.write_str("}")
            }
        }

       impl<'a, 'b, 'i> DeclarationParser<'i> for FontFaceRuleParser<'a, 'b> {
           type Declaration = ();
           type Error = StyleParseErrorKind<'i>;

           fn parse_value<'t>(&mut self, name: CowRcStr<'i>, input: &mut Parser<'i, 't>)
                              -> Result<(), ParseError<'i>> {
                match_ignore_ascii_case! { &*name,
                    $(
                        $name if is_descriptor_enabled!($name) => {
                            // DeclarationParser also calls parse_entirely
                            // so we’d normally not need to,
                            // but in this case we do because we set the value as a side effect
                            // rather than returning it.
                            let value = input.parse_entirely(|i| Parse::parse(self.context, i))?;
                            self.rule.$ident = Some(value)
                        }
                    )*
                    _ => return Err(input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
                }
                Ok(())
            }
        }
    }
}

macro_rules! font_face_descriptors {
    (
        mandatory descriptors = [
            $( #[$m_doc: meta] $m_name: tt $m_ident: ident / $m_gecko_ident: ident: $m_ty: ty, )*
        ]
        optional descriptors = [
            $( #[$o_doc: meta] $o_name: tt $o_ident: ident / $o_gecko_ident: ident: $o_ty: ty =
                $o_initial: expr, )*
        ]
    ) => {
        font_face_descriptors_common! {
            $( #[$m_doc] $m_name $m_ident / $m_gecko_ident: $m_ty, )*
            $( #[$o_doc] $o_name $o_ident / $o_gecko_ident: $o_ty, )*
        }

        impl FontFaceRuleData {
            /// Per https://github.com/w3c/csswg-drafts/issues/1133 an @font-face rule
            /// is valid as far as the CSS parser is concerned even if it doesn’t have
            /// a font-family or src declaration.
            ///
            /// However both are required for the rule to represent an actual font face.
            #[cfg(feature = "servo")]
            pub fn font_face(&self) -> Option<FontFace> {
                if $( self.$m_ident.is_some() )&&* {
                    Some(FontFace(self))
                } else {
                    None
                }
            }
        }

        #[cfg(feature = "servo")]
        impl<'a> FontFace<'a> {
            $(
                #[$m_doc]
                pub fn $m_ident(&self) -> &$m_ty {
                    self.0 .$m_ident.as_ref().unwrap()
                }
            )*
            $(
                #[$o_doc]
                pub fn $o_ident(&self) -> $o_ty {
                    if let Some(ref value) = self.0 .$o_ident {
                        value.clone()
                    } else {
                        $o_initial
                    }
                }
            )*
        }
    }
}

/// css-name rust_identifier: Type = initial_value,
#[cfg(feature = "gecko")]
font_face_descriptors! {
    mandatory descriptors = [
        /// The name of this font face
        "font-family" family / mFamily: FamilyName,

        /// The alternative sources for this font face.
        "src" sources / mSrc: Vec<Source>,
    ]
    optional descriptors = [
        /// The style of this font face
        "font-style" style / mStyle: font_style::T = font_style::T::normal,

        /// The weight of this font face
        "font-weight" weight / mWeight: FontWeight = FontWeight::Normal,

        /// The stretch of this font face
        "font-stretch" stretch / mStretch: font_stretch::T = font_stretch::T::normal,

        /// The display of this font face
        "font-display" display / mDisplay: FontDisplay = FontDisplay::Auto,

        /// The ranges of code points outside of which this font face should not be used.
        "unicode-range" unicode_range / mUnicodeRange: Vec<UnicodeRange> = vec![
            UnicodeRange { start: 0, end: 0x10FFFF }
        ],

        /// The feature settings of this font face.
        "font-feature-settings" feature_settings / mFontFeatureSettings: SpecifiedFontFeatureSettings = {
            font_feature_settings::SpecifiedValue::normal()
        },

        /// The variation settings of this font face.
        "font-variation-settings" variation_settings / mFontVariationSettings: FontVariationSettings = {
            font_variation_settings::SpecifiedValue::normal()
        },

        /// The language override of this font face.
        "font-language-override" language_override / mFontLanguageOverride: font_language_override::SpecifiedValue = {
            font_language_override::SpecifiedValue::Normal
        },
    ]
}

#[cfg(feature = "servo")]
font_face_descriptors! {
    mandatory descriptors = [
        /// The name of this font face
        "font-family" family / mFamily: FamilyName,

        /// The alternative sources for this font face.
        "src" sources / mSrc: Vec<Source>,
    ]
    optional descriptors = [
    ]
}
<commit_msg>Auto merge of #20044 - upsuper:font-face-initial, r=SimonSapin<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! The [`@font-face`][ff] at-rule.
//!
//! [ff]: https://drafts.csswg.org/css-fonts/#at-font-face-rule

#![deny(missing_docs)]

#[cfg(feature = "gecko")]
use computed_values::{font_stretch, font_style, font_weight};
use cssparser::{AtRuleParser, DeclarationListParser, DeclarationParser, Parser};
use cssparser::{SourceLocation, CowRcStr};
use error_reporting::{ContextualParseError, ParseErrorReporter};
#[cfg(feature = "gecko")] use gecko_bindings::structs::CSSFontFaceDescriptors;
#[cfg(feature = "gecko")] use cssparser::UnicodeRange;
use parser::{ParserContext, ParserErrorContext, Parse};
#[cfg(feature = "gecko")]
use properties::longhands::font_language_override;
use selectors::parser::SelectorParseErrorKind;
use shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};
use std::fmt::{self, Write};
use str::CssStringWriter;
use style_traits::{Comma, CssWriter, OneOrMoreSeparated, ParseError};
use style_traits::{StyleParseErrorKind, ToCss};
use values::computed::font::FamilyName;
#[cfg(feature = "gecko")]
use values::specified::font::{SpecifiedFontFeatureSettings, FontVariationSettings};
use values::specified::url::SpecifiedUrl;

/// A source for a font-face rule.
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
#[derive(Clone, Debug, Eq, PartialEq, ToCss)]
pub enum Source {
    /// A `url()` source.
    Url(UrlSource),
    /// A `local()` source.
    #[css(function)]
    Local(FamilyName),
}

impl OneOrMoreSeparated for Source {
    type S = Comma;
}

/// A `UrlSource` represents a font-face source that has been specified with a
/// `url()` function.
///
/// <https://drafts.csswg.org/css-fonts/#src-desc>
#[derive(Clone, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
pub struct UrlSource {
    /// The specified url.
    pub url: SpecifiedUrl,
    /// The format hints specified with the `format()` function.
    pub format_hints: Vec<String>,
}

impl ToCss for UrlSource {
    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    where
        W: Write,
    {
        self.url.to_css(dest)
    }
}

/// A font-display value for a @font-face rule.
/// The font-display descriptor determines how a font face is displayed based
/// on whether and when it is downloaded and ready to use.
#[allow(missing_docs)]
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq)]
#[derive(ToComputedValue, ToCss)]
pub enum FontDisplay {
    Auto,
    Block,
    Swap,
    Fallback,
    Optional,
}

/// A font-weight value for a @font-face rule.
/// The font-weight CSS property specifies the weight or boldness of the font.
#[cfg(feature = "gecko")]
#[derive(Clone, Debug, Eq, PartialEq, ToCss)]
pub enum FontWeight {
    /// Numeric font weights for fonts that provide more than just normal and bold.
    Weight(font_weight::T),
    /// Normal font weight. Same as 400.
    Normal,
    /// Bold font weight. Same as 700.
    Bold,
}

#[cfg(feature = "gecko")]
impl Parse for FontWeight {
    fn parse<'i, 't>(_: &ParserContext, input: &mut Parser<'i, 't>)
        -> Result<FontWeight, ParseError<'i>> {
        let result = input.try(|input| {
            let ident = input.expect_ident().map_err(|_| ())?;
            match_ignore_ascii_case! { &ident,
                "normal" => Ok(FontWeight::Normal),
                "bold" => Ok(FontWeight::Bold),
                _ => Err(())
            }
        });
        result.or_else(|_| {
            font_weight::T::from_int(input.expect_integer()?)
                .map(FontWeight::Weight)
                .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
        })
    }
}

/// Parse the block inside a `@font-face` rule.
///
/// Note that the prelude parsing code lives in the `stylesheets` module.
pub fn parse_font_face_block<R>(context: &ParserContext,
                                error_context: &ParserErrorContext<R>,
                                input: &mut Parser,
                                location: SourceLocation)
                                -> FontFaceRuleData
    where R: ParseErrorReporter
{
    let mut rule = FontFaceRuleData::empty(location);
    {
        let parser = FontFaceRuleParser {
            context: context,
            rule: &mut rule,
        };
        let mut iter = DeclarationListParser::new(input, parser);
        while let Some(declaration) = iter.next() {
            if let Err((error, slice)) = declaration {
                let location = error.location;
                let error = ContextualParseError::UnsupportedFontFaceDescriptor(slice, error);
                context.log_css_error(error_context, location, error)
            }
        }
    }
    rule
}

/// A @font-face rule that is known to have font-family and src declarations.
#[cfg(feature = "servo")]
pub struct FontFace<'a>(&'a FontFaceRuleData);

/// A list of effective sources that we send over through IPC to the font cache.
#[cfg(feature = "servo")]
#[derive(Clone, Debug)]
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
pub struct EffectiveSources(Vec<Source>);

#[cfg(feature = "servo")]
impl<'a> FontFace<'a> {
    /// Returns the list of effective sources for that font-face, that is the
    /// sources which don't list any format hint, or the ones which list at
    /// least "truetype" or "opentype".
    pub fn effective_sources(&self) -> EffectiveSources {
        EffectiveSources(self.sources().iter().rev().filter(|source| {
            if let Source::Url(ref url_source) = **source {
                let hints = &url_source.format_hints;
                // We support only opentype fonts and truetype is an alias for
                // that format. Sources without format hints need to be
                // downloaded in case we support them.
                hints.is_empty() || hints.iter().any(|hint| {
                    hint == "truetype" || hint == "opentype" || hint == "woff"
                })
            } else {
                true
            }
        }).cloned().collect())
    }
}

#[cfg(feature = "servo")]
impl Iterator for EffectiveSources {
    type Item = Source;
    fn next(&mut self) -> Option<Source> {
        self.0.pop()
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        (self.0.len(), Some(self.0.len()))
    }
}

struct FontFaceRuleParser<'a, 'b: 'a> {
    context: &'a ParserContext<'b>,
    rule: &'a mut FontFaceRuleData,
}

/// Default methods reject all at rules.
impl<'a, 'b, 'i> AtRuleParser<'i> for FontFaceRuleParser<'a, 'b> {
    type PreludeNoBlock = ();
    type PreludeBlock = ();
    type AtRule = ();
    type Error = StyleParseErrorKind<'i>;
}

impl Parse for Source {
    fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
                     -> Result<Source, ParseError<'i>> {
        if input.try(|input| input.expect_function_matching("local")).is_ok() {
            return input.parse_nested_block(|input| {
                FamilyName::parse(context, input)
            }).map(Source::Local)
        }

        let url = SpecifiedUrl::parse(context, input)?;

        // Parsing optional format()
        let format_hints = if input.try(|input| input.expect_function_matching("format")).is_ok() {
            input.parse_nested_block(|input| {
                input.parse_comma_separated(|input| {
                    Ok(input.expect_string()?.as_ref().to_owned())
                })
            })?
        } else {
            vec![]
        };

        Ok(Source::Url(UrlSource {
            url: url,
            format_hints: format_hints,
        }))
    }
}

macro_rules! is_descriptor_enabled {
    ("font-display") => {
        unsafe {
            use gecko_bindings::structs::mozilla;
            mozilla::StylePrefs_sFontDisplayEnabled
        }
    };
    ("font-variation-settings") => {
        unsafe {
            use gecko_bindings::structs::mozilla;
            mozilla::StylePrefs_sFontVariationsEnabled
        }
    };
    ($name: tt) => { true }
}

macro_rules! font_face_descriptors_common {
    (
        $( #[$doc: meta] $name: tt $ident: ident / $gecko_ident: ident: $ty: ty, )*
    ) => {
        /// Data inside a `@font-face` rule.
        ///
        /// <https://drafts.csswg.org/css-fonts/#font-face-rule>
        #[derive(Clone, Debug, PartialEq)]
        pub struct FontFaceRuleData {
            $(
                #[$doc]
                pub $ident: Option<$ty>,
            )*
            /// Line and column of the @font-face rule source code.
            pub source_location: SourceLocation,
        }

        impl FontFaceRuleData {
            fn empty(location: SourceLocation) -> Self {
                FontFaceRuleData {
                    $(
                        $ident: None,
                    )*
                    source_location: location,
                }
            }

            /// Convert to Gecko types
            #[cfg(feature = "gecko")]
            pub fn set_descriptors(self, descriptors: &mut CSSFontFaceDescriptors) {
                $(
                    if let Some(value) = self.$ident {
                        descriptors.$gecko_ident.set_from(value)
                    }
                )*
                // Leave unset descriptors to eCSSUnit_Null,
                // FontFaceSet::FindOrCreateUserFontEntryFromFontFace does the defaulting
                // to initial values.
            }
        }

        impl ToCssWithGuard for FontFaceRuleData {
            // Serialization of FontFaceRule is not specced.
            fn to_css(&self, _guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
                dest.write_str("@font-face {\n")?;
                $(
                    if let Some(ref value) = self.$ident {
                        dest.write_str(concat!("  ", $name, ": "))?;
                        ToCss::to_css(value, &mut CssWriter::new(dest))?;
                        dest.write_str(";\n")?;
                    }
                )*
                dest.write_str("}")
            }
        }

       impl<'a, 'b, 'i> DeclarationParser<'i> for FontFaceRuleParser<'a, 'b> {
           type Declaration = ();
           type Error = StyleParseErrorKind<'i>;

           fn parse_value<'t>(&mut self, name: CowRcStr<'i>, input: &mut Parser<'i, 't>)
                              -> Result<(), ParseError<'i>> {
                match_ignore_ascii_case! { &*name,
                    $(
                        $name if is_descriptor_enabled!($name) => {
                            // DeclarationParser also calls parse_entirely
                            // so we’d normally not need to,
                            // but in this case we do because we set the value as a side effect
                            // rather than returning it.
                            let value = input.parse_entirely(|i| Parse::parse(self.context, i))?;
                            self.rule.$ident = Some(value)
                        }
                    )*
                    _ => return Err(input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
                }
                Ok(())
            }
        }
    }
}

macro_rules! font_face_descriptors {
    (
        mandatory descriptors = [
            $( #[$m_doc: meta] $m_name: tt $m_ident: ident / $m_gecko_ident: ident: $m_ty: ty, )*
        ]
        optional descriptors = [
            $( #[$o_doc: meta] $o_name: tt $o_ident: ident / $o_gecko_ident: ident: $o_ty: ty, )*
        ]
    ) => {
        font_face_descriptors_common! {
            $( #[$m_doc] $m_name $m_ident / $m_gecko_ident: $m_ty, )*
            $( #[$o_doc] $o_name $o_ident / $o_gecko_ident: $o_ty, )*
        }

        impl FontFaceRuleData {
            /// Per https://github.com/w3c/csswg-drafts/issues/1133 an @font-face rule
            /// is valid as far as the CSS parser is concerned even if it doesn’t have
            /// a font-family or src declaration.
            ///
            /// However both are required for the rule to represent an actual font face.
            #[cfg(feature = "servo")]
            pub fn font_face(&self) -> Option<FontFace> {
                if $( self.$m_ident.is_some() )&&* {
                    Some(FontFace(self))
                } else {
                    None
                }
            }
        }

        #[cfg(feature = "servo")]
        impl<'a> FontFace<'a> {
            $(
                #[$m_doc]
                pub fn $m_ident(&self) -> &$m_ty {
                    self.0 .$m_ident.as_ref().unwrap()
                }
            )*
        }
    }
}

/// css-name rust_identifier: Type,
#[cfg(feature = "gecko")]
font_face_descriptors! {
    mandatory descriptors = [
        /// The name of this font face
        "font-family" family / mFamily: FamilyName,

        /// The alternative sources for this font face.
        "src" sources / mSrc: Vec<Source>,
    ]
    optional descriptors = [
        /// The style of this font face
        "font-style" style / mStyle: font_style::T,

        /// The weight of this font face
        "font-weight" weight / mWeight: FontWeight,

        /// The stretch of this font face
        "font-stretch" stretch / mStretch: font_stretch::T,

        /// The display of this font face
        "font-display" display / mDisplay: FontDisplay,

        /// The ranges of code points outside of which this font face should not be used.
        "unicode-range" unicode_range / mUnicodeRange: Vec<UnicodeRange>,

        /// The feature settings of this font face.
        "font-feature-settings" feature_settings / mFontFeatureSettings: SpecifiedFontFeatureSettings,

        /// The variation settings of this font face.
        "font-variation-settings" variation_settings / mFontVariationSettings: FontVariationSettings,

        /// The language override of this font face.
        "font-language-override" language_override / mFontLanguageOverride: font_language_override::SpecifiedValue,
    ]
}

#[cfg(feature = "servo")]
font_face_descriptors! {
    mandatory descriptors = [
        /// The name of this font face
        "font-family" family / mFamily: FamilyName,

        /// The alternative sources for this font face.
        "src" sources / mSrc: Vec<Source>,
    ]
    optional descriptors = [
    ]
}
<|endoftext|>


<commit_before><commit_msg>day 12, part 2<commit_after><|endoftext|>


<commit_before>use kernel::{
    async_trait::async_trait,
    eyre::{bail, eyre, Result},
    stencila_schema::{CodeError, Node},
    Kernel, KernelStatus, KernelTrait, KernelType,
};
use serde::Serialize;
use std::{env, fs};
use tokio::{
    io::{AsyncBufReadExt, AsyncWriteExt, BufReader, BufWriter},
    process::{Child, ChildStderr, ChildStdin, ChildStdout},
};

// Line end flags for the Microkernel protocol
// On Windows, Rscript (and possibly other binaries) escapes unicode on stdout and stderr
// So the _ALT flags are provided for these instances (or where it is not possible to output Unicode at all).

/// The end of a startup and kernel is ready to process transactions.
const READY: char = '\u{10ACDC}';
const READY_ALT: &str = "<U+0010ACDC>";

/// The end of a result ("outputs" on `stderr` and "messages" on `stderr`).
const RESULT: char = '\u{10D00B}';
const RESULT_ALT: &str = "<U+0010D00B>";

/// The end of a transaction, kernel is ready for next transaction.
const TRANS: char = '\u{10ABBA}';
const TRANS_ALT: &str = "<U+0010ABBA>";

#[derive(Debug, Serialize)]
pub struct MicroKernel {
    /// The name of the kernel
    ///
    /// The convention for Microkernels, is to prefix the name with `u`.
    name: String,

    /// The language of the kernel
    ///
    /// Used to be able to return a `Kernel` spec.
    languages: Vec<String>,

    /// Operating systems that the kernel will run on
    ///
    /// Possible OS names can be found here https://doc.rust-lang.org/std/env/consts/constant.OS.html
    oses: Vec<String>,

    /// A specification of the runtime executable needed for the kernel
    runtime: (String, String),

    /// Arguments that should be supplied to the runtime binary
    ///
    /// Use the argument `"{{script}}"` as a placeholder for the name
    /// of the script file.
    args: Vec<String>,

    /// The script that runs the kernel
    #[serde(skip)]
    script: (String, String),

    /// Other files that the kernel uses (e.g. codec)
    #[serde(skip)]
    others: Vec<(String, String)>,

    /// The code template for setting a variable
    #[serde(skip)]
    set_template: String,

    /// The code template for getting a variable
    #[serde(skip)]
    get_template: String,

    /// The current status of the kernel
    status: KernelStatus,

    /// The child process of the kernel
    #[serde(skip)]
    child: Option<Child>,

    /// The writer for the stdin stream of the child process
    #[serde(skip)]
    stdin: Option<BufWriter<ChildStdin>>,

    /// The reader for the stdout stream of the child process
    #[serde(skip)]
    stdout: Option<BufReader<ChildStdout>>,

    /// The reader for the stderr stream of the child process
    #[serde(skip)]
    stderr: Option<BufReader<ChildStderr>>,
}

impl MicroKernel {
    /// Create a new `MicroKernel`
    #[allow(clippy::too_many_arguments)]
    pub fn new(
        name: &str,
        languages: &[&str],
        oses: &[&str],
        runtime: (&str, &str),
        args: &[&str],
        script: (&str, &str),
        others: &[(&str, &str)],
        set_template: &str,
        get_template: &str,
    ) -> Self {
        Self {
            name: name.into(),
            languages: languages.iter().map(|lang| lang.to_string()).collect(),
            oses: oses.iter().map(|os| os.to_string()).collect(),
            runtime: (runtime.0.into(), runtime.1.into()),
            args: args.iter().map(|arg| arg.to_string()).collect(),
            script: (script.0.to_string(), script.1.to_string()),
            others: others
                .iter()
                .map(|file| (file.0.to_string(), file.1.to_string()))
                .collect(),
            set_template: set_template.into(),
            get_template: get_template.into(),
            status: KernelStatus::Pending,
            child: None,
            stdin: None,
            stdout: None,
            stderr: None,
        }
    }
}

/// Include a file as a (name, content) tuple
#[macro_export]
macro_rules! include_file {
    ($name:literal) => {
        ($name, include_str!($name))
    };
}

#[async_trait]
impl KernelTrait for MicroKernel {
    /// Get the [`Kernel`] specification
    fn spec(&self) -> Kernel {
        Kernel {
            name: self.name.clone(),
            r#type: KernelType::Micro,
            languages: self.languages.clone(),
        }
    }

    /// Is the kernel available on the current machine?
    ///
    /// Returns `true` if the operating system is listed in `oses` and
    /// a runtime matching the semver requirements in `runtime` is found to be installed.
    async fn available(&self) -> bool {
        if !self.oses.contains(&std::env::consts::OS.to_string()) {
            return false;
        }
        let (name, semver) = &self.runtime;
        binaries::installed(name, semver).await
    }

    /// Start the kernel
    ///
    /// An override of `KernelTrait::start` that searches for the preferred executable
    /// and runs it using specified commands, including the kernel script file if specified
    /// in the arguments.
    async fn start(&mut self) -> Result<()> {
        // Resolve the directory where kernels ar run
        let user_data_dir = dirs::data_dir().unwrap_or_else(|| env::current_dir().unwrap());
        let dir = match env::consts::OS {
            "macos" | "windows" => user_data_dir.join("Stencila").join("Microkernels"),
            _ => user_data_dir.join("stencila").join("microkernels"),
        };
        fs::create_dir_all(&dir)?;

        // Copy over script file
        let script_path = dir.join(&self.script.0);
        fs::write(&script_path, &self.script.1)?;

        // Copy over other files
        for other in &self.others {
            fs::write(dir.join(&other.0), &other.1)?;
        }

        // Construct args array, inserting script path where appropriate
        let args: Vec<String> = self
            .args
            .iter()
            .map(|arg| match arg.as_str() {
                "{{script}}" => script_path.display().to_string(),
                _ => arg.to_string(),
            })
            .collect();

        // Start child process
        let (name, semver) = &self.runtime;
        let binary = binaries::installation(name, semver).await?;
        let mut child = binary.interact(&args)?;

        let stdin = child
            .stdin
            .take()
            .ok_or_else(|| eyre!("Child has no stdin handle"))?;

        let stdout = child
            .stdout
            .take()
            .ok_or_else(|| eyre!("Child has no stdout handle"))?;

        let stderr = child
            .stderr
            .take()
            .ok_or_else(|| eyre!("Child has no stderr handle"))?;

        self.child = Some(child);
        self.stdin = Some(BufWriter::new(stdin));
        self.stdout = Some(BufReader::new(stdout));
        self.stderr = Some(BufReader::new(stderr));
        self.status = KernelStatus::Starting;

        // Capture stdout until the READY flag
        let stdout = self.stdout.as_mut().unwrap();
        loop {
            match stdout.lines().next_line().await {
                Ok(Some(line)) => {
                    tracing::debug!("Received on stdout: {}", line);
                    if line.ends_with(READY) || line.ends_with(READY_ALT) {
                        break;
                    }
                }
                Ok(None) => bail!("Unexpected end of stdout"),
                Err(error) => bail!("When receiving stdout from kernel: {}", error),
            };
        }

        // Capture stderr until the READY flag and warn if any content
        let stderr = self.stderr.as_mut().unwrap();
        let mut err = String::new();
        loop {
            match stderr.lines().next_line().await {
                Ok(Some(line)) => {
                    tracing::debug!("Received on stderr: {}", line);
                    if let Some(line) = line
                        .strip_suffix(READY)
                        .or_else(|| line.strip_suffix(READY_ALT))
                    {
                        err.push_str(line);
                        err.push('\n');
                        break;
                    }
                }
                Ok(None) => bail!("Unexpected end of stderr"),
                Err(error) => bail!("When receiving stderr from kernel: {}", error),
            };
        }
        let err = err.trim();
        if !err.is_empty() {
            tracing::warn!(
                "While starting kernel `{}` got output on stderr: {}",
                self.name,
                err
            )
        }

        self.status = KernelStatus::Idle;

        Ok(())
    }

    /// Stop the kernel
    ///
    /// An override of `KernelTrait::stop` that kills the child process.
    async fn stop(&mut self) -> Result<()> {
        if let Some(child) = self.child.as_mut() {
            self.status = KernelStatus::Stopping;
            child.kill().await?;
            self.child = None;
            self.status = KernelStatus::Finished;
        }
        Ok(())
    }

    /// Get the status of the kernel
    async fn status(&self) -> Result<KernelStatus> {
        Ok(self.status.clone())
    }

    /// Get a symbol from the kernel
    async fn get(&mut self, name: &str) -> Result<Node> {
        let code = self.get_template.replace("{{name}}", name);

        let (outputs, messages) = self.exec(&code).await?;

        if let Some(output) = outputs.first() {
            Ok(output.clone())
        } else {
            // TODO: When messages include CodeWarning etc log those
            // and only bail on CodeError
            let message = messages
                .first()
                .map(|message| message.error_message.clone())
                .unwrap_or_else(|| "Unknown error".to_string());
            bail!("Unable to get symbol `{}`: {}", name, message)
        }
    }

    /// Set a symbol in the kernel
    async fn set(&mut self, name: &str, value: Node) -> Result<()> {
        let json = serde_json::to_string(&value)?;
        let code = self
            .set_template
            .replace("{{name}}", name)
            .replace("{{json}}", &json);

        let (.., messages) = self.exec(&code).await?;

        if messages.is_empty() {
            Ok(())
        } else {
            // TODO: When messages include CodeWarning etc log those
            // and only bail on CodeError
            let message = messages
                .first()
                .map(|message| message.error_message.clone())
                .unwrap_or_else(|| "Unknown error".to_string());
            bail!("Unable to set symbol `{}`: {}", name, message)
        }
    }

    /// Execute some code in the kernel
    async fn exec(&mut self, code: &str) -> Result<(Vec<Node>, Vec<CodeError>)> {
        let stdin = self
            .stdin
            .as_mut()
            .expect("Kernel should have started and have stdin");
        let stdout = self
            .stdout
            .as_mut()
            .expect("Kernel should have started and have stdout");
        let stderr = self
            .stderr
            .as_mut()
            .expect("Kernel should have started and have stderr");

        // Send code to the kernel
        tracing::debug!("Sending on stdin");
        let escaped = code.replace("\n", "\\n");
        if let Err(error) = stdin.write_all([&escaped, "\n"].concat().as_bytes()).await {
            self.status = KernelStatus::Failed;
            bail!("When writing code to kernel: {}", error)
        }
        if let Err(error) = stdin.flush().await {
            self.status = KernelStatus::Failed;
            bail!("When flushing code to kernel: {}", error)
        }

        // Capture outputs separating them as we go
        let mut output = String::new();
        let mut outputs = Vec::new();
        loop {
            let line = match stdout.lines().next_line().await {
                Ok(Some(line)) => line,
                Ok(None) => break,
                Err(error) => {
                    self.status = KernelStatus::Failed;
                    bail!("When receiving outputs from kernel: {}", error)
                }
            };

            tracing::debug!("Received on stdout: {}", line);
            if !push_line(&line, &mut output, &mut outputs) {
                break;
            }
        }

        // Attempt to parse each output as JSON into a `Node`, falling back to a string.
        let outputs: Vec<Node> = outputs
            .iter()
            .map(|output| -> Node {
                serde_json::from_str(output).unwrap_or_else(|_| Node::String(output.clone()))
            })
            .collect();

        // Capture messages separating them as we go
        let mut message = String::new();
        let mut messages = Vec::new();
        loop {
            let line = match stderr.lines().next_line().await {
                Ok(Some(line)) => line,
                Ok(None) => break,
                Err(error) => {
                    self.status = KernelStatus::Failed;
                    bail!("When receiving messages from kernel: {}", error)
                }
            };

            tracing::debug!("Received on stderr: {}", line);
            if !push_line(&line, &mut message, &mut messages) {
                break;
            }
        }

        // Attempt to parse each message as JSON into a `CodeMessage`.
        let messages: Vec<CodeError> = messages
            .iter()
            .map(|message| -> CodeError {
                serde_json::from_str(message).unwrap_or_else(|_| CodeError {
                    error_message: message.into(),
                    ..Default::default()
                })
            })
            .collect();

        Ok((outputs, messages))
    }
}

/// Handle a line of stdout or stderr
///
/// How the line is handled depends upon whether it has a result or transaction
/// separator at the end. Returns false at the end of a transaction.
fn push_line(line: &str, current: &mut String, vec: &mut Vec<String>) -> bool {
    if let Some(line) = line
        .strip_suffix(RESULT)
        .or_else(|| line.strip_suffix(RESULT_ALT))
    {
        current.push_str(line);
        if !current.is_empty() {
            vec.push(current.clone());
            current.clear();
        }
        true
    } else if let Some(line) = line
        .strip_suffix(TRANS)
        .or_else(|| line.strip_suffix(TRANS_ALT))
    {
        current.push_str(line);
        if !current.is_empty() {
            vec.push(current.clone());
        }
        false
    } else {
        current.push_str(line);
        current.push('\n');
        true
    }
}
<commit_msg>fix(Microkernels): Add lines other than those with `READY` flag<commit_after>use kernel::{
    async_trait::async_trait,
    eyre::{bail, eyre, Result},
    stencila_schema::{CodeError, Node},
    Kernel, KernelStatus, KernelTrait, KernelType,
};
use serde::Serialize;
use std::{env, fs};
use tokio::{
    io::{AsyncBufReadExt, AsyncWriteExt, BufReader, BufWriter},
    process::{Child, ChildStderr, ChildStdin, ChildStdout},
};

// Line end flags for the Microkernel protocol
// On Windows, Rscript (and possibly other binaries) escapes unicode on stdout and stderr
// So the _ALT flags are provided for these instances (or where it is not possible to output Unicode at all).

/// The end of a startup and kernel is ready to process transactions.
const READY: char = '\u{10ACDC}';
const READY_ALT: &str = "<U+0010ACDC>";

/// The end of a result ("outputs" on `stderr` and "messages" on `stderr`).
const RESULT: char = '\u{10D00B}';
const RESULT_ALT: &str = "<U+0010D00B>";

/// The end of a transaction, kernel is ready for next transaction.
const TRANS: char = '\u{10ABBA}';
const TRANS_ALT: &str = "<U+0010ABBA>";

#[derive(Debug, Serialize)]
pub struct MicroKernel {
    /// The name of the kernel
    ///
    /// The convention for Microkernels, is to prefix the name with `u`.
    name: String,

    /// The language of the kernel
    ///
    /// Used to be able to return a `Kernel` spec.
    languages: Vec<String>,

    /// Operating systems that the kernel will run on
    ///
    /// Possible OS names can be found here https://doc.rust-lang.org/std/env/consts/constant.OS.html
    oses: Vec<String>,

    /// A specification of the runtime executable needed for the kernel
    runtime: (String, String),

    /// Arguments that should be supplied to the runtime binary
    ///
    /// Use the argument `"{{script}}"` as a placeholder for the name
    /// of the script file.
    args: Vec<String>,

    /// The script that runs the kernel
    #[serde(skip)]
    script: (String, String),

    /// Other files that the kernel uses (e.g. codec)
    #[serde(skip)]
    others: Vec<(String, String)>,

    /// The code template for setting a variable
    #[serde(skip)]
    set_template: String,

    /// The code template for getting a variable
    #[serde(skip)]
    get_template: String,

    /// The current status of the kernel
    status: KernelStatus,

    /// The child process of the kernel
    #[serde(skip)]
    child: Option<Child>,

    /// The writer for the stdin stream of the child process
    #[serde(skip)]
    stdin: Option<BufWriter<ChildStdin>>,

    /// The reader for the stdout stream of the child process
    #[serde(skip)]
    stdout: Option<BufReader<ChildStdout>>,

    /// The reader for the stderr stream of the child process
    #[serde(skip)]
    stderr: Option<BufReader<ChildStderr>>,
}

impl MicroKernel {
    /// Create a new `MicroKernel`
    #[allow(clippy::too_many_arguments)]
    pub fn new(
        name: &str,
        languages: &[&str],
        oses: &[&str],
        runtime: (&str, &str),
        args: &[&str],
        script: (&str, &str),
        others: &[(&str, &str)],
        set_template: &str,
        get_template: &str,
    ) -> Self {
        Self {
            name: name.into(),
            languages: languages.iter().map(|lang| lang.to_string()).collect(),
            oses: oses.iter().map(|os| os.to_string()).collect(),
            runtime: (runtime.0.into(), runtime.1.into()),
            args: args.iter().map(|arg| arg.to_string()).collect(),
            script: (script.0.to_string(), script.1.to_string()),
            others: others
                .iter()
                .map(|file| (file.0.to_string(), file.1.to_string()))
                .collect(),
            set_template: set_template.into(),
            get_template: get_template.into(),
            status: KernelStatus::Pending,
            child: None,
            stdin: None,
            stdout: None,
            stderr: None,
        }
    }
}

/// Include a file as a (name, content) tuple
#[macro_export]
macro_rules! include_file {
    ($name:literal) => {
        ($name, include_str!($name))
    };
}

#[async_trait]
impl KernelTrait for MicroKernel {
    /// Get the [`Kernel`] specification
    fn spec(&self) -> Kernel {
        Kernel {
            name: self.name.clone(),
            r#type: KernelType::Micro,
            languages: self.languages.clone(),
        }
    }

    /// Is the kernel available on the current machine?
    ///
    /// Returns `true` if the operating system is listed in `oses` and
    /// a runtime matching the semver requirements in `runtime` is found to be installed.
    async fn available(&self) -> bool {
        if !self.oses.contains(&std::env::consts::OS.to_string()) {
            return false;
        }
        let (name, semver) = &self.runtime;
        binaries::installed(name, semver).await
    }

    /// Start the kernel
    ///
    /// An override of `KernelTrait::start` that searches for the preferred executable
    /// and runs it using specified commands, including the kernel script file if specified
    /// in the arguments.
    async fn start(&mut self) -> Result<()> {
        // Resolve the directory where kernels ar run
        let user_data_dir = dirs::data_dir().unwrap_or_else(|| env::current_dir().unwrap());
        let dir = match env::consts::OS {
            "macos" | "windows" => user_data_dir.join("Stencila").join("Microkernels"),
            _ => user_data_dir.join("stencila").join("microkernels"),
        };
        fs::create_dir_all(&dir)?;

        // Copy over script file
        let script_path = dir.join(&self.script.0);
        fs::write(&script_path, &self.script.1)?;

        // Copy over other files
        for other in &self.others {
            fs::write(dir.join(&other.0), &other.1)?;
        }

        // Construct args array, inserting script path where appropriate
        let args: Vec<String> = self
            .args
            .iter()
            .map(|arg| match arg.as_str() {
                "{{script}}" => script_path.display().to_string(),
                _ => arg.to_string(),
            })
            .collect();

        // Start child process
        let (name, semver) = &self.runtime;
        let binary = binaries::installation(name, semver).await?;
        let mut child = binary.interact(&args)?;

        let stdin = child
            .stdin
            .take()
            .ok_or_else(|| eyre!("Child has no stdin handle"))?;

        let stdout = child
            .stdout
            .take()
            .ok_or_else(|| eyre!("Child has no stdout handle"))?;

        let stderr = child
            .stderr
            .take()
            .ok_or_else(|| eyre!("Child has no stderr handle"))?;

        self.child = Some(child);
        self.stdin = Some(BufWriter::new(stdin));
        self.stdout = Some(BufReader::new(stdout));
        self.stderr = Some(BufReader::new(stderr));
        self.status = KernelStatus::Starting;

        // Capture stdout until the READY flag
        let stdout = self.stdout.as_mut().unwrap();
        loop {
            match stdout.lines().next_line().await {
                Ok(Some(line)) => {
                    tracing::debug!("Received on stdout: {}", line);
                    if line.ends_with(READY) || line.ends_with(READY_ALT) {
                        break;
                    }
                }
                Ok(None) => bail!("Unexpected end of stdout"),
                Err(error) => bail!("When receiving stdout from kernel: {}", error),
            };
        }

        // Capture stderr until the READY flag and warn if any content
        let stderr = self.stderr.as_mut().unwrap();
        let mut err = String::new();
        loop {
            match stderr.lines().next_line().await {
                Ok(Some(line)) => {
                    tracing::debug!("Received on stderr: {}", line);
                    if let Some(line) = line
                        .strip_suffix(READY)
                        .or_else(|| line.strip_suffix(READY_ALT))
                    {
                        err.push_str(line);
                        err.push('\n');
                        break;
                    } else {
                        err.push_str(&line);
                        err.push('\n');
                    }
                }
                Ok(None) => bail!("Unexpected end of stderr"),
                Err(error) => bail!("When receiving stderr from kernel: {}", error),
            };
        }
        let err = err.trim();
        if !err.is_empty() {
            tracing::warn!(
                "While starting kernel `{}` got output on stderr: {}",
                self.name,
                err
            )
        }

        self.status = KernelStatus::Idle;

        Ok(())
    }

    /// Stop the kernel
    ///
    /// An override of `KernelTrait::stop` that kills the child process.
    async fn stop(&mut self) -> Result<()> {
        if let Some(child) = self.child.as_mut() {
            self.status = KernelStatus::Stopping;
            child.kill().await?;
            self.child = None;
            self.status = KernelStatus::Finished;
        }
        Ok(())
    }

    /// Get the status of the kernel
    async fn status(&self) -> Result<KernelStatus> {
        Ok(self.status.clone())
    }

    /// Get a symbol from the kernel
    async fn get(&mut self, name: &str) -> Result<Node> {
        let code = self.get_template.replace("{{name}}", name);

        let (outputs, messages) = self.exec(&code).await?;

        if let Some(output) = outputs.first() {
            Ok(output.clone())
        } else {
            // TODO: When messages include CodeWarning etc log those
            // and only bail on CodeError
            let message = messages
                .first()
                .map(|message| message.error_message.clone())
                .unwrap_or_else(|| "Unknown error".to_string());
            bail!("Unable to get symbol `{}`: {}", name, message)
        }
    }

    /// Set a symbol in the kernel
    async fn set(&mut self, name: &str, value: Node) -> Result<()> {
        let json = serde_json::to_string(&value)?;
        let code = self
            .set_template
            .replace("{{name}}", name)
            .replace("{{json}}", &json);

        let (.., messages) = self.exec(&code).await?;

        if messages.is_empty() {
            Ok(())
        } else {
            // TODO: When messages include CodeWarning etc log those
            // and only bail on CodeError
            let message = messages
                .first()
                .map(|message| message.error_message.clone())
                .unwrap_or_else(|| "Unknown error".to_string());
            bail!("Unable to set symbol `{}`: {}", name, message)
        }
    }

    /// Execute some code in the kernel
    async fn exec(&mut self, code: &str) -> Result<(Vec<Node>, Vec<CodeError>)> {
        let stdin = self
            .stdin
            .as_mut()
            .expect("Kernel should have started and have stdin");
        let stdout = self
            .stdout
            .as_mut()
            .expect("Kernel should have started and have stdout");
        let stderr = self
            .stderr
            .as_mut()
            .expect("Kernel should have started and have stderr");

        // Send code to the kernel
        tracing::debug!("Sending on stdin");
        let escaped = code.replace("\n", "\\n");
        if let Err(error) = stdin.write_all([&escaped, "\n"].concat().as_bytes()).await {
            self.status = KernelStatus::Failed;
            bail!("When writing code to kernel: {}", error)
        }
        if let Err(error) = stdin.flush().await {
            self.status = KernelStatus::Failed;
            bail!("When flushing code to kernel: {}", error)
        }

        // Capture outputs separating them as we go
        let mut output = String::new();
        let mut outputs = Vec::new();
        loop {
            let line = match stdout.lines().next_line().await {
                Ok(Some(line)) => line,
                Ok(None) => break,
                Err(error) => {
                    self.status = KernelStatus::Failed;
                    bail!("When receiving outputs from kernel: {}", error)
                }
            };

            tracing::debug!("Received on stdout: {}", line);
            if !push_line(&line, &mut output, &mut outputs) {
                break;
            }
        }

        // Attempt to parse each output as JSON into a `Node`, falling back to a string.
        let outputs: Vec<Node> = outputs
            .iter()
            .map(|output| -> Node {
                serde_json::from_str(output).unwrap_or_else(|_| Node::String(output.clone()))
            })
            .collect();

        // Capture messages separating them as we go
        let mut message = String::new();
        let mut messages = Vec::new();
        loop {
            let line = match stderr.lines().next_line().await {
                Ok(Some(line)) => line,
                Ok(None) => break,
                Err(error) => {
                    self.status = KernelStatus::Failed;
                    bail!("When receiving messages from kernel: {}", error)
                }
            };

            tracing::debug!("Received on stderr: {}", line);
            if !push_line(&line, &mut message, &mut messages) {
                break;
            }
        }

        // Attempt to parse each message as JSON into a `CodeMessage`.
        let messages: Vec<CodeError> = messages
            .iter()
            .map(|message| -> CodeError {
                serde_json::from_str(message).unwrap_or_else(|_| CodeError {
                    error_message: message.into(),
                    ..Default::default()
                })
            })
            .collect();

        Ok((outputs, messages))
    }
}

/// Handle a line of stdout or stderr
///
/// How the line is handled depends upon whether it has a result or transaction
/// separator at the end. Returns false at the end of a transaction.
fn push_line(line: &str, current: &mut String, vec: &mut Vec<String>) -> bool {
    if let Some(line) = line
        .strip_suffix(RESULT)
        .or_else(|| line.strip_suffix(RESULT_ALT))
    {
        current.push_str(line);
        if !current.is_empty() {
            vec.push(current.clone());
            current.clear();
        }
        true
    } else if let Some(line) = line
        .strip_suffix(TRANS)
        .or_else(|| line.strip_suffix(TRANS_ALT))
    {
        current.push_str(line);
        if !current.is_empty() {
            vec.push(current.clone());
        }
        false
    } else {
        current.push_str(line);
        current.push('\n');
        true
    }
}
<|endoftext|>


<commit_before>
use super::Compat;
use crate::{TryFutureExt, FutureExt, future::UnitError};
use futures::future::Executor as Executor01;
use futures_core::task::Executor as Executor03;
use futures_core::task as task03;
use futures_core::future::FutureObj;

pub struct BoxedExecutor03(Box<dyn Executor03 + Send>);

impl Executor03 for BoxedExecutor03 {
    fn spawn_obj(
        &mut self,
        future: FutureObj<'static, ()>,
    ) -> Result<(), task03::SpawnObjError> {
        (&mut *self.0).spawn_obj(future)
    }
}

/// A future that can run on a futures 0.1 executor.
pub type Executor01Future = Compat<UnitError<FutureObj<'static, ()>>, BoxedExecutor03>;

/// Extension trait for futures 0.1 Executors.
pub trait Executor01CompatExt: Executor01<Executor01Future> +
                               Clone + Send + 'static
{
    /// Creates an `Executor` compatable with futures 0.3.
    fn compat(self) -> Executor01As03<Self>
        where Self: Sized;
}

impl<Ex> Executor01CompatExt for Ex
where Ex: Executor01<Executor01Future> + Clone + Send + 'static
{
    fn compat(self) -> Executor01As03<Self> {
        Executor01As03 {
            executor01: self,
        }
    }
}

/// Converts a futures 0.1 `Executor` into a futures 0.3 `Executor`.
#[derive(Clone)]
pub struct Executor01As03<Ex> {
    executor01: Ex
}

impl<Ex> Executor03 for Executor01As03<Ex>
where Ex: Executor01<Executor01Future>,
      Ex: Clone + Send + 'static,
{
    fn spawn_obj(
        &mut self,
        future: FutureObj<'static, ()>,
    ) -> Result<(), task03::SpawnObjError> {
        let future = future.unit_error().compat(BoxedExecutor03(Box::new(self.clone())));

        match self.executor01.execute(future) {
            Ok(()) => Ok(()),
            Err(err) => {
                use futures_core::task::{SpawnObjError, SpawnErrorKind};

                let fut = err.into_future().into_inner().unwrap_or_else(|_| ());
                Err(SpawnObjError {
                    kind: SpawnErrorKind::shutdown(),
                    future: Box::new(fut).into(),
                })
            }
        }
    }
}
<commit_msg>remove BoxedExecutor<commit_after>
use super::Compat;
use crate::{TryFutureExt, FutureExt, future::UnitError};
use futures::future::Executor as Executor01;
use futures_core::task::Executor as Executor03;
use futures_core::task as task03;
use futures_core::future::FutureObj;

/// A future that can run on a futures 0.1 executor.
pub type Executor01Future = Compat<UnitError<FutureObj<'static, ()>>, Box<dyn Executor03 + Send>>;

/// Extension trait for futures 0.1 Executors.
pub trait Executor01CompatExt: Executor01<Executor01Future> +
                               Clone + Send + 'static
{
    /// Creates an `Executor` compatable with futures 0.3.
    fn compat(self) -> Executor01As03<Self>
        where Self: Sized;
}

impl<Ex> Executor01CompatExt for Ex
where Ex: Executor01<Executor01Future> + Clone + Send + 'static
{
    fn compat(self) -> Executor01As03<Self> {
        Executor01As03 {
            executor01: self,
        }
    }
}

/// Converts a futures 0.1 `Executor` into a futures 0.3 `Executor`.
#[derive(Clone)]
pub struct Executor01As03<Ex> {
    executor01: Ex
}

impl<Ex> Executor03 for Executor01As03<Ex>
where Ex: Executor01<Executor01Future>,
      Ex: Clone + Send + 'static,
{
    fn spawn_obj(
        &mut self,
        future: FutureObj<'static, ()>,
    ) -> Result<(), task03::SpawnObjError> {
        let exec: Box<dyn Executor03 + Send> = Box::new(self.clone());
        let future = future.unit_error().compat(exec);

        match self.executor01.execute(future) {
            Ok(()) => Ok(()),
            Err(err) => {
                use futures_core::task::{SpawnObjError, SpawnErrorKind};

                let fut = err.into_future().into_inner().unwrap_or_else(|_| ());
                Err(SpawnObjError {
                    kind: SpawnErrorKind::shutdown(),
                    future: Box::new(fut).into(),
                })
            }
        }
    }
}
<|endoftext|>