<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/. */

use url::Url;
use hyper::method::Method;
use hyper::mime::{Mime, TopLevel, SubLevel, Attr, Value};
use hyper::header::{Headers, ContentType, IfModifiedSince, IfNoneMatch, Accept};
use hyper::header::{IfUnmodifiedSince, IfMatch, IfRange, Location, HeaderView};
use hyper::header::{AcceptLanguage, ContentLanguage};
use hyper::status::StatusCode;
use fetch::cors_cache::{CORSCache, CacheRequestDetails};
use fetch::response::{Response, ResponseType};
use std::ascii::AsciiExt;

/// A [request context](https://fetch.spec.whatwg.org/#concept-request-context)
#[derive(Copy, Clone, PartialEq)]
pub enum Context {
    Audio, Beacon, CSPreport, Download, Embed, Eventsource,
    Favicon, Fetch, Font, Form, Frame, Hyperlink, IFrame, Image,
    ImageSet, Import, Internal, Location, Manifest, Object, Ping,
    Plugin, Prefetch, Script, ServiceWorker, SharedWorker, Subresource,
    Style, Track, Video, Worker, XMLHttpRequest, XSLT
}

/// A [request context frame type](https://fetch.spec.whatwg.org/#concept-request-context-frame-type)
#[derive(Copy, Clone, PartialEq)]
pub enum ContextFrameType {
    Auxiliary,
    TopLevel,
    Nested,
    ContextNone
}

/// A [referer](https://fetch.spec.whatwg.org/#concept-request-referrer)
pub enum Referer {
    RefererNone,
    Client,
    RefererUrl(Url)
}

/// A [request mode](https://fetch.spec.whatwg.org/#concept-request-mode)
#[derive(Copy, Clone, PartialEq)]
pub enum RequestMode {
    SameOrigin,
    NoCORS,
    CORSMode,
    ForcedPreflightMode
}

/// Request [credentials mode](https://fetch.spec.whatwg.org/#concept-request-credentials-mode)
#[derive(Copy, Clone, PartialEq)]
pub enum CredentialsMode {
    Omit,
    CredentialsSameOrigin,
    Include
}

/// [Cache mode](https://fetch.spec.whatwg.org/#concept-request-cache-mode)
#[derive(Copy, Clone, PartialEq)]
pub enum CacheMode {
    Default,
    NoStore,
    Reload,
    NoCache,
    ForceCache,
    OnlyIfCached
}

/// [Redirect mode](https://fetch.spec.whatwg.org/#concept-request-redirect-mode)
#[derive(Copy, Clone, PartialEq)]
pub enum RedirectMode {
    Follow,
    Error,
    Manual
}

/// [Response tainting](https://fetch.spec.whatwg.org/#concept-request-response-tainting)
#[derive(Copy, Clone, PartialEq)]
pub enum ResponseTainting {
    Basic,
    CORSTainting,
    Opaque
}

/// A [Request](https://fetch.spec.whatwg.org/#requests) as defined by the Fetch spec
pub struct Request {
    pub method: Method,
    pub url: Url,
    pub headers: Headers,
    pub unsafe_request: bool,
    pub body: Option<Vec<u8>>,
    pub preserve_content_codings: bool,
    // pub client: GlobalRef, // XXXManishearth copy over only the relevant fields of the global scope,
                              // not the entire scope to avoid the libscript dependency
    pub is_service_worker_global_scope: bool,
    pub skip_service_worker: bool,
    pub context: Context,
    pub context_frame_type: ContextFrameType,
    pub origin: Option<Url>,
    pub force_origin_header: bool,
    pub same_origin_data: bool,
    pub referer: Referer,
    pub authentication: bool,
    pub sync: bool,
    pub mode: RequestMode,
    pub credentials_mode: CredentialsMode,
    pub use_url_credentials: bool,
    pub cache_mode: CacheMode,
    pub redirect_mode: RedirectMode,
    pub redirect_count: usize,
    pub response_tainting: ResponseTainting,
    pub cache: Option<Box<CORSCache+'static>>
}

impl Request {
    pub fn new(url: Url, context: Context, is_service_worker_global_scope: bool) -> Request {
         Request {
            method: Method::Get,
            url: url,
            headers: Headers::new(),
            unsafe_request: false,
            body: None,
            preserve_content_codings: false,
            is_service_worker_global_scope: is_service_worker_global_scope,
            skip_service_worker: false,
            context: context,
            context_frame_type: ContextFrameType::ContextNone,
            origin: None,
            force_origin_header: false,
            same_origin_data: false,
            referer: Referer::Client,
            authentication: false,
            sync: false,
            mode: RequestMode::NoCORS,
            credentials_mode: CredentialsMode::Omit,
            use_url_credentials: false,
            cache_mode: CacheMode::Default,
            redirect_mode: RedirectMode::Follow,
            redirect_count: 0,
            response_tainting: ResponseTainting::Basic,
            cache: None
        }
    }

    // [Fetch](https://fetch.spec.whatwg.org#concept-fetch)
    pub fn fetch(&mut self, _cors_flag: bool) -> Response {
        // TODO: Implement fetch spec
        Response::network_error()
    }

    /// [Basic fetch](https://fetch.spec.whatwg.org#basic-fetch)
    pub fn basic_fetch(&mut self) -> Response {
        match &*self.url.scheme {
            "about" => match self.url.non_relative_scheme_data() {
                Some(s) if &*s == "blank" => {
                    let mut response = Response::new();
                    response.headers.set(ContentType(Mime(
                        TopLevel::Text, SubLevel::Html,
                        vec![(Attr::Charset, Value::Utf8)])));
                    response
                },
                _ => Response::network_error()
            },
            "http" | "https" => {
                self.http_fetch(false, false, false)
            },
            "blob" | "data" | "file" | "ftp" => {
                // XXXManishearth handle these
                panic!("Unimplemented scheme for Fetch")
            },

            _ => Response::network_error()
        }
    }

    // [HTTP fetch](https://fetch.spec.whatwg.org#http-fetch)
    pub fn http_fetch(&mut self, cors_flag: bool, cors_preflight_flag: bool, authentication_fetch_flag: bool) -> Response {
        // Step 1
        let mut response: Option<Response> = None;
        // Step 2
        if !self.skip_service_worker && !self.is_service_worker_global_scope {
            // TODO: Substep 1 (handle fetch unimplemented)
            // Substep 2
            if let Some(ref res) = response {
                if (res.response_type == ResponseType::Opaque && self.mode != RequestMode::NoCORS) ||
                   res.response_type == ResponseType::Error {
                    return Response::network_error();
                }
            }
        }
        // Step 3
        if response.is_none() {
            // Substep 1
            if cors_preflight_flag {
                let mut method_mismatch = false;
                let mut header_mismatch = false;
                if let Some(ref mut cache) = self.cache {
                    let origin = self.origin.clone().unwrap_or(Url::parse("").unwrap());
                    let url = self.url.clone();
                    let credentials = self.credentials_mode == CredentialsMode::Include;
                    let method_cache_match = cache.match_method(CacheRequestDetails {
                        origin: origin.clone(),
                        destination: url.clone(),
                        credentials: credentials
                    }, self.method.clone());
                    method_mismatch = !method_cache_match && (!is_simple_method(&self.method) ||
                        self.mode == RequestMode::ForcedPreflightMode);
                    header_mismatch = self.headers.iter().any(|view|
                        !cache.match_header(CacheRequestDetails {
                            origin: origin.clone(),
                            destination: url.clone(),
                            credentials: credentials
                        }, view.name()) && !is_simple_header(&view)
                        );
                }
                if method_mismatch || header_mismatch {
                    let preflight_result = self.preflight_fetch();
                    if preflight_result.response_type == ResponseType::Error {
                        return Response::network_error();
                    }
                    response = Some(preflight_result);
                }
            }
            // Substep 2
            self.skip_service_worker = true;
            // Substep 3
            let credentials = match self.credentials_mode {
                CredentialsMode::Include => true,
                CredentialsMode::CredentialsSameOrigin if !cors_flag => true,
                _ => false
            };
            // Substep 4
            if self.cache_mode == CacheMode::Default && is_no_store_cache(&self.headers) {
                self.cache_mode = CacheMode::NoStore;
            }
            // Substep 5
            let fetch_result = self.http_network_or_cache_fetch(credentials, authentication_fetch_flag);
            // Substep 6
            if cors_flag && self.cors_check(&fetch_result).is_err() {
                return Response::network_error();
            }
            response = Some(fetch_result);
        }
        // Step 4
        let mut response = response.unwrap();
        match response.status.unwrap() {
            // Code 304
            StatusCode::NotModified => match self.cache_mode {
                CacheMode::Default | CacheMode::NoCache => {
                    // TODO: Check HTTP cache for request and response entry
                }
                _ => { }
            },
            // Code 301, 302, 303, 307, 308
            StatusCode::MovedPermanently | StatusCode::Found | StatusCode::SeeOther |
            StatusCode::TemporaryRedirect | StatusCode::PermanentRedirect => {
                // Step 1
                if self.redirect_mode == RedirectMode::Error {
                    return Response::network_error();
                }
                // Step 2-4
                if !response.headers.has::<Location>() {
                    return response;
                }
                let location = match response.headers.get::<Location>() {
                    None => return Response::network_error(),
                    Some(location) => location,
                };
                // Step 5
                let location_url = Url::parse(location);
                // Step 6
                let location_url = match location_url {
                    Ok(url) => url,
                    Err(_) => return Response::network_error()
                };
                // Step 7
                if self.redirect_count == 20 {
                    return Response::network_error();
                }
                // Step 8
                self.redirect_count += 1;
                // Step 9
                self.same_origin_data = false;
                // Step 10
                if self.redirect_mode == RedirectMode::Follow {
                    // FIXME: Origin method of the Url crate hasn't been implemented (https://github.com/servo/rust-url/issues/54)
                    // Substep 1
                    // if cors_flag && location_url.origin() != self.url.origin() { self.origin = None; }
                    // Substep 2
                    if cors_flag && (!location_url.username().unwrap_or("").is_empty() ||
                                      location_url.password().is_some()) {
                        return Response::network_error();
                    }
                    // Substep 3
                    if response.status.unwrap() == StatusCode::MovedPermanently ||
                       response.status.unwrap() == StatusCode::SeeOther ||
                       (response.status.unwrap() == StatusCode::Found && self.method == Method::Post) {
                        self.method = Method::Get;
                    }
                    // Substep 4
                    self.url = location_url;
                    // Substep 5
                    return self.fetch(cors_flag);
                }
            }
            // Code 401
            StatusCode::Unauthorized => {
                // Step 1
                if !self.authentication || cors_flag {
                    return response;
                }
                // Step 2
                // TODO: Spec says requires testing
                // Step 3
                if !self.use_url_credentials || authentication_fetch_flag {
                    // TODO: Prompt the user for username and password
                }
                return self.http_fetch(cors_flag, cors_preflight_flag, true);
            }
            // Code 407
            StatusCode::ProxyAuthenticationRequired => {
                // Step 1
                // TODO: Spec says requires testing
                // Step 2
                // TODO: Prompt the user for proxy authentication credentials
                // Step 3
                return self.http_fetch(cors_flag, cors_preflight_flag, authentication_fetch_flag);
            }
            _ => { }
        }
        // Step 5
        if authentication_fetch_flag {
            // TODO: Create authentication entry for this request
        }
        // Step 6
        response
    }

    // [HTTP network or cache fetch](https://fetch.spec.whatwg.org#http-network-or-cache-fetch)
    pub fn http_network_or_cache_fetch(&mut self, _credentials_flag: bool, _authentication_fetch_flag: bool) -> Response {
        // TODO: Implement HTTP network or cache fetch spec
        Response::network_error()
    }

    // [CORS preflight fetch](https://fetch.spec.whatwg.org#cors-preflight-fetch)
    pub fn preflight_fetch(&mut self) -> Response {
        // TODO: Implement preflight fetch spec
        Response::network_error()
    }

    // [CORS check](https://fetch.spec.whatwg.org#concept-cors-check)
    pub fn cors_check(&mut self, response: &Response) -> Result<(), ()> {
        // TODO: Implement CORS check spec
        Err(())
    }
}

fn is_no_store_cache(headers: &Headers) -> bool {
    headers.has::<IfModifiedSince>() | headers.has::<IfNoneMatch>() |
    headers.has::<IfUnmodifiedSince>() | headers.has::<IfMatch>() |
    headers.has::<IfRange>()
}

fn is_simple_header(h: &HeaderView) -> bool {
    if h.is::<ContentType>() {
        match h.value() {
            Some(&ContentType(Mime(TopLevel::Text, SubLevel::Plain, _))) |
            Some(&ContentType(Mime(TopLevel::Application, SubLevel::WwwFormUrlEncoded, _))) |
            Some(&ContentType(Mime(TopLevel::Multipart, SubLevel::FormData, _))) => true,
            _ => false

        }
    } else {
        h.is::<Accept>() || h.is::<AcceptLanguage>() || h.is::<ContentLanguage>()
    }
}

fn is_simple_method(m: &Method) -> bool {
    match *m {
        Method::Get | Method::Head | Method::Post => true,
        _ => false
    }
}
<commit_msg>Auto merge of #6129 - KiChjang:fetch-spec, r=jdm<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/. */

use url::Url;
use hyper::method::Method;
use hyper::mime::{Mime, TopLevel, SubLevel, Attr, Value};
use hyper::header::{Header, Headers, ContentType, IfModifiedSince, IfNoneMatch};
use hyper::header::{Accept, IfUnmodifiedSince, IfMatch, IfRange, Location};
use hyper::header::{HeaderView, AcceptLanguage, ContentLanguage, Language};
use hyper::header::{QualityItem, qitem, q};
use hyper::status::StatusCode;
use fetch::cors_cache::{CORSCache, CacheRequestDetails};
use fetch::response::{Response, ResponseType};
use std::ascii::AsciiExt;
use std::str::FromStr;

/// A [request context](https://fetch.spec.whatwg.org/#concept-request-context)
#[derive(Copy, Clone, PartialEq)]
pub enum Context {
    Audio, Beacon, CSPreport, Download, Embed, Eventsource,
    Favicon, Fetch, Font, Form, Frame, Hyperlink, IFrame, Image,
    ImageSet, Import, Internal, Location, Manifest, MetaRefresh, Object,
    Ping, Plugin, Prefetch, PreRender, Script, ServiceWorker, SharedWorker,
    Subresource, Style, Track, Video, Worker, XMLHttpRequest, XSLT
}

/// A [request context frame type](https://fetch.spec.whatwg.org/#concept-request-context-frame-type)
#[derive(Copy, Clone, PartialEq)]
pub enum ContextFrameType {
    Auxiliary,
    TopLevel,
    Nested,
    ContextNone
}

/// A [referer](https://fetch.spec.whatwg.org/#concept-request-referrer)
pub enum Referer {
    RefererNone,
    Client,
    RefererUrl(Url)
}

/// A [request mode](https://fetch.spec.whatwg.org/#concept-request-mode)
#[derive(Copy, Clone, PartialEq)]
pub enum RequestMode {
    SameOrigin,
    NoCORS,
    CORSMode,
    ForcedPreflightMode
}

/// Request [credentials mode](https://fetch.spec.whatwg.org/#concept-request-credentials-mode)
#[derive(Copy, Clone, PartialEq)]
pub enum CredentialsMode {
    Omit,
    CredentialsSameOrigin,
    Include
}

/// [Cache mode](https://fetch.spec.whatwg.org/#concept-request-cache-mode)
#[derive(Copy, Clone, PartialEq)]
pub enum CacheMode {
    Default,
    NoStore,
    Reload,
    NoCache,
    ForceCache,
    OnlyIfCached
}

/// [Redirect mode](https://fetch.spec.whatwg.org/#concept-request-redirect-mode)
#[derive(Copy, Clone, PartialEq)]
pub enum RedirectMode {
    Follow,
    Error,
    Manual
}

/// [Response tainting](https://fetch.spec.whatwg.org/#concept-request-response-tainting)
#[derive(Copy, Clone, PartialEq)]
pub enum ResponseTainting {
    Basic,
    CORSTainting,
    Opaque
}

/// A [Request](https://fetch.spec.whatwg.org/#requests) as defined by the Fetch spec
pub struct Request {
    pub method: Method,
    pub url: Url,
    pub headers: Headers,
    pub unsafe_request: bool,
    pub body: Option<Vec<u8>>,
    pub preserve_content_codings: bool,
    // pub client: GlobalRef, // XXXManishearth copy over only the relevant fields of the global scope,
                              // not the entire scope to avoid the libscript dependency
    pub is_service_worker_global_scope: bool,
    pub skip_service_worker: bool,
    pub context: Context,
    pub context_frame_type: ContextFrameType,
    pub origin: Option<Url>,
    pub force_origin_header: bool,
    pub same_origin_data: bool,
    pub referer: Referer,
    pub authentication: bool,
    pub sync: bool,
    pub mode: RequestMode,
    pub credentials_mode: CredentialsMode,
    pub use_url_credentials: bool,
    pub cache_mode: CacheMode,
    pub redirect_mode: RedirectMode,
    pub redirect_count: usize,
    pub response_tainting: ResponseTainting,
    pub cache: Option<Box<CORSCache+'static>>
}

impl Request {
    pub fn new(url: Url, context: Context, is_service_worker_global_scope: bool) -> Request {
         Request {
            method: Method::Get,
            url: url,
            headers: Headers::new(),
            unsafe_request: false,
            body: None,
            preserve_content_codings: false,
            is_service_worker_global_scope: is_service_worker_global_scope,
            skip_service_worker: false,
            context: context,
            context_frame_type: ContextFrameType::ContextNone,
            origin: None,
            force_origin_header: false,
            same_origin_data: false,
            referer: Referer::Client,
            authentication: false,
            sync: false,
            mode: RequestMode::NoCORS,
            credentials_mode: CredentialsMode::Omit,
            use_url_credentials: false,
            cache_mode: CacheMode::Default,
            redirect_mode: RedirectMode::Follow,
            redirect_count: 0,
            response_tainting: ResponseTainting::Basic,
            cache: None
        }
    }

    /// [Fetch](https://fetch.spec.whatwg.org#concept-fetch)
    pub fn fetch(&mut self, cors_flag: bool) -> Response {
        // Step 1
        if self.context != Context::Fetch && !self.headers.has::<Accept>() {
            // Substep 1
            let value = match self.context {
                Context::Favicon | Context::Image | Context::ImageSet
                    => vec![qitem(Mime(TopLevel::Image, SubLevel::Png, vec![])),
                        // FIXME: This should properly generate a MimeType that has a
                        // SubLevel of svg+xml (https://github.com/hyperium/mime.rs/issues/22)
                        qitem(Mime(TopLevel::Image, SubLevel::Ext("svg+xml".to_string()), vec![])),
                        QualityItem::new(Mime(TopLevel::Image, SubLevel::Star, vec![]), q(0.8)),
                        QualityItem::new(Mime(TopLevel::Star, SubLevel::Star, vec![]), q(0.5))],
                Context::Form | Context::Frame | Context::Hyperlink |
                Context::IFrame | Context::Location | Context::MetaRefresh |
                Context::PreRender
                    => vec![qitem(Mime(TopLevel::Text, SubLevel::Html, vec![])),
                        // FIXME: This should properly generate a MimeType that has a
                        // SubLevel of xhtml+xml (https://github.com/hyperium/mime.rs/issues/22)
                        qitem(Mime(TopLevel::Application, SubLevel::Ext("xhtml+xml".to_string()), vec![])),
                        QualityItem::new(Mime(TopLevel::Application, SubLevel::Xml, vec![]), q(0.9)),
                        QualityItem::new(Mime(TopLevel::Star, SubLevel::Star, vec![]), q(0.8))],
                Context::Internal if self.context_frame_type != ContextFrameType::ContextNone
                    => vec![qitem(Mime(TopLevel::Text, SubLevel::Html, vec![])),
                        // FIXME: This should properly generate a MimeType that has a
                        // SubLevel of xhtml+xml (https://github.com/hyperium/mime.rs/issues/22)
                        qitem(Mime(TopLevel::Application, SubLevel::Ext("xhtml+xml".to_string()), vec![])),
                        QualityItem::new(Mime(TopLevel::Application, SubLevel::Xml, vec![]), q(0.9)),
                        QualityItem::new(Mime(TopLevel::Star, SubLevel::Star, vec![]), q(0.8))],
                Context::Style
                    => vec![qitem(Mime(TopLevel::Text, SubLevel::Css, vec![])),
                        QualityItem::new(Mime(TopLevel::Star, SubLevel::Star, vec![]), q(0.1))],
                _ => vec![qitem(Mime(TopLevel::Star, SubLevel::Star, vec![]))]
            };
            // Substep 2
            self.headers.set(Accept(value));
        }
        // Step 2
        if self.context != Context::Fetch && !self.headers.has::<AcceptLanguage>() {
            self.headers.set(AcceptLanguage(vec![qitem(Language::from_str("en-US").unwrap())]));
        }
        // TODO: Figure out what a Priority object is
        // Step 3
        // Step 4
        self.main_fetch(cors_flag)
    }

    /// [Main fetch](https://fetch.spec.whatwg.org/#concept-main-fetch)
    pub fn main_fetch(&mut self, _cors_flag: bool) -> Response {
        // TODO: Implement main fetch spec
        Response::network_error()
    }

    /// [Basic fetch](https://fetch.spec.whatwg.org#basic-fetch)
    pub fn basic_fetch(&mut self) -> Response {
        match &*self.url.scheme {
            "about" => match self.url.non_relative_scheme_data() {
                Some(s) if &*s == "blank" => {
                    let mut response = Response::new();
                    response.headers.set(ContentType(Mime(
                        TopLevel::Text, SubLevel::Html,
                        vec![(Attr::Charset, Value::Utf8)])));
                    response
                },
                _ => Response::network_error()
            },
            "http" | "https" => {
                self.http_fetch(false, false, false)
            },
            "blob" | "data" | "file" | "ftp" => {
                // XXXManishearth handle these
                panic!("Unimplemented scheme for Fetch")
            },

            _ => Response::network_error()
        }
    }

    /// [HTTP fetch](https://fetch.spec.whatwg.org#http-fetch)
    pub fn http_fetch(&mut self, cors_flag: bool, cors_preflight_flag: bool, authentication_fetch_flag: bool) -> Response {
        // Step 1
        let mut response: Option<Response> = None;
        // Step 2
        if !self.skip_service_worker && !self.is_service_worker_global_scope {
            // TODO: Substep 1 (handle fetch unimplemented)
            // Substep 2
            if let Some(ref res) = response {
                if (res.response_type == ResponseType::Opaque && self.mode != RequestMode::NoCORS) ||
                   res.response_type == ResponseType::Error {
                    return Response::network_error();
                }
            }
        }
        // Step 3
        if response.is_none() {
            // Substep 1
            if cors_preflight_flag {
                let mut method_mismatch = false;
                let mut header_mismatch = false;
                if let Some(ref mut cache) = self.cache {
                    let origin = self.origin.clone().unwrap_or(Url::parse("").unwrap());
                    let url = self.url.clone();
                    let credentials = self.credentials_mode == CredentialsMode::Include;
                    let method_cache_match = cache.match_method(CacheRequestDetails {
                        origin: origin.clone(),
                        destination: url.clone(),
                        credentials: credentials
                    }, self.method.clone());
                    method_mismatch = !method_cache_match && (!is_simple_method(&self.method) ||
                        self.mode == RequestMode::ForcedPreflightMode);
                    header_mismatch = self.headers.iter().any(|view|
                        !cache.match_header(CacheRequestDetails {
                            origin: origin.clone(),
                            destination: url.clone(),
                            credentials: credentials
                        }, view.name()) && !is_simple_header(&view)
                        );
                }
                if method_mismatch || header_mismatch {
                    let preflight_result = self.preflight_fetch();
                    if preflight_result.response_type == ResponseType::Error {
                        return Response::network_error();
                    }
                    response = Some(preflight_result);
                }
            }
            // Substep 2
            self.skip_service_worker = true;
            // Substep 3
            let credentials = match self.credentials_mode {
                CredentialsMode::Include => true,
                CredentialsMode::CredentialsSameOrigin if !cors_flag => true,
                _ => false
            };
            // Substep 4
            if self.cache_mode == CacheMode::Default && is_no_store_cache(&self.headers) {
                self.cache_mode = CacheMode::NoStore;
            }
            // Substep 5
            let fetch_result = self.http_network_or_cache_fetch(credentials, authentication_fetch_flag);
            // Substep 6
            if cors_flag && self.cors_check(&fetch_result).is_err() {
                return Response::network_error();
            }
            response = Some(fetch_result);
        }
        // Step 4
        let mut response = response.unwrap();
        match response.status.unwrap() {
            // Code 304
            StatusCode::NotModified => match self.cache_mode {
                CacheMode::Default | CacheMode::NoCache => {
                    // TODO: Check HTTP cache for request and response entry
                }
                _ => { }
            },
            // Code 301, 302, 303, 307, 308
            StatusCode::MovedPermanently | StatusCode::Found | StatusCode::SeeOther |
            StatusCode::TemporaryRedirect | StatusCode::PermanentRedirect => {
                // Step 1
                if self.redirect_mode == RedirectMode::Error {
                    return Response::network_error();
                }
                // Step 2-4
                if !response.headers.has::<Location>() {
                    return response;
                }
                let location = match response.headers.get::<Location>() {
                    None => return Response::network_error(),
                    Some(location) => location,
                };
                // Step 5
                let location_url = Url::parse(location);
                // Step 6
                let location_url = match location_url {
                    Ok(url) => url,
                    Err(_) => return Response::network_error()
                };
                // Step 7
                if self.redirect_count == 20 {
                    return Response::network_error();
                }
                // Step 8
                self.redirect_count += 1;
                // Step 9
                self.same_origin_data = false;
                // Step 10
                if self.redirect_mode == RedirectMode::Follow {
                    // FIXME: Origin method of the Url crate hasn't been implemented (https://github.com/servo/rust-url/issues/54)
                    // Substep 1
                    // if cors_flag && location_url.origin() != self.url.origin() { self.origin = None; }
                    // Substep 2
                    if cors_flag && (!location_url.username().unwrap_or("").is_empty() ||
                                      location_url.password().is_some()) {
                        return Response::network_error();
                    }
                    // Substep 3
                    if response.status.unwrap() == StatusCode::MovedPermanently ||
                       response.status.unwrap() == StatusCode::SeeOther ||
                       (response.status.unwrap() == StatusCode::Found && self.method == Method::Post) {
                        self.method = Method::Get;
                    }
                    // Substep 4
                    self.url = location_url;
                    // Substep 5
                    return self.fetch(cors_flag);
                }
            }
            // Code 401
            StatusCode::Unauthorized => {
                // Step 1
                if !self.authentication || cors_flag {
                    return response;
                }
                // Step 2
                // TODO: Spec says requires testing
                // Step 3
                if !self.use_url_credentials || authentication_fetch_flag {
                    // TODO: Prompt the user for username and password
                }
                return self.http_fetch(cors_flag, cors_preflight_flag, true);
            }
            // Code 407
            StatusCode::ProxyAuthenticationRequired => {
                // Step 1
                // TODO: Spec says requires testing
                // Step 2
                // TODO: Prompt the user for proxy authentication credentials
                // Step 3
                return self.http_fetch(cors_flag, cors_preflight_flag, authentication_fetch_flag);
            }
            _ => { }
        }
        // Step 5
        if authentication_fetch_flag {
            // TODO: Create authentication entry for this request
        }
        // Step 6
        response
    }

    /// [HTTP network or cache fetch](https://fetch.spec.whatwg.org#http-network-or-cache-fetch)
    pub fn http_network_or_cache_fetch(&mut self, _credentials_flag: bool, _authentication_fetch_flag: bool) -> Response {
        // TODO: Implement HTTP network or cache fetch spec
        Response::network_error()
    }

    /// [CORS preflight fetch](https://fetch.spec.whatwg.org#cors-preflight-fetch)
    pub fn preflight_fetch(&mut self) -> Response {
        // TODO: Implement preflight fetch spec
        Response::network_error()
    }

    /// [CORS check](https://fetch.spec.whatwg.org#concept-cors-check)
    pub fn cors_check(&mut self, response: &Response) -> Result<(), ()> {
        // TODO: Implement CORS check spec
        Err(())
    }
}

fn is_no_store_cache(headers: &Headers) -> bool {
    headers.has::<IfModifiedSince>() | headers.has::<IfNoneMatch>() |
    headers.has::<IfUnmodifiedSince>() | headers.has::<IfMatch>() |
    headers.has::<IfRange>()
}

fn is_simple_header(h: &HeaderView) -> bool {
    if h.is::<ContentType>() {
        match h.value() {
            Some(&ContentType(Mime(TopLevel::Text, SubLevel::Plain, _))) |
            Some(&ContentType(Mime(TopLevel::Application, SubLevel::WwwFormUrlEncoded, _))) |
            Some(&ContentType(Mime(TopLevel::Multipart, SubLevel::FormData, _))) => true,
            _ => false

        }
    } else {
        h.is::<Accept>() || h.is::<AcceptLanguage>() || h.is::<ContentLanguage>()
    }
}

fn is_simple_method(m: &Method) -> bool {
    match *m {
        Method::Get | Method::Head | Method::Post => true,
        _ => false
    }
}
<|endoftext|>


<commit_before>#[derive(Debug,Clone,PartialEq)]
pub enum Token {
  // Structure
  EOF,
  Enter,
  Exit,

  // Literals
  Null,
  Bool(bool),
  Float(f64),
  Int(i64),
  StrLit(String),
  Name(String),

  // Keywords
  Break,
  Continue,
  Func,
  If,
  Return,

  // Symbols
  Arr, // ->
  Col, // :
  Com, // ,
  Dot, // .
  Eql, // =

  // Braces
  Cul, // {
  Cur, // }
  Pal, // (
  Par, // )
  Sql, // [
  Sqr, // ]

  // Operators
  Add, // +
  Div, // /
  Mul, // *
  Sub, // -
}

use self::Token::*;

pub fn lex(path: &str) {
  println!("HELLO I AM LEXING {}", path);
}
<commit_msg>lexer: Add some dummy lexing<commit_after>#[derive(Debug,Clone,PartialEq)]
pub enum Token {
  // Structure
  EOF,
  Enter,
  Exit,

  // Literals
  Null,
  Bool(bool),
  Float(f64),
  Int(i64),
  StrLit(String),
  Name(String),

  // Keywords
  Break,
  Continue,
  Else,
  For,
  Func,
  If,
  Return,
  While,

  // Symbols
  Arr, // ->
  Col, // :
  Com, // ,
  Dot, // .
  Ass, // =

  // Braces
  Cul, // {
  Cur, // }
  Pal, // (
  Par, // )
  Sql, // [
  Sqr, // ]

  // Operators
  Add, // +
  Div, // /
  Mul, // *
  Sub, // -
  Dol, // $
  Pct, // %
  Car, // ^
  And, // &
  Or,  // |
  Not, // !
  Neg, // ~

  // Comparisons
  Gt,  // >
  Ge,  // >=
  Lt,  // <
  Le,  // <=
  Ne,  // !=
  Eql, // ==
}

use self::Token::*;

pub fn lex(input: &str) {
  let mut tokens: Vec<Token> = Vec::new();
  let mut it = input.chars().peekable();

  while let Some(&c) = it.peek() {
    match c {
      '-' => {
        it.next();
        match it.peek() {
          Some(&'>') => {
            tokens.push(Arr);
            it.next();
          }
          _ => {
            tokens.push(Sub);
          }
        }
      }
      '+' => {tokens.push(Add); it.next();}
      '/' => {tokens.push(Div); it.next();}
      '*' => {tokens.push(Mul); it.next();}
      '(' => {tokens.push(Pal); it.next();}
      ')' => {tokens.push(Par); it.next();}
      '[' => {tokens.push(Sql); it.next();}
      ']' => {tokens.push(Sqr); it.next();}
      '{' => {tokens.push(Cul); it.next();}
      '}' => {tokens.push(Cur); it.next();}
      '<' => {tokens.push(Lt); it.next();}
      '>' => {tokens.push(Gt); it.next();}
      _ => {it.next();}
    }
  }

  println!("{:?}", tokens);
}
<|endoftext|>


<commit_before>// Copyright © 2015 Sebastian Thiel
// Copyright © 2015 Andy Grover
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// “Software”), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

// Base a lexer for LVM2's text format on the more complex (hah) json format.
// This code is based on https://github.com/Byron/json-tools.

/// A lexer for utf-8 encoded json data
pub struct Lexer<'a> {
    chars: &'a[u8],
    next_byte: Option<u8>,
    cursor: usize,
}

#[derive(Debug, PartialEq, Clone)]
pub enum Token<'a> {
    /// `{`
    CurlyOpen,
    /// `}`
    CurlyClose,

    /// `[`
    BracketOpen,
    /// `]`
    BracketClose,

    /// `=`
    Equals,
    /// `,`
    Comma,

    /// A string , like `"foo"`
    String(&'a[u8]),

    Ident(&'a[u8]),

    /// An unsigned integer number
    Number(u64),

    Comment(&'a[u8]),

    /// The type of the token could not be identified.
    /// Should be removed if this lexer is ever to be feature complete
    Invalid(u8),
}

impl<'a> AsRef<str> for Token<'a> {
    fn as_ref(&self) -> &str {
        match *self {
            Token::CurlyOpen => "{",
            Token::CurlyClose => "}",
            Token::BracketOpen => "[",
            Token::BracketClose => "]",
            Token::Equals => "=",
            Token::Comma => ",",

            Token::Invalid(c) => panic!("Cannot convert invalid Token {}", c),
            _ => panic!("Cannot convert variant Tokens"),
        }
    }
}

impl<'a> Lexer<'a> {
    /// Returns a new Lexer from a given byte iterator.
    pub fn new(chars: &'a[u8]) -> Lexer<'a> {
        Lexer {
            chars: chars,
            next_byte: None,
            cursor: 0,
        }
    }

    fn put_back(&mut self, c: u8) {
        debug_assert!(self.next_byte.is_none());
        self.next_byte = Some(c);
        self.cursor -= 1;
    }

    fn next_byte(&mut self) -> Option<u8> {
        match self.next_byte.take() {
            Some(c) => {
                self.cursor += 1;
                Some(c)
            },
            None => {
                if self.cursor >= self.chars.len() {
                    None
                }
                else {
                    let res = self.chars[self.cursor];
                    self.cursor += 1;
                    Some(res)
                }
            }
        }
    }
}

// Identifies the state of the lexer
enum Mode {
    // tells position where these modes were started
    String(usize),
    Ident(usize),
    Number(usize),
    Comment(usize),
    Main,
}

impl<'a> Iterator for Lexer<'a> {
    type Item = Token<'a>;

    /// Lex the underlying byte stream to generate tokens
    fn next(&mut self) -> Option<Token<'a>> {

            let mut state = Mode::Main;

            while let Some(c) = self.next_byte() {

                match state {
                    Mode::String(first) => {
                        match c {
                            b'"' => {
                                return Some(Token::String(
                                    &self.chars[first+1..self.cursor-1]));
                            },
                            _ => {
                                continue;
                            }
                        }
                    },
                    Mode::Ident(first) => {
                        match c {
                            b'a' ... b'z' | b'_' => {
                                continue;
                            }
                            _ => {
                                return Some(Token::Ident(
                                    &self.chars[first..self.cursor]));
                            }
                        }
                    },
                    Mode::Number(first) => {
                        match c {
                            b'0' ... b'9' => {
                                continue;
                            },
                            _ => {
                                self.put_back(c);
                                let s = String::from_utf8_lossy(
                                    &self.chars[first..self.cursor]).into_owned();
                                return Some(Token::Number(s.parse().unwrap()));
                            }
                        }
                    }
                    Mode::Comment(first) => {
                        match c {
                            b'\n' => {
                                self.put_back(c);
                                return Some(Token::Comment(
                                    &self.chars[first..self.cursor]));
                            }
                            _ => {
                                continue;
                            }
                        }
                    },
                    Mode::Main => {
                        match c {
                            b'{' => {
                                return Some(Token::CurlyOpen);
                            },
                            b'}' => {
                                return Some(Token::CurlyClose);
                            },
                            b'"' => {
                                state = Mode::String(self.cursor - 1);
                            },
                            b'a' ... b'z' | b'_' => {
                                state = Mode::Ident(self.cursor - 1);
                            },
                            b'0' ... b'9' => {
                                state = Mode::Number(self.cursor - 1);
                            },
                            b'#' => {
                                state = Mode::Comment(self.cursor - 1);
                            },
                            b'[' => {
                                return Some(Token::BracketOpen);
                            },
                            b']' => {
                                return Some(Token::BracketClose);
                            },
                            b'=' => {
                                return Some(Token::Equals);
                            },
                            b',' => {
                                return Some(Token::Comma);
                            },
                            b' ' | b'\n' | b'\t' | b'\0' => {
                                // ignore whitespace
                            }
                            _ => {
                                return Some(Token::Invalid(c));
                            },
                        }
                    }
                }
            }

            None
        }
    }
<commit_msg>move main<commit_after>// Copyright © 2015 Sebastian Thiel
// Copyright © 2015 Andy Grover
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// “Software”), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

// Base a lexer for LVM2's text format on the more complex (hah) json format.
// This code is based on https://github.com/Byron/json-tools.

pub struct Lexer<'a> {
    chars: &'a[u8],
    next_byte: Option<u8>,
    cursor: usize,
}

#[derive(Debug, PartialEq, Clone)]
pub enum Token<'a> {
    /// `{`
    CurlyOpen,
    /// `}`
    CurlyClose,

    /// `[`
    BracketOpen,
    /// `]`
    BracketClose,

    /// `=`
    Equals,
    /// `,`
    Comma,

    /// A string , like `"foo"`
    String(&'a[u8]),

    Ident(&'a[u8]),

    /// An unsigned integer number
    Number(u64),

    Comment(&'a[u8]),

    /// The type of the token could not be identified.
    /// Should be removed if this lexer is ever to be feature complete
    Invalid(u8),
}

impl<'a> AsRef<str> for Token<'a> {
    fn as_ref(&self) -> &str {
        match *self {
            Token::CurlyOpen => "{",
            Token::CurlyClose => "}",
            Token::BracketOpen => "[",
            Token::BracketClose => "]",
            Token::Equals => "=",
            Token::Comma => ",",

            Token::Invalid(c) => panic!("Cannot convert invalid Token {}", c),
            _ => panic!("Cannot convert variant Tokens"),
        }
    }
}

impl<'a> Lexer<'a> {
    /// Returns a new Lexer from a given byte iterator.
    pub fn new(chars: &'a[u8]) -> Lexer<'a> {
        Lexer {
            chars: chars,
            next_byte: None,
            cursor: 0,
        }
    }

    fn put_back(&mut self, c: u8) {
        debug_assert!(self.next_byte.is_none());
        self.next_byte = Some(c);
        self.cursor -= 1;
    }

    fn next_byte(&mut self) -> Option<u8> {
        match self.next_byte.take() {
            Some(c) => {
                self.cursor += 1;
                Some(c)
            },
            None => {
                if self.cursor >= self.chars.len() {
                    None
                }
                else {
                    let res = self.chars[self.cursor];
                    self.cursor += 1;
                    Some(res)
                }
            }
        }
    }
}

// Identifies the state of the lexer
enum Mode {
    // tells position where these modes were started
    String(usize),
    Ident(usize),
    Number(usize),
    Comment(usize),
    Main,
}

impl<'a> Iterator for Lexer<'a> {
    type Item = Token<'a>;

    /// Lex the underlying byte stream to generate tokens
    fn next(&mut self) -> Option<Token<'a>> {

            let mut state = Mode::Main;

            while let Some(c) = self.next_byte() {
                match state {
                    Mode::Main => {
                        match c {
                            b'{' => {
                                return Some(Token::CurlyOpen);
                            },
                            b'}' => {
                                return Some(Token::CurlyClose);
                            },
                            b'"' => {
                                state = Mode::String(self.cursor - 1);
                            },
                            b'a' ... b'z' | b'_' => {
                                state = Mode::Ident(self.cursor - 1);
                            },
                            b'0' ... b'9' => {
                                state = Mode::Number(self.cursor - 1);
                            },
                            b'#' => {
                                state = Mode::Comment(self.cursor - 1);
                            },
                            b'[' => {
                                return Some(Token::BracketOpen);
                            },
                            b']' => {
                                return Some(Token::BracketClose);
                            },
                            b'=' => {
                                return Some(Token::Equals);
                            },
                            b',' => {
                                return Some(Token::Comma);
                            },
                            b' ' | b'\n' | b'\t' | b'\0' => {
                                // ignore whitespace
                            }
                            _ => {
                                return Some(Token::Invalid(c));
                            },
                        }
                    }
                    Mode::String(first) => {
                        match c {
                            b'"' => {
                                return Some(Token::String(
                                    &self.chars[first+1..self.cursor-1]));
                            },
                            _ => {
                                continue;
                            }
                        }
                    },
                    Mode::Ident(first) => {
                        match c {
                            b'a' ... b'z' | b'_' => {
                                continue;
                            }
                            _ => {
                                return Some(Token::Ident(
                                    &self.chars[first..self.cursor]));
                            }
                        }
                    },
                    Mode::Number(first) => {
                        match c {
                            b'0' ... b'9' => {
                                continue;
                            },
                            _ => {
                                self.put_back(c);
                                let s = String::from_utf8_lossy(
                                    &self.chars[first..self.cursor]).into_owned();
                                return Some(Token::Number(s.parse().unwrap()));
                            }
                        }
                    }
                    Mode::Comment(first) => {
                        match c {
                            b'\n' => {
                                self.put_back(c);
                                return Some(Token::Comment(
                                    &self.chars[first..self.cursor]));
                            }
                            _ => {
                                continue;
                            }
                        }
                    },
                }
            }

            None
        }
    }
<|endoftext|>


<commit_before>use rustc::lint::*;
use rustc_front::hir::*;
use reexport::*;
use rustc_front::visit::{Visitor, walk_expr};
use rustc::middle::ty;
use std::collections::HashSet;

use utils::{snippet, span_lint, get_parent_expr, match_trait_method, match_type,
            in_external_macro, expr_block, span_help_and_lint};
use utils::{VEC_PATH, LL_PATH};

declare_lint!{ pub NEEDLESS_RANGE_LOOP, Warn,
               "for-looping over a range of indices where an iterator over items would do" }

declare_lint!{ pub EXPLICIT_ITER_LOOP, Warn,
               "for-looping over `_.iter()` or `_.iter_mut()` when `&_` or `&mut _` would do" }

declare_lint!{ pub ITER_NEXT_LOOP, Warn,
               "for-looping over `_.next()` which is probably not intended" }

declare_lint!{ pub WHILE_LET_LOOP, Warn,
               "`loop { if let { ... } else break }` can be written as a `while let` loop" }

declare_lint!{ pub UNUSED_COLLECT, Warn,
               "`collect()`ing an iterator without using the result; this is usually better \
                written as a for loop" }

#[derive(Copy, Clone)]
pub struct LoopsPass;

impl LintPass for LoopsPass {
    fn get_lints(&self) -> LintArray {
        lint_array!(NEEDLESS_RANGE_LOOP, EXPLICIT_ITER_LOOP, ITER_NEXT_LOOP,
                    WHILE_LET_LOOP, UNUSED_COLLECT)
    }

    fn check_expr(&mut self, cx: &Context, expr: &Expr) {
        if let Some((pat, arg, body)) = recover_for_loop(expr) {
            // check for looping over a range and then indexing a sequence with it
            // -> the iteratee must be a range literal
            if let ExprRange(Some(ref l), _) = arg.node {
                // Range should start with `0`
                if let ExprLit(ref lit) = l.node {
                    if let LitInt(0, _) = lit.node {

                        // the var must be a single name
                        if let PatIdent(_, ref ident, _) = pat.node {
                            let mut visitor = VarVisitor { cx: cx, var: ident.node.name,
                                                           indexed: HashSet::new(), nonindex: false };
                            walk_expr(&mut visitor, body);
                            // linting condition: we only indexed one variable
                            if visitor.indexed.len() == 1 {
                                let indexed = visitor.indexed.into_iter().next().expect(
                                    "Len was nonzero, but no contents found");
                                if visitor.nonindex {
                                    span_lint(cx, NEEDLESS_RANGE_LOOP, expr.span, &format!(
                                        "the loop variable `{}` is used to index `{}`. Consider using \
                                         `for ({}, item) in {}.iter().enumerate()` or similar iterators",
                                        ident.node.name, indexed, ident.node.name, indexed));
                                } else {
                                    span_lint(cx, NEEDLESS_RANGE_LOOP, expr.span, &format!(
                                        "the loop variable `{}` is only used to index `{}`. \
                                         Consider using `for item in &{}` or similar iterators",
                                        ident.node.name, indexed, indexed));
                                }
                            }
                        }
                    }
                }
            }

            if let ExprMethodCall(ref method, _, ref args) = arg.node {
                // just the receiver, no arguments
                if args.len() == 1 {
                    let method_name = method.node.name;
                    // check for looping over x.iter() or x.iter_mut(), could use &x or &mut x
                    if method_name == "iter" || method_name == "iter_mut" {
                        if is_ref_iterable_type(cx, &args[0]) {
                            let object = snippet(cx, args[0].span, "_");
                            span_lint(cx, EXPLICIT_ITER_LOOP, expr.span, &format!(
                                "it is more idiomatic to loop over `&{}{}` instead of `{}.{}()`",
                                if method_name == "iter_mut" { "mut " } else { "" },
                                object, object, method_name));
                        }
                    }
                    // check for looping over Iterator::next() which is not what you want
                    else if method_name == "next" {
                        if match_trait_method(cx, arg, &["core", "iter", "Iterator"]) {
                            span_lint(cx, ITER_NEXT_LOOP, expr.span,
                                      "you are iterating over `Iterator::next()` which is an Option; \
                                       this will compile but is probably not what you want");
                        }
                    }
                }
            }
        }
        // check for `loop { if let {} else break }` that could be `while let`
        // (also matches explicit "match" instead of "if let")
        if let ExprLoop(ref block, _) = expr.node {
            // extract a single expression
            if let Some(inner) = extract_single_expr(block) {
                if let ExprMatch(ref matchexpr, ref arms, ref source) = inner.node {
                    // ensure "if let" compatible match structure
                    match *source {
                        MatchSource::Normal | MatchSource::IfLetDesugar{..} => if
                            arms.len() == 2 &&
                            arms[0].pats.len() == 1 && arms[0].guard.is_none() &&
                            arms[1].pats.len() == 1 && arms[1].guard.is_none() &&
                            // finally, check for "break" in the second clause
                            is_break_expr(&arms[1].body)
                        {
                            if in_external_macro(cx, expr.span) { return; }
                            span_help_and_lint(cx, WHILE_LET_LOOP, expr.span,
                                               "this loop could be written as a `while let` loop",
                                               &format!("try\nwhile let {} = {} {}",
                                                        snippet(cx, arms[0].pats[0].span, ".."),
                                                        snippet(cx, matchexpr.span, ".."),
                                                        expr_block(cx, &arms[0].body, "..")));
                        },
                        _ => ()
                    }
                }
            }
        }
    }

    fn check_stmt(&mut self, cx: &Context, stmt: &Stmt) {
        if let StmtSemi(ref expr, _) = stmt.node {
            if let ExprMethodCall(ref method, _, ref args) = expr.node {
                if args.len() == 1 && method.node.name == "collect" {
                    if match_trait_method(cx, expr, &["core", "iter", "Iterator"]) {
                        span_lint(cx, UNUSED_COLLECT, expr.span, &format!(
                            "you are collect()ing an iterator and throwing away the result. \
                             Consider using an explicit for loop to exhaust the iterator"));
                    }
                }
            }
        }
    }
}

/// Recover the essential nodes of a desugared for loop:
/// `for pat in arg { body }` becomes `(pat, arg, body)`.
fn recover_for_loop(expr: &Expr) -> Option<(&Pat, &Expr, &Expr)> {
    if_let_chain! {
        [
            let ExprMatch(ref iterexpr, ref arms, _) = expr.node,
            let ExprCall(_, ref iterargs) = iterexpr.node,
            iterargs.len() == 1 && arms.len() == 1 && arms[0].guard.is_none(),
            let ExprLoop(ref block, _) = arms[0].body.node,
            block.stmts.is_empty(),
            let Some(ref loopexpr) = block.expr,
            let ExprMatch(_, ref innerarms, MatchSource::ForLoopDesugar) = loopexpr.node,
            innerarms.len() == 2 && innerarms[0].pats.len() == 1,
            let PatEnum(_, Some(ref somepats)) = innerarms[0].pats[0].node,
            somepats.len() == 1
        ], {
            return Some((&somepats[0],
                         &iterargs[0],
                         &innerarms[0].body));
        }
    }
    None
}

struct VarVisitor<'v, 't: 'v> {
    cx: &'v Context<'v, 't>, // context reference
    var: Name,               // var name to look for as index
    indexed: HashSet<Name>,  // indexed variables
    nonindex: bool,          // has the var been used otherwise?
}

impl<'v, 't> Visitor<'v> for VarVisitor<'v, 't> {
    fn visit_expr(&mut self, expr: &'v Expr) {
        if let ExprPath(None, ref path) = expr.node {
            if path.segments.len() == 1 && path.segments[0].identifier.name == self.var {
                // we are referencing our variable! now check if it's as an index
                if_let_chain! {
                    [
                        let Some(parexpr) = get_parent_expr(self.cx, expr),
                        let ExprIndex(ref seqexpr, _) = parexpr.node,
                        let ExprPath(None, ref seqvar) = seqexpr.node,
                        seqvar.segments.len() == 1
                    ], {
                        self.indexed.insert(seqvar.segments[0].identifier.name);
                        return;  // no need to walk further
                    }
                }
                // we are not indexing anything, record that
                self.nonindex = true;
                return;
            }
        }
        walk_expr(self, expr);
    }
}

/// Return true if the type of expr is one that provides IntoIterator impls
/// for &T and &mut T, such as Vec.
fn is_ref_iterable_type(cx: &Context, e: &Expr) -> bool {
    // no walk_ptrs_ty: calling iter() on a reference can make sense because it
    // will allow further borrows afterwards
    let ty = cx.tcx.expr_ty(e);
    is_array(ty) ||
        match_type(cx, ty, &VEC_PATH) ||
        match_type(cx, ty, &LL_PATH) ||
        match_type(cx, ty, &["std", "collections", "hash", "map", "HashMap"]) ||
        match_type(cx, ty, &["std", "collections", "hash", "set", "HashSet"]) ||
        match_type(cx, ty, &["collections", "vec_deque", "VecDeque"]) ||
        match_type(cx, ty, &["collections", "binary_heap", "BinaryHeap"]) ||
        match_type(cx, ty, &["collections", "btree", "map", "BTreeMap"]) ||
        match_type(cx, ty, &["collections", "btree", "set", "BTreeSet"])
}

fn is_array(ty: ty::Ty) -> bool {
    match ty.sty {
        ty::TyArray(..) => true,
        _ => false
    }
}

/// If block consists of a single expression (with or without semicolon), return it.
fn extract_single_expr(block: &Block) -> Option<&Expr> {
    match (&block.stmts.len(), &block.expr) {
        (&1, &None) => match block.stmts[0].node {
            StmtExpr(ref expr, _) |
            StmtSemi(ref expr, _) => Some(expr),
            _ => None,
        },
        (&0, &Some(ref expr)) => Some(expr),
        _ => None
    }
}

/// Return true if expr contains a single break expr (maybe within a block).
fn is_break_expr(expr: &Expr) -> bool {
    match expr.node {
        ExprBreak(None) => true,
        ExprBlock(ref b) => match extract_single_expr(b) {
            Some(ref subexpr) => is_break_expr(subexpr),
            None => false,
        },
        _ => false,
    }
}
<commit_msg>dogfooding a newly caught problem<commit_after>use rustc::lint::*;
use rustc_front::hir::*;
use reexport::*;
use rustc_front::visit::{Visitor, walk_expr};
use rustc::middle::ty;
use std::collections::HashSet;

use utils::{snippet, span_lint, get_parent_expr, match_trait_method, match_type,
            in_external_macro, expr_block, span_help_and_lint};
use utils::{VEC_PATH, LL_PATH};

declare_lint!{ pub NEEDLESS_RANGE_LOOP, Warn,
               "for-looping over a range of indices where an iterator over items would do" }

declare_lint!{ pub EXPLICIT_ITER_LOOP, Warn,
               "for-looping over `_.iter()` or `_.iter_mut()` when `&_` or `&mut _` would do" }

declare_lint!{ pub ITER_NEXT_LOOP, Warn,
               "for-looping over `_.next()` which is probably not intended" }

declare_lint!{ pub WHILE_LET_LOOP, Warn,
               "`loop { if let { ... } else break }` can be written as a `while let` loop" }

declare_lint!{ pub UNUSED_COLLECT, Warn,
               "`collect()`ing an iterator without using the result; this is usually better \
                written as a for loop" }

#[derive(Copy, Clone)]
pub struct LoopsPass;

impl LintPass for LoopsPass {
    fn get_lints(&self) -> LintArray {
        lint_array!(NEEDLESS_RANGE_LOOP, EXPLICIT_ITER_LOOP, ITER_NEXT_LOOP,
                    WHILE_LET_LOOP, UNUSED_COLLECT)
    }

    fn check_expr(&mut self, cx: &Context, expr: &Expr) {
        if let Some((pat, arg, body)) = recover_for_loop(expr) {
            // check for looping over a range and then indexing a sequence with it
            // -> the iteratee must be a range literal
            if let ExprRange(Some(ref l), _) = arg.node {
                // Range should start with `0`
                if let ExprLit(ref lit) = l.node {
                    if let LitInt(0, _) = lit.node {

                        // the var must be a single name
                        if let PatIdent(_, ref ident, _) = pat.node {
                            let mut visitor = VarVisitor { cx: cx, var: ident.node.name,
                                                           indexed: HashSet::new(), nonindex: false };
                            walk_expr(&mut visitor, body);
                            // linting condition: we only indexed one variable
                            if visitor.indexed.len() == 1 {
                                let indexed = visitor.indexed.into_iter().next().expect(
                                    "Len was nonzero, but no contents found");
                                if visitor.nonindex {
                                    span_lint(cx, NEEDLESS_RANGE_LOOP, expr.span, &format!(
                                        "the loop variable `{}` is used to index `{}`. Consider using \
                                         `for ({}, item) in {}.iter().enumerate()` or similar iterators",
                                        ident.node.name, indexed, ident.node.name, indexed));
                                } else {
                                    span_lint(cx, NEEDLESS_RANGE_LOOP, expr.span, &format!(
                                        "the loop variable `{}` is only used to index `{}`. \
                                         Consider using `for item in &{}` or similar iterators",
                                        ident.node.name, indexed, indexed));
                                }
                            }
                        }
                    }
                }
            }

            if let ExprMethodCall(ref method, _, ref args) = arg.node {
                // just the receiver, no arguments
                if args.len() == 1 {
                    let method_name = method.node.name;
                    // check for looping over x.iter() or x.iter_mut(), could use &x or &mut x
                    if method_name == "iter" || method_name == "iter_mut" {
                        if is_ref_iterable_type(cx, &args[0]) {
                            let object = snippet(cx, args[0].span, "_");
                            span_lint(cx, EXPLICIT_ITER_LOOP, expr.span, &format!(
                                "it is more idiomatic to loop over `&{}{}` instead of `{}.{}()`",
                                if method_name == "iter_mut" { "mut " } else { "" },
                                object, object, method_name));
                        }
                    }
                    // check for looping over Iterator::next() which is not what you want
                    else if method_name == "next" &&
                            match_trait_method(cx, arg, &["core", "iter", "Iterator"]) {
                        span_lint(cx, ITER_NEXT_LOOP, expr.span,
                                  "you are iterating over `Iterator::next()` which is an Option; \
                                   this will compile but is probably not what you want");
                    }
                }
            }
        }
        // check for `loop { if let {} else break }` that could be `while let`
        // (also matches explicit "match" instead of "if let")
        if let ExprLoop(ref block, _) = expr.node {
            // extract a single expression
            if let Some(inner) = extract_single_expr(block) {
                if let ExprMatch(ref matchexpr, ref arms, ref source) = inner.node {
                    // ensure "if let" compatible match structure
                    match *source {
                        MatchSource::Normal | MatchSource::IfLetDesugar{..} => if
                            arms.len() == 2 &&
                            arms[0].pats.len() == 1 && arms[0].guard.is_none() &&
                            arms[1].pats.len() == 1 && arms[1].guard.is_none() &&
                            // finally, check for "break" in the second clause
                            is_break_expr(&arms[1].body)
                        {
                            if in_external_macro(cx, expr.span) { return; }
                            span_help_and_lint(cx, WHILE_LET_LOOP, expr.span,
                                               "this loop could be written as a `while let` loop",
                                               &format!("try\nwhile let {} = {} {}",
                                                        snippet(cx, arms[0].pats[0].span, ".."),
                                                        snippet(cx, matchexpr.span, ".."),
                                                        expr_block(cx, &arms[0].body, "..")));
                        },
                        _ => ()
                    }
                }
            }
        }
    }

    fn check_stmt(&mut self, cx: &Context, stmt: &Stmt) {
        if let StmtSemi(ref expr, _) = stmt.node {
            if let ExprMethodCall(ref method, _, ref args) = expr.node {
                if args.len() == 1 && method.node.name == "collect" &&
                        match_trait_method(cx, expr, &["core", "iter", "Iterator"]) {
                    span_lint(cx, UNUSED_COLLECT, expr.span, &format!(
                        "you are collect()ing an iterator and throwing away the result. \
                         Consider using an explicit for loop to exhaust the iterator"));
                }
            }
        }
    }
}

/// Recover the essential nodes of a desugared for loop:
/// `for pat in arg { body }` becomes `(pat, arg, body)`.
fn recover_for_loop(expr: &Expr) -> Option<(&Pat, &Expr, &Expr)> {
    if_let_chain! {
        [
            let ExprMatch(ref iterexpr, ref arms, _) = expr.node,
            let ExprCall(_, ref iterargs) = iterexpr.node,
            iterargs.len() == 1 && arms.len() == 1 && arms[0].guard.is_none(),
            let ExprLoop(ref block, _) = arms[0].body.node,
            block.stmts.is_empty(),
            let Some(ref loopexpr) = block.expr,
            let ExprMatch(_, ref innerarms, MatchSource::ForLoopDesugar) = loopexpr.node,
            innerarms.len() == 2 && innerarms[0].pats.len() == 1,
            let PatEnum(_, Some(ref somepats)) = innerarms[0].pats[0].node,
            somepats.len() == 1
        ], {
            return Some((&somepats[0],
                         &iterargs[0],
                         &innerarms[0].body));
        }
    }
    None
}

struct VarVisitor<'v, 't: 'v> {
    cx: &'v Context<'v, 't>, // context reference
    var: Name,               // var name to look for as index
    indexed: HashSet<Name>,  // indexed variables
    nonindex: bool,          // has the var been used otherwise?
}

impl<'v, 't> Visitor<'v> for VarVisitor<'v, 't> {
    fn visit_expr(&mut self, expr: &'v Expr) {
        if let ExprPath(None, ref path) = expr.node {
            if path.segments.len() == 1 && path.segments[0].identifier.name == self.var {
                // we are referencing our variable! now check if it's as an index
                if_let_chain! {
                    [
                        let Some(parexpr) = get_parent_expr(self.cx, expr),
                        let ExprIndex(ref seqexpr, _) = parexpr.node,
                        let ExprPath(None, ref seqvar) = seqexpr.node,
                        seqvar.segments.len() == 1
                    ], {
                        self.indexed.insert(seqvar.segments[0].identifier.name);
                        return;  // no need to walk further
                    }
                }
                // we are not indexing anything, record that
                self.nonindex = true;
                return;
            }
        }
        walk_expr(self, expr);
    }
}

/// Return true if the type of expr is one that provides IntoIterator impls
/// for &T and &mut T, such as Vec.
fn is_ref_iterable_type(cx: &Context, e: &Expr) -> bool {
    // no walk_ptrs_ty: calling iter() on a reference can make sense because it
    // will allow further borrows afterwards
    let ty = cx.tcx.expr_ty(e);
    is_array(ty) ||
        match_type(cx, ty, &VEC_PATH) ||
        match_type(cx, ty, &LL_PATH) ||
        match_type(cx, ty, &["std", "collections", "hash", "map", "HashMap"]) ||
        match_type(cx, ty, &["std", "collections", "hash", "set", "HashSet"]) ||
        match_type(cx, ty, &["collections", "vec_deque", "VecDeque"]) ||
        match_type(cx, ty, &["collections", "binary_heap", "BinaryHeap"]) ||
        match_type(cx, ty, &["collections", "btree", "map", "BTreeMap"]) ||
        match_type(cx, ty, &["collections", "btree", "set", "BTreeSet"])
}

fn is_array(ty: ty::Ty) -> bool {
    match ty.sty {
        ty::TyArray(..) => true,
        _ => false
    }
}

/// If block consists of a single expression (with or without semicolon), return it.
fn extract_single_expr(block: &Block) -> Option<&Expr> {
    match (&block.stmts.len(), &block.expr) {
        (&1, &None) => match block.stmts[0].node {
            StmtExpr(ref expr, _) |
            StmtSemi(ref expr, _) => Some(expr),
            _ => None,
        },
        (&0, &Some(ref expr)) => Some(expr),
        _ => None
    }
}

/// Return true if expr contains a single break expr (maybe within a block).
fn is_break_expr(expr: &Expr) -> bool {
    match expr.node {
        ExprBreak(None) => true,
        ExprBlock(ref b) => match extract_single_expr(b) {
            Some(ref subexpr) => is_break_expr(subexpr),
            None => false,
        },
        _ => false,
    }
}
<|endoftext|>


<commit_before>// Copyright 2018 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Implementation for MacOS / iOS
extern crate std;

use crate::Error;
use std::io;
use core::num::NonZeroU32;

enum SecRandom {}

/// Essentially a null pointer (type `SecRandomRef`)
#[allow(non_upper_case_globals)]
const kSecRandomDefault: *const SecRandom = 0 as *const SecRandom;

#[link(name = "Security", kind = "framework")]
extern {
    fn SecRandomCopyBytes(
        rnd: *const SecRandom, count: libc::size_t, bytes: *mut u8,
    ) -> libc::c_int;
}

pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
    let ret = unsafe {
        SecRandomCopyBytes(
            kSecRandomDefault,
            dest.len() as libc::size_t,
            dest.as_mut_ptr(),
        )
    };
    if ret == -1 {
        error!("SecRandomCopyBytes call failed");
        Err(io::Error::last_os_error().into())
    } else {
        Ok(())
    }
}

#[inline(always)]
pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> { None }
<commit_msg>macos: Cleanup SecRandom type (#28)<commit_after>// Copyright 2018 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Implementation for MacOS / iOS
extern crate std;

use crate::Error;
use std::io;
use core::num::NonZeroU32;

// TODO: Make extern once extern_types feature is stabilized. See:
//   https://github.com/rust-lang/rust/issues/43467
#[repr(C)]
struct SecRandom([u8; 0]);

#[link(name = "Security", kind = "framework")]
extern {
    static kSecRandomDefault: *const SecRandom;

    fn SecRandomCopyBytes(rnd: *const SecRandom, count: usize, bytes: *mut u8) -> libc::c_int;
}

pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
    let ret = unsafe { SecRandomCopyBytes(kSecRandomDefault, dest.len(), dest.as_mut_ptr()) };
    if ret == -1 {
        error!("SecRandomCopyBytes call failed");
        Err(io::Error::last_os_error().into())
    } else {
        Ok(())
    }
}

#[inline(always)]
pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> { None }
<|endoftext|>


<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use chrono::Utc;
use once_cell::sync::Lazy;
use serde::Serialize;
use serde_json::Value;
use std::{
    collections::HashMap,
    env,
    fs::{File, OpenOptions},
    io,
    io::Write as IoWrite,
    marker::PhantomData,
    net::UdpSocket,
    str::FromStr,
    sync::{
        atomic::{AtomicUsize, Ordering},
        mpsc::{self, Receiver, SyncSender},
    },
    thread,
};

pub trait StructLogSink: Sync {
    fn send(&self, entry: StructuredLogEntry);
}

// This is poor's man AtomicReference from crossbeam
// It have few unsafe lines, but does not require extra dependency
static NOP: NopStructLog = NopStructLog {};
static mut STRUCT_LOGGER: &'static dyn StructLogSink = &NOP;
static STRUCT_LOGGER_STATE: AtomicUsize = AtomicUsize::new(UNINITIALIZED);
const UNINITIALIZED: usize = 0;

const INITIALIZING: usize = 1;
const INITIALIZED: usize = 2;

// severity level - lower is worse
const SEVERITY_CRITICAL: usize = 1;
const SEVERITY_WARNING: usize = 2;

#[derive(Default, Serialize)]
pub struct StructuredLogEntry {
    #[serde(skip_serializing_if = "Option::is_none")]
    log: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pattern: Option<&'static str>,
    #[serde(skip_serializing_if = "Option::is_none")]
    name: Option<&'static str>,
    #[serde(skip_serializing_if = "Option::is_none")]
    module: Option<&'static str>,
    #[serde(skip_serializing_if = "Option::is_none")]
    location: Option<&'static str>,
    #[serde(skip_serializing_if = "Option::is_none")]
    git_rev: Option<&'static str>,
    #[serde(skip_serializing_if = "Option::is_none")]
    timestamp: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    severity: Option<usize>,
    #[serde(skip_serializing_if = "HashMap::is_empty")]
    data: HashMap<&'static str, Value>,
}

#[must_use = "use StructuredLogEntry::send to send structured log"]
impl StructuredLogEntry {
    pub fn new_unnamed() -> Self {
        let mut ret = Self::default();
        ret.timestamp = Some(Utc::now().format("%F %T").to_string());
        ret
    }

    pub fn new_named(name: &'static str) -> Self {
        let mut ret = Self::default();
        ret.name = Some(name);
        ret.timestamp = Some(Utc::now().format("%F %T").to_string());
        ret
    }

    pub fn critical(mut self) -> Self {
        self.severity = Some(SEVERITY_CRITICAL);
        self
    }

    pub fn warning(mut self) -> Self {
        self.severity = Some(SEVERITY_WARNING);
        self
    }

    pub fn json_data(mut self, key: &'static str, value: Value) -> Self {
        self.data.insert(key, value);
        self
    }

    pub fn data<D: Serialize>(mut self, key: &'static str, value: D) -> Self {
        self.data.insert(
            key,
            serde_json::to_value(value).expect("Failed to serialize StructuredLogEntry key"),
        );
        self
    }

    pub fn field<D: Serialize>(self, field: &LoggingField<D>, value: D) -> Self {
        self.data(field.0, value)
    }

    #[doc(hidden)] // set from macro
    pub fn log(&mut self, log: String) -> &mut Self {
        self.log = Some(log);
        self
    }

    #[doc(hidden)] // set from macro
    pub fn pattern(&mut self, pattern: &'static str) -> &mut Self {
        self.pattern = Some(pattern);
        self
    }

    #[doc(hidden)] // set from macro
    pub fn module(&mut self, module: &'static str) -> &mut Self {
        self.module = Some(module);
        self
    }

    #[doc(hidden)] // set from macro
    pub fn location(&mut self, location: &'static str) -> &mut Self {
        self.location = Some(location);
        self
    }

    #[doc(hidden)] // set from macro
    pub fn git_rev(&mut self, git_rev: Option<&'static str>) -> &mut Self {
        self.git_rev = git_rev;
        self
    }

    #[doc(hidden)] // set from macro
    pub fn data_mutref<D: Serialize>(&mut self, key: &'static str, value: D) -> &mut Self {
        self.data.insert(
            key,
            serde_json::to_value(value).expect("Failed to serialize StructuredLogEntry key"),
        );
        self
    }

    // Use send_struct_log! macro instead of this method to populate extra meta information such as git rev and module name
    #[doc(hidden)]
    pub fn send(self) {
        struct_logger().send(self);
    }
}

/// Field is similar to .data but restricts type of the value to a specific type.
///
/// Example:
///
/// mod logging {
///    pub const MY_FIELD:LoggingField<u64> = LoggingField::new("my_field");
/// }
///
/// mod my_code {
///    fn my_fn() {
///        send_struct_log!(StructuredLogEntry::new(...).field(&logging::MY_FIELD, 0))
///    }
/// }
pub struct LoggingField<D>(&'static str, PhantomData<D>);

impl<D> LoggingField<D> {
    pub const fn new(name: &'static str) -> Self {
        Self(name, PhantomData)
    }
}

// This is exact copy of similar function in log crate
/// Sets structured logger
pub fn set_struct_logger(logger: &'static dyn StructLogSink) -> Result<(), ()> {
    unsafe {
        match STRUCT_LOGGER_STATE.compare_and_swap(UNINITIALIZED, INITIALIZING, Ordering::SeqCst) {
            UNINITIALIZED => {
                STRUCT_LOGGER = logger;
                STRUCT_LOGGER_STATE.store(INITIALIZED, Ordering::SeqCst);
                Ok(())
            }
            INITIALIZING => {
                while STRUCT_LOGGER_STATE.load(Ordering::SeqCst) == INITIALIZING {}
                Err(())
            }
            _ => Err(()),
        }
    }
}

static STRUCT_LOG_LEVEL: Lazy<log::Level> = Lazy::new(|| {
    let level = env::var("STRUCT_LOG_LEVEL").unwrap_or_else(|_| "debug".to_string());
    log::Level::from_str(&level).expect("Failed to parse log level")
});

/// Checks if structured logging is enabled for level
pub fn struct_logger_enabled(level: log::Level) -> bool {
    struct_logger_set() && level <= *STRUCT_LOG_LEVEL
}

/// Checks if structured logging is enabled
pub fn struct_logger_set() -> bool {
    STRUCT_LOGGER_STATE.load(Ordering::SeqCst) == INITIALIZED
}

/// Initializes struct logger from STRUCT_LOG_FILE env var.
/// If STRUCT_LOG_FILE is set, STRUCT_LOG_UDP_ADDR will be ignored.
/// Can only be called once
pub fn init_struct_log_from_env() -> Result<(), InitLoggerError> {
    if let Ok(file) = env::var("STRUCT_LOG_FILE") {
        init_file_struct_log(file)
    } else if let Ok(udp_address) = env::var("STRUCT_LOG_UDP_ADDR") {
        init_udp_struct_log(udp_address)
    } else {
        Ok(())
    }
}

/// Initializes struct logger sink that writes to specified file.
/// Can only be called once
pub fn init_file_struct_log(file_path: String) -> Result<(), InitLoggerError> {
    let logger = FileStructLog::start_new(file_path).map_err(InitLoggerError::IoError)?;
    let logger = Box::leak(Box::new(logger));
    set_struct_logger(logger).map_err(|_| InitLoggerError::StructLoggerAlreadySet)
}

/// Initializes struct logger sink that stream logs through UDP protocol.
/// Can only be called once
pub fn init_udp_struct_log(udp_address: String) -> Result<(), InitLoggerError> {
    let logger = UDPStructLog::start_new(udp_address).map_err(InitLoggerError::IoError)?;
    let logger = Box::leak(Box::new(logger));
    set_struct_logger(logger).map_err(|_| InitLoggerError::StructLoggerAlreadySet)
}

/// Initialize struct logger sink that prints all structured logs to stdout
/// Can only be called once
pub fn init_println_struct_log() -> Result<(), ()> {
    let logger = PrintStructLog {};
    let logger = Box::leak(Box::new(logger));
    set_struct_logger(logger)
}

#[derive(Debug)]
pub enum InitLoggerError {
    IoError(io::Error),
    StructLoggerAlreadySet,
}

// This is exact copy of similar function in log crate
fn struct_logger() -> &'static dyn StructLogSink {
    unsafe {
        if STRUCT_LOGGER_STATE.load(Ordering::SeqCst) != INITIALIZED {
            &NOP
        } else {
            STRUCT_LOGGER
        }
    }
}

struct NopStructLog {}

impl StructLogSink for NopStructLog {
    fn send(&self, _entry: StructuredLogEntry) {}
}

struct PrintStructLog {}

impl StructLogSink for PrintStructLog {
    fn send(&self, entry: StructuredLogEntry) {
        println!("{}", serde_json::to_string(&entry).unwrap());
    }
}

/// Sink that prints all structured logs to specified file
struct FileStructLog {
    sender: SyncSender<StructuredLogEntry>,
}

impl FileStructLog {
    /// Creates new FileStructLog and starts async thread to write results
    pub fn start_new(file_path: String) -> io::Result<Self> {
        let (sender, receiver) = mpsc::sync_channel(1_024);
        let file = OpenOptions::new()
            .append(true)
            .create(true)
            .open(file_path)?;
        let sink_thread = FileStructLogThread { receiver, file };
        thread::spawn(move || sink_thread.run());
        Ok(Self { sender })
    }
}

impl StructLogSink for FileStructLog {
    fn send(&self, entry: StructuredLogEntry) {
        if let Err(e) = self.sender.try_send(entry) {
            // Use log crate macro to avoid generation of structured log in this case
            // Otherwise we will have infinite loop
            log::error!("Failed to send structured log: {}", e);
        }
    }
}

struct FileStructLogThread {
    receiver: Receiver<StructuredLogEntry>,
    file: File,
}

impl FileStructLogThread {
    pub fn run(mut self) {
        for entry in self.receiver {
            let json = match serde_json::to_value(entry) {
                Err(e) => {
                    log::error!("Failed to serialize struct log entry: {}", e);
                    continue;
                }
                Ok(json) => json,
            };
            if let Err(e) = writeln!(&mut self.file, "{}", json) {
                log::error!("Failed to write struct log entry: {}", e);
            }
        }
    }
}

/// Sink that streams all structured logs to an address through UDP protocol
struct UDPStructLog {
    sender: SyncSender<StructuredLogEntry>,
}

impl UDPStructLog {
    /// Creates new UDPStructLog and starts async thread to send results
    pub fn start_new(udp_address: String) -> io::Result<Self> {
        let (sender, receiver) = mpsc::sync_channel(1_024);
        let socket = UdpSocket::bind("0.0.0.0:0").expect("couldn't bind to address");
        let sink_thread = UDPStructLogThread {
            receiver,
            socket,
            udp_address,
        };
        thread::spawn(move || sink_thread.run());
        Ok(Self { sender })
    }
}

impl StructLogSink for UDPStructLog {
    fn send(&self, entry: StructuredLogEntry) {
        if let Err(e) = self.sender.try_send(entry) {
            // Use log crate macro to avoid generation of structured log in this case
            // Otherwise we will have infinite loop
            log::error!("[Logging] Failed to send structured log: {}", e);
        }
    }
}

struct UDPStructLogThread {
    receiver: Receiver<StructuredLogEntry>,
    socket: UdpSocket,
    udp_address: String,
}

impl UDPStructLogThread {
    pub fn run(self) {
        for entry in self.receiver {
            let json = match serde_json::to_value(entry) {
                Err(e) => {
                    log::error!("[Logging] Failed to serialize struct log entry: {}", e);
                    continue;
                }
                Ok(json) => json,
            };
            match self
                .socket
                .send_to(json.to_string().as_bytes(), self.udp_address.clone())
            {
                Ok(_) => {
                    continue;
                }
                Err(e) => {
                    println!("[Logging] Error while sending data to socket: {}", e);
                    break;
                }
            }
        }
    }
}
<commit_msg>Fix UDP logging<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use chrono::Utc;
use once_cell::sync::Lazy;
use serde::Serialize;
use serde_json::Value;
use std::{
    collections::HashMap,
    env,
    fs::{File, OpenOptions},
    io,
    io::Write as IoWrite,
    marker::PhantomData,
    net::UdpSocket,
    str::FromStr,
    sync::{
        atomic::{AtomicUsize, Ordering},
        mpsc::{self, Receiver, SyncSender},
    },
    thread,
};

pub trait StructLogSink: Sync {
    fn send(&self, entry: StructuredLogEntry);
}

// This is poor's man AtomicReference from crossbeam
// It have few unsafe lines, but does not require extra dependency
static NOP: NopStructLog = NopStructLog {};
static mut STRUCT_LOGGER: &'static dyn StructLogSink = &NOP;
static STRUCT_LOGGER_STATE: AtomicUsize = AtomicUsize::new(UNINITIALIZED);
const UNINITIALIZED: usize = 0;

const INITIALIZING: usize = 1;
const INITIALIZED: usize = 2;

// severity level - lower is worse
const SEVERITY_CRITICAL: usize = 1;
const SEVERITY_WARNING: usize = 2;

#[derive(Default, Serialize)]
pub struct StructuredLogEntry {
    #[serde(skip_serializing_if = "Option::is_none")]
    log: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pattern: Option<&'static str>,
    #[serde(skip_serializing_if = "Option::is_none")]
    name: Option<&'static str>,
    #[serde(skip_serializing_if = "Option::is_none")]
    module: Option<&'static str>,
    #[serde(skip_serializing_if = "Option::is_none")]
    location: Option<&'static str>,
    #[serde(skip_serializing_if = "Option::is_none")]
    git_rev: Option<&'static str>,
    #[serde(skip_serializing_if = "Option::is_none")]
    timestamp: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    severity: Option<usize>,
    #[serde(skip_serializing_if = "HashMap::is_empty")]
    data: HashMap<&'static str, Value>,
}

#[must_use = "use StructuredLogEntry::send to send structured log"]
impl StructuredLogEntry {
    pub fn new_unnamed() -> Self {
        let mut ret = Self::default();
        ret.timestamp = Some(Utc::now().format("%F %T").to_string());
        ret
    }

    pub fn new_named(name: &'static str) -> Self {
        let mut ret = Self::default();
        ret.name = Some(name);
        ret.timestamp = Some(Utc::now().format("%F %T").to_string());
        ret
    }

    pub fn critical(mut self) -> Self {
        self.severity = Some(SEVERITY_CRITICAL);
        self
    }

    pub fn warning(mut self) -> Self {
        self.severity = Some(SEVERITY_WARNING);
        self
    }

    pub fn json_data(mut self, key: &'static str, value: Value) -> Self {
        self.data.insert(key, value);
        self
    }

    pub fn data<D: Serialize>(mut self, key: &'static str, value: D) -> Self {
        self.data.insert(
            key,
            serde_json::to_value(value).expect("Failed to serialize StructuredLogEntry key"),
        );
        self
    }

    pub fn field<D: Serialize>(self, field: &LoggingField<D>, value: D) -> Self {
        self.data(field.0, value)
    }

    #[doc(hidden)] // set from macro
    pub fn log(&mut self, log: String) -> &mut Self {
        self.log = Some(log);
        self
    }

    #[doc(hidden)] // set from macro
    pub fn pattern(&mut self, pattern: &'static str) -> &mut Self {
        self.pattern = Some(pattern);
        self
    }

    #[doc(hidden)] // set from macro
    pub fn module(&mut self, module: &'static str) -> &mut Self {
        self.module = Some(module);
        self
    }

    #[doc(hidden)] // set from macro
    pub fn location(&mut self, location: &'static str) -> &mut Self {
        self.location = Some(location);
        self
    }

    #[doc(hidden)] // set from macro
    pub fn git_rev(&mut self, git_rev: Option<&'static str>) -> &mut Self {
        self.git_rev = git_rev;
        self
    }

    #[doc(hidden)] // set from macro
    pub fn data_mutref<D: Serialize>(&mut self, key: &'static str, value: D) -> &mut Self {
        self.data.insert(
            key,
            serde_json::to_value(value).expect("Failed to serialize StructuredLogEntry key"),
        );
        self
    }

    // Use send_struct_log! macro instead of this method to populate extra meta information such as git rev and module name
    #[doc(hidden)]
    pub fn send(self) {
        struct_logger().send(self);
    }
}

/// Field is similar to .data but restricts type of the value to a specific type.
///
/// Example:
///
/// mod logging {
///    pub const MY_FIELD:LoggingField<u64> = LoggingField::new("my_field");
/// }
///
/// mod my_code {
///    fn my_fn() {
///        send_struct_log!(StructuredLogEntry::new(...).field(&logging::MY_FIELD, 0))
///    }
/// }
pub struct LoggingField<D>(&'static str, PhantomData<D>);

impl<D> LoggingField<D> {
    pub const fn new(name: &'static str) -> Self {
        Self(name, PhantomData)
    }
}

// This is exact copy of similar function in log crate
/// Sets structured logger
pub fn set_struct_logger(logger: &'static dyn StructLogSink) -> Result<(), ()> {
    unsafe {
        match STRUCT_LOGGER_STATE.compare_and_swap(UNINITIALIZED, INITIALIZING, Ordering::SeqCst) {
            UNINITIALIZED => {
                STRUCT_LOGGER = logger;
                STRUCT_LOGGER_STATE.store(INITIALIZED, Ordering::SeqCst);
                Ok(())
            }
            INITIALIZING => {
                while STRUCT_LOGGER_STATE.load(Ordering::SeqCst) == INITIALIZING {}
                Err(())
            }
            _ => Err(()),
        }
    }
}

static STRUCT_LOG_LEVEL: Lazy<log::Level> = Lazy::new(|| {
    let level = env::var("STRUCT_LOG_LEVEL").unwrap_or_else(|_| "debug".to_string());
    log::Level::from_str(&level).expect("Failed to parse log level")
});

/// Checks if structured logging is enabled for level
pub fn struct_logger_enabled(level: log::Level) -> bool {
    struct_logger_set() && level <= *STRUCT_LOG_LEVEL
}

/// Checks if structured logging is enabled
pub fn struct_logger_set() -> bool {
    STRUCT_LOGGER_STATE.load(Ordering::SeqCst) == INITIALIZED
}

/// Initializes struct logger from STRUCT_LOG_FILE env var.
/// If STRUCT_LOG_FILE is set, STRUCT_LOG_UDP_ADDR will be ignored.
/// Can only be called once
pub fn init_struct_log_from_env() -> Result<(), InitLoggerError> {
    if let Ok(file) = env::var("STRUCT_LOG_FILE") {
        init_file_struct_log(file)
    } else if let Ok(udp_address) = env::var("STRUCT_LOG_UDP_ADDR") {
        init_udp_struct_log(udp_address)
    } else {
        Ok(())
    }
}

/// Initializes struct logger sink that writes to specified file.
/// Can only be called once
pub fn init_file_struct_log(file_path: String) -> Result<(), InitLoggerError> {
    let logger = FileStructLog::start_new(file_path).map_err(InitLoggerError::IoError)?;
    let logger = Box::leak(Box::new(logger));
    set_struct_logger(logger).map_err(|_| InitLoggerError::StructLoggerAlreadySet)
}

/// Initializes struct logger sink that stream logs through UDP protocol.
/// Can only be called once
pub fn init_udp_struct_log(udp_address: String) -> Result<(), InitLoggerError> {
    let logger = UDPStructLog::start_new(udp_address).map_err(InitLoggerError::IoError)?;
    let logger = Box::leak(Box::new(logger));
    set_struct_logger(logger).map_err(|_| InitLoggerError::StructLoggerAlreadySet)
}

/// Initialize struct logger sink that prints all structured logs to stdout
/// Can only be called once
pub fn init_println_struct_log() -> Result<(), ()> {
    let logger = PrintStructLog {};
    let logger = Box::leak(Box::new(logger));
    set_struct_logger(logger)
}

#[derive(Debug)]
pub enum InitLoggerError {
    IoError(io::Error),
    StructLoggerAlreadySet,
}

// This is exact copy of similar function in log crate
fn struct_logger() -> &'static dyn StructLogSink {
    unsafe {
        if STRUCT_LOGGER_STATE.load(Ordering::SeqCst) != INITIALIZED {
            &NOP
        } else {
            STRUCT_LOGGER
        }
    }
}

struct NopStructLog {}

impl StructLogSink for NopStructLog {
    fn send(&self, _entry: StructuredLogEntry) {}
}

struct PrintStructLog {}

impl StructLogSink for PrintStructLog {
    fn send(&self, entry: StructuredLogEntry) {
        println!("{}", serde_json::to_string(&entry).unwrap());
    }
}

/// Sink that prints all structured logs to specified file
struct FileStructLog {
    sender: SyncSender<StructuredLogEntry>,
}

impl FileStructLog {
    /// Creates new FileStructLog and starts async thread to write results
    pub fn start_new(file_path: String) -> io::Result<Self> {
        let (sender, receiver) = mpsc::sync_channel(1_024);
        let file = OpenOptions::new()
            .append(true)
            .create(true)
            .open(file_path)?;
        let sink_thread = FileStructLogThread { receiver, file };
        thread::spawn(move || sink_thread.run());
        Ok(Self { sender })
    }
}

impl StructLogSink for FileStructLog {
    fn send(&self, entry: StructuredLogEntry) {
        if let Err(e) = self.sender.try_send(entry) {
            // Use log crate macro to avoid generation of structured log in this case
            // Otherwise we will have infinite loop
            log::error!("Failed to send structured log: {}", e);
        }
    }
}

struct FileStructLogThread {
    receiver: Receiver<StructuredLogEntry>,
    file: File,
}

impl FileStructLogThread {
    pub fn run(mut self) {
        for entry in self.receiver {
            let json = match serde_json::to_value(entry) {
                Err(e) => {
                    log::error!("Failed to serialize struct log entry: {}", e);
                    continue;
                }
                Ok(json) => json,
            };
            if let Err(e) = writeln!(&mut self.file, "{}", json) {
                log::error!("Failed to write struct log entry: {}", e);
            }
        }
    }
}

/// Sink that streams all structured logs to an address through UDP protocol
struct UDPStructLog {
    sender: SyncSender<StructuredLogEntry>,
}

impl UDPStructLog {
    /// Creates new UDPStructLog and starts async thread to send results
    pub fn start_new(udp_address: String) -> io::Result<Self> {
        let (sender, receiver) = mpsc::sync_channel(1_024);
        let socket = UdpSocket::bind("0.0.0.0:0").expect("couldn't bind to address");
        let sink_thread = UDPStructLogThread {
            receiver,
            socket,
            udp_address,
        };
        thread::spawn(move || sink_thread.run());
        Ok(Self { sender })
    }
}

impl StructLogSink for UDPStructLog {
    fn send(&self, entry: StructuredLogEntry) {
        if let Err(e) = self.sender.try_send(entry) {
            // Use log crate macro to avoid generation of structured log in this case
            // Otherwise we will have infinite loop
            log::error!("[Logging] Failed to send structured log: {}", e);
        }
    }
}

struct UDPStructLogThread {
    receiver: Receiver<StructuredLogEntry>,
    socket: UdpSocket,
    udp_address: String,
}

impl UDPStructLogThread {
    pub fn run(self) {
        for entry in self.receiver {
            let json = match serde_json::to_value(entry) {
                Err(e) => {
                    log::error!("[Logging] Failed to serialize struct log entry: {}", e);
                    continue;
                }
                Ok(json) => json,
            };
            match self
                .socket
                .send_to(json.to_string().as_bytes(), self.udp_address.clone())
            {
                Ok(_) => {
                    continue;
                }
                Err(e) => {
                    // do not break on error, move on to the next log message
                    println!("[Logging] Error while sending data to socket: {}", e);
                }
            }
        }
    }
}
<|endoftext|>