<commit_before>#![crate_id = "ansi_term#0.1dev"]
#![crate_type = "rlib"]
#![crate_type = "dylib"]
#![desc = "A rust library for ANSI terminal colours and styles (bold, underline)"]

#![feature(phase)]
extern crate regex;
#[phase(plugin)] extern crate regex_macros;

pub enum Colour {
    Black, Red, Green, Yellow, Blue, Purple, Cyan, White, Fixed(u8),
}

// These are the standard numeric sequences.
// See http://invisible-island.net/xterm/ctlseqs/ctlseqs.html

impl Colour {
    fn foreground_code(&self) -> String {
        match *self {
            Black => "30".to_string(),
            Red => "31".to_string(),
            Green => "32".to_string(),
            Yellow => "33".to_string(),
            Blue => "34".to_string(),
            Purple => "35".to_string(),
            Cyan => "36".to_string(),
            White => "37".to_string(),
            Fixed(num) => format!("38;5;{}", num),
        }
    }

    fn background_code(&self) -> String {
        match *self {
            Black => "40".to_string(),
            Red => "41".to_string(),
            Green => "42".to_string(),
            Yellow => "43".to_string(),
            Blue => "44".to_string(),
            Purple => "45".to_string(),
            Cyan => "46".to_string(),
            White => "47".to_string(),
            Fixed(num) => format!("48;5;{}", num),
        }
    }
}

// There are only three different styles: plain (no formatting), only
// a foreground colour, and a catch-all for anything more complicated
// than that. It's technically possible to write other cases such as
// "bold foreground", but probably isn't worth writing all the code.

pub enum Style {
    Plain,
    Foreground(Colour),
    Style(StyleStruct),
}

// Having a struct inside an enum is currently unfinished in Rust, but
// should be put in there when that feature is complete.

pub struct StyleStruct {
    foreground: Colour,
    background: Option<Colour>,
    bold: bool,
    underline: bool,
}

impl Style {
    pub fn paint(&self, input: &str) -> String {
        match *self {
            Plain => input.to_string(),
            Foreground(c) => c.paint(input),
            Style(s) => match s {
                StyleStruct { foreground, background, bold, underline } => {
                    let bg = match background {
                        Some(c) => format!("{};", c.background_code()),
                        None => "".to_string()
                    };
                    let bo = if bold { "1;" } else { "" };
                    let un = if underline { "4;" } else { "" };
                    let painted = format!("\x1B[{}{}{}{}m{}\x1B[0m", bo, un, bg, foreground.foreground_code(), input.to_string());
                    return painted.to_string();
                }
            }
        }
    }
}

impl Style {
    pub fn bold(&self) -> Style {
        match *self {
            Plain => Style(StyleStruct         { foreground: White,         background: None,          bold: true, underline: false }),
            Foreground(c) => Style(StyleStruct { foreground: c,             background: None,          bold: true, underline: false }),
            Style(st) => Style(StyleStruct     { foreground: st.foreground, background: st.background, bold: true, underline: st.underline }),
        }
    }

    pub fn underline(&self) -> Style {
        match *self {
            Plain => Style(StyleStruct         { foreground: White,         background: None,          bold: false,   underline: true }),
            Foreground(c) => Style(StyleStruct { foreground: c,             background: None,          bold: false,   underline: true }),
            Style(st) => Style(StyleStruct     { foreground: st.foreground, background: st.background, bold: st.bold, underline: true }),
        }
    }

    pub fn on(&self, background: Colour) -> Style {
        match *self {
            Plain => Style(StyleStruct         { foreground: White,         background: Some(background), bold: false,   underline: false }),
            Foreground(c) => Style(StyleStruct { foreground: c,             background: Some(background), bold: false,   underline: false }),
            Style(st) => Style(StyleStruct     { foreground: st.foreground, background: Some(background), bold: st.bold, underline: st.underline }),
        }
    }
}

impl Colour {
    // This is a short-cut so you don't have to use Blue.normal() just
    // to turn Blue into a Style. Annoyingly, this means that Blue and
    // Blue.normal() aren't of the same type, but this hasn't been an
    // issue so far.

    pub fn paint(&self, input: &str) -> String {
        let re = format!("\x1B[{}m{}\x1B[0m", self.foreground_code(), input);
        return re.to_string();
    }

    pub fn underline(&self) -> Style {
        Style(StyleStruct { foreground: *self, background: None, bold: false, underline: true })
    }

    pub fn bold(&self) -> Style {
        Style(StyleStruct { foreground: *self, background: None, bold: true, underline: false })
    }

    pub fn normal(&self) -> Style {
        Style(StyleStruct { foreground: *self, background: None, bold: false, underline: false })
    }

    pub fn on(&self, background: Colour) -> Style {
        Style(StyleStruct { foreground: *self, background: Some(background), bold: false, underline: false })
    }
}
#[cfg(test)]
mod tests {
    use super::{Black, Red, Green, Yellow, Blue, Purple, Cyan, White, Fixed};

    #[test]
    fn test_red() {
        let hi = Red.paint("hi");
        assert!(hi == "\x1B[31mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_black() {
        let hi = Black.normal().paint("hi");
        assert!(hi == "\x1B[30mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_yellow_bold() {
        let hi = Yellow.bold().paint("hi");
        assert!(hi == "\x1B[1;33mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_yellow_bold_2() {
        let hi = Yellow.normal().bold().paint("hi");
        assert!(hi == "\x1B[1;33mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_blue_underline() {
        let hi = Blue.underline().paint("hi");
        assert!(hi == "\x1B[4;34mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_green_bold_underline() {
        let hi = Green.bold().underline().paint("hi");
        assert!(hi == "\x1B[1;4;32mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_green_bold_underline_2() {
        let hi = Green.underline().bold().paint("hi");
        assert!(hi == "\x1B[1;4;32mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_purple_on_white() {
        let hi = Purple.on(White).paint("hi");
        assert!(hi == "\x1B[47;35mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_purple_on_white_2() {
        let hi = Purple.normal().on(White).paint("hi");
        assert!(hi == "\x1B[47;35mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_cyan_bold_on_white() {
        let hi = Cyan.bold().on(White).paint("hi");
        assert!(hi == "\x1B[1;47;36mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_cyan_underline_on_white() {
        let hi = Cyan.underline().on(White).paint("hi");
        assert!(hi == "\x1B[4;47;36mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_cyan_bold_underline_on_white() {
        let hi = Cyan.bold().underline().on(White).paint("hi");
        assert!(hi == "\x1B[1;4;47;36mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_cyan_underline_bold_on_white() {
        let hi = Cyan.underline().bold().on(White).paint("hi");
        assert!(hi == "\x1B[1;4;47;36mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_fixed() {
        let hi = Fixed(100).paint("hi");
        assert!(hi == "\x1B[38;5;100mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_fixed_on_purple() {
        let hi = Fixed(100).on(Purple).paint("hi");
        assert!(hi == "\x1B[45;38;5;100mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_fixed_on_fixed() {
        let hi = Fixed(100).on(Fixed(200)).paint("hi");
        assert!(hi == "\x1B[48;5;200;38;5;100mhi\x1B[0m".to_string());
    }
}

pub fn strip_formatting(input: &String) -> String {
    let re = regex!("\x1B\\[.+?m");
    re.replace_all(input.as_slice(), "").to_string()
}
<commit_msg>Add strip_formatting tests<commit_after>#![crate_id = "ansi_term#0.1dev"]
#![crate_type = "rlib"]
#![crate_type = "dylib"]
#![desc = "A rust library for ANSI terminal colours and styles (bold, underline)"]

#![feature(phase)] extern crate regex;
#[phase(plugin)] extern crate regex_macros;

pub enum Colour {
    Black, Red, Green, Yellow, Blue, Purple, Cyan, White, Fixed(u8),
}

// These are the standard numeric sequences.
// See http://invisible-island.net/xterm/ctlseqs/ctlseqs.html

impl Colour {
    fn foreground_code(&self) -> String {
        match *self {
            Black => "30".to_string(),
            Red => "31".to_string(),
            Green => "32".to_string(),
            Yellow => "33".to_string(),
            Blue => "34".to_string(),
            Purple => "35".to_string(),
            Cyan => "36".to_string(),
            White => "37".to_string(),
            Fixed(num) => format!("38;5;{}", num),
        }
    }

    fn background_code(&self) -> String {
        match *self {
            Black => "40".to_string(),
            Red => "41".to_string(),
            Green => "42".to_string(),
            Yellow => "43".to_string(),
            Blue => "44".to_string(),
            Purple => "45".to_string(),
            Cyan => "46".to_string(),
            White => "47".to_string(),
            Fixed(num) => format!("48;5;{}", num),
        }
    }
}

// There are only three different styles: plain (no formatting), only
// a foreground colour, and a catch-all for anything more complicated
// than that. It's technically possible to write other cases such as
// "bold foreground", but probably isn't worth writing all the code.

pub enum Style {
    Plain,
    Foreground(Colour),
    Style(StyleStruct),
}

// Having a struct inside an enum is currently unfinished in Rust, but
// should be put in there when that feature is complete.

pub struct StyleStruct {
    foreground: Colour,
    background: Option<Colour>,
    bold: bool,
    underline: bool,
}

impl Style {
    pub fn paint(&self, input: &str) -> String {
        match *self {
            Plain => input.to_string(),
            Foreground(c) => c.paint(input),
            Style(s) => match s {
                StyleStruct { foreground, background, bold, underline } => {
                    let bg = match background {
                        Some(c) => format!("{};", c.background_code()),
                        None => "".to_string()
                    };
                    let bo = if bold { "1;" } else { "" };
                    let un = if underline { "4;" } else { "" };
                    let painted = format!("\x1B[{}{}{}{}m{}\x1B[0m", bo, un, bg, foreground.foreground_code(), input.to_string());
                    return painted.to_string();
                }
            }
        }
    }
}

impl Style {
    pub fn bold(&self) -> Style {
        match *self {
            Plain => Style(StyleStruct         { foreground: White,         background: None,          bold: true, underline: false }),
            Foreground(c) => Style(StyleStruct { foreground: c,             background: None,          bold: true, underline: false }),
            Style(st) => Style(StyleStruct     { foreground: st.foreground, background: st.background, bold: true, underline: st.underline }),
        }
    }

    pub fn underline(&self) -> Style {
        match *self {
            Plain => Style(StyleStruct         { foreground: White,         background: None,          bold: false,   underline: true }),
            Foreground(c) => Style(StyleStruct { foreground: c,             background: None,          bold: false,   underline: true }),
            Style(st) => Style(StyleStruct     { foreground: st.foreground, background: st.background, bold: st.bold, underline: true }),
        }
    }

    pub fn on(&self, background: Colour) -> Style {
        match *self {
            Plain => Style(StyleStruct         { foreground: White,         background: Some(background), bold: false,   underline: false }),
            Foreground(c) => Style(StyleStruct { foreground: c,             background: Some(background), bold: false,   underline: false }),
            Style(st) => Style(StyleStruct     { foreground: st.foreground, background: Some(background), bold: st.bold, underline: st.underline }),
        }
    }
}

impl Colour {
    // This is a short-cut so you don't have to use Blue.normal() just
    // to turn Blue into a Style. Annoyingly, this means that Blue and
    // Blue.normal() aren't of the same type, but this hasn't been an
    // issue so far.

    pub fn paint(&self, input: &str) -> String {
        let re = format!("\x1B[{}m{}\x1B[0m", self.foreground_code(), input);
        return re.to_string();
    }

    pub fn underline(&self) -> Style {
        Style(StyleStruct { foreground: *self, background: None, bold: false, underline: true })
    }

    pub fn bold(&self) -> Style {
        Style(StyleStruct { foreground: *self, background: None, bold: true, underline: false })
    }

    pub fn normal(&self) -> Style {
        Style(StyleStruct { foreground: *self, background: None, bold: false, underline: false })
    }

    pub fn on(&self, background: Colour) -> Style {
        Style(StyleStruct { foreground: *self, background: Some(background), bold: false, underline: false })
    }
}

pub fn strip_formatting(input: String) -> String {
    let re = regex!("\x1B\\[.+?m");
    re.replace_all(input.as_slice(), "").to_string()
}

#[cfg(test)]
mod tests {
    use super::{Black, Red, Green, Yellow, Blue, Purple, Cyan, White, Fixed, Plain, strip_formatting};

    #[test]
    fn test_plain() {
        let hi = Plain.paint("text/plain");
        assert!(hi == "text/plain".to_string());
    }

    #[test]
    fn test_red() {
        let hi = Red.paint("hi");
        assert!(hi == "\x1B[31mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_black() {
        let hi = Black.normal().paint("hi");
        assert!(hi == "\x1B[30mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_yellow_bold() {
        let hi = Yellow.bold().paint("hi");
        assert!(hi == "\x1B[1;33mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_yellow_bold_2() {
        let hi = Yellow.normal().bold().paint("hi");
        assert!(hi == "\x1B[1;33mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_blue_underline() {
        let hi = Blue.underline().paint("hi");
        assert!(hi == "\x1B[4;34mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_green_bold_underline() {
        let hi = Green.bold().underline().paint("hi");
        assert!(hi == "\x1B[1;4;32mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_green_bold_underline_2() {
        let hi = Green.underline().bold().paint("hi");
        assert!(hi == "\x1B[1;4;32mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_purple_on_white() {
        let hi = Purple.on(White).paint("hi");
        assert!(hi == "\x1B[47;35mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_purple_on_white_2() {
        let hi = Purple.normal().on(White).paint("hi");
        assert!(hi == "\x1B[47;35mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_cyan_bold_on_white() {
        let hi = Cyan.bold().on(White).paint("hi");
        assert!(hi == "\x1B[1;47;36mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_cyan_underline_on_white() {
        let hi = Cyan.underline().on(White).paint("hi");
        assert!(hi == "\x1B[4;47;36mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_cyan_bold_underline_on_white() {
        let hi = Cyan.bold().underline().on(White).paint("hi");
        assert!(hi == "\x1B[1;4;47;36mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_cyan_underline_bold_on_white() {
        let hi = Cyan.underline().bold().on(White).paint("hi");
        assert!(hi == "\x1B[1;4;47;36mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_fixed() {
        let hi = Fixed(100).paint("hi");
        assert!(hi == "\x1B[38;5;100mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_fixed_on_purple() {
        let hi = Fixed(100).on(Purple).paint("hi");
        assert!(hi == "\x1B[45;38;5;100mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_fixed_on_fixed() {
        let hi = Fixed(100).on(Fixed(200)).paint("hi");
        assert!(hi == "\x1B[48;5;200;38;5;100mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_strip_formatting() {
        let hi = strip_formatting(Blue.paint("hi"));
        assert!(hi == "hi".to_string());
    }

    #[test]
    fn test_strip_formatting_2() {
        let hi = strip_formatting(Blue.on(Fixed(230)).bold().paint("hi"));
        assert!(hi == "hi".to_string());
    }
}
<|endoftext|>


<commit_before>use std::sync::Arc;

use hyper::server::{Server, Request, Response};
use hyper::status::StatusCode;
use hyper::method::Method;
use hyper::uri::RequestUri;
use serde_json;
use serde::Serialize;

use super::config::Config;
use super::memdbstash::MemDbStash;
use super::{Result, ResultExt};

struct ServerContext {
    config: Config,
    stash: MemDbStash,
}

pub struct ApiServer {
    ctx: Arc<ServerContext>,
}

type Handler = fn(&ServerContext, Request, Response);

#[derive(Serialize)]
struct ApiError {
    #[serde(rename="type")]
    ty: String,
    message: String,
}

impl ApiServer {
    pub fn new(config: &Config) -> Result<ApiServer> {
        Ok(ApiServer {
            ctx: Arc::new(ServerContext {
                config: config.clone(),
                stash: MemDbStash::new(config)?,
            }),
        })
    }

    pub fn run(&self) -> Result<()> {
        let ctx = self.ctx.clone();
        Server::http(self.ctx.config.get_http_socket_addr()?)?
            .handle(move |mut req: Request, mut resp: Response|
        {
            let handler = match req.method {
                Method::Get => {
                    if let RequestUri::AbsolutePath(ref path) = req.uri {
                        match path.as_str() {
                            "/health" => healthcheck_handler,
                            _ => not_found_handler,
                        }
                    } else {
                        bad_request_handler
                    }
                }
                _ => {
                    method_not_allowed_handler
                }
            };
            handler(&*ctx.clone(), req, resp).unwrap();
        })?;
        Ok(())
    }
}

fn respond<T: Serialize>(mut resp: Response, obj: T, status: StatusCode) -> Result<()> {
    *resp.status_mut() = status;
    let mut body : Vec<u8> = vec![];
    serde_json::to_writer(&mut body, &obj)
        .chain_err(|| "Failed to serialize response for client")?;
    resp.send(&body[..]);
    Ok(())
}

fn healthcheck_handler(ctx: &ServerContext, _: Request, mut resp: Response)
    -> Result<()>
{
    *resp.status_mut() = StatusCode::Ok;
    Ok(())
}

fn not_found_handler(_: &ServerContext, _: Request, resp: Response)
    -> Result<()>
{
    respond(resp, ApiError {
        ty: "not_found".into(),
        message: "The requested resource was not found".into()
    }, StatusCode::NotFound)
}

fn bad_request_handler(_: &ServerContext, _: Request, mut resp: Response)
    -> Result<()>
{
    *resp.status_mut() = StatusCode::BadRequest;
    Ok(())
}

fn method_not_allowed_handler(_: &ServerContext, _: Request, mut resp: Response)
    -> Result<()>
{
    *resp.status_mut() = StatusCode::MethodNotAllowed;
    Ok(())
}
<commit_msg>Added health check to api server<commit_after>use std::sync::Arc;

use hyper::server::{Server, Request, Response};
use hyper::status::StatusCode;
use hyper::method::Method;
use hyper::uri::RequestUri;
use serde_json;
use serde::Serialize;

use super::config::Config;
use super::memdbstash::MemDbStash;
use super::{Result, ResultExt};

struct ServerContext {
    pub config: Config,
    pub stash: MemDbStash,
}

pub struct ApiServer {
    ctx: Arc<ServerContext>,
}

type Handler = fn(&ServerContext, Request, Response);

#[derive(Serialize)]
struct ApiError {
    #[serde(rename="type")]
    pub ty: String,
    pub message: String,
}

#[derive(Serialize)]
struct HealthCheckResult {
    is_healthy: bool,
    sync_lag: u32,
}

impl ApiServer {
    pub fn new(config: &Config) -> Result<ApiServer> {
        Ok(ApiServer {
            ctx: Arc::new(ServerContext {
                config: config.clone(),
                stash: MemDbStash::new(config)?,
            }),
        })
    }

    pub fn run(&self) -> Result<()> {
        let ctx = self.ctx.clone();
        Server::http(self.ctx.config.get_http_socket_addr()?)?
            .handle(move |mut req: Request, mut resp: Response|
        {
            let handler = match req.method {
                Method::Get => {
                    if let RequestUri::AbsolutePath(ref path) = req.uri {
                        match path.as_str() {
                            "/health" => healthcheck_handler,
                            _ => not_found_handler,
                        }
                    } else {
                        bad_request_handler
                    }
                }
                _ => {
                    method_not_allowed_handler
                }
            };
            handler(&*ctx.clone(), req, resp).unwrap();
        })?;
        Ok(())
    }
}

fn respond<T: Serialize>(mut resp: Response, obj: T, status: StatusCode) -> Result<()> {
    *resp.status_mut() = status;
    let mut body : Vec<u8> = vec![];
    serde_json::to_writer(&mut body, &obj)
        .chain_err(|| "Failed to serialize response for client")?;
    resp.send(&body[..]);
    Ok(())
}

fn healthcheck_handler(ctx: &ServerContext, _: Request, mut resp: Response)
    -> Result<()>
{
    // TODO: cache this
    let state = ctx.stash.get_sync_status()?;
    respond(resp, HealthCheckResult {
        is_healthy: state.is_healthy(),
        sync_lag: state.lag(),
    }, if state.is_healthy() {
        StatusCode::Ok
    } else {
        StatusCode::ServiceUnavailable
    })
}

fn not_found_handler(_: &ServerContext, _: Request, resp: Response)
    -> Result<()>
{
    respond(resp, ApiError {
        ty: "not_found".into(),
        message: "The requested resource was not found".into()
    }, StatusCode::NotFound)
}

fn bad_request_handler(_: &ServerContext, _: Request, mut resp: Response)
    -> Result<()>
{
    respond(resp, ApiError {
        ty: "bad_request".into(),
        message: "The request could not be handled".into()
    }, StatusCode::BadRequest)
}

fn method_not_allowed_handler(_: &ServerContext, _: Request, mut resp: Response)
    -> Result<()>
{
    respond(resp, ApiError {
        ty: "method_not_allowed".into(),
        message: "The server cannot handle this method".into()
    }, StatusCode::MethodNotAllowed)
}
<|endoftext|>


<commit_before>//! Command-line options which can be passed to `docker-compose`.

use std::collections::BTreeMap;
use std::ffi::{OsStr, OsString};
use std::ops::{Deref, DerefMut};

use crate::args::ToArgs;

/// An empty set of options, used for `docker-compose` subcommands for
/// which don't need any.
#[derive(Debug, Clone, Copy)]
#[allow(missing_copy_implementations)]
pub struct Empty;

impl ToArgs for Empty {
    fn to_args(&self) -> Vec<OsString> {
        vec![]
    }
}

/// Options for `docker-compose pull`.
#[derive(Debug, Default, Clone)]
#[allow(missing_copy_implementations)]
#[non_exhaustive]
pub struct Pull {
    /// Hide download progress.
    pub quiet: bool,
}

impl ToArgs for Pull {
    fn to_args(&self) -> Vec<OsString> {
        // For now, this one is hard-coded.  We always pass `-d` because we
        // need to detach from each pod to launch the next.  To avoid this,
        // we'd need to use multiple parallel threads and maybe some
        // intelligent output buffering.
        vec!["--quiet".into()]
    }
}

/// Command-line flags with for `docker-compose up`.
#[derive(Debug, Default, Clone)]
#[allow(missing_copy_implementations)]
#[non_exhaustive]
pub struct Up {
    /// Should we initialize each pod after we start it up?
    pub init: bool,
}

impl Up {
    /// Create new `Up` options.
    pub fn new(init: bool) -> Up {
        Up { init }
    }
}

impl ToArgs for Up {
    fn to_args(&self) -> Vec<OsString> {
        // For now, this one is hard-coded.  We always pass `-d` because we
        // need to detach from each pod to launch the next.  To avoid this,
        // we'd need to use multiple parallel threads and maybe some
        // intelligent output buffering.
        vec!["-d".into()]
    }
}

/// Command-line flags which can be passed to `docker-compose exec` and `run`.
#[derive(Debug, Clone)]
pub struct Process {
    /// Should we execute this command in the background?
    pub detached: bool,

    /// An optional user as whom we should run the command.
    ///
    /// TODO LOW: Is this technically "user[:group]"?  If so, we need
    /// support for that type in `compose_yml` and use it here.
    pub user: Option<String>,

    /// Should we allocate a TTY when executing the command?
    /// Defaults to true for `docker-compose`.
    pub allocate_tty: bool,

    /// PRIVATE: This field is a stand-in for future options.
    /// See http://stackoverflow.com/q/39277157/12089
    #[doc(hidden)]
    pub _nonexhaustive: (),
}

impl ToArgs for Process {
    fn to_args(&self) -> Vec<OsString> {
        let mut args: Vec<OsString> = vec![];
        if self.detached {
            args.push(OsStr::new("-d").to_owned());
        }
        if let Some(ref user) = self.user {
            args.push(OsStr::new("--user").to_owned());
            args.push(user.into());
        }
        if !self.allocate_tty {
            args.push(OsStr::new("-T").to_owned());
        }
        args
    }
}

#[test]
fn process_options_to_args_returns_empty_for_default_opts() {
    assert_eq!(Process::default().to_args(), Vec::<OsString>::new());
}

#[test]
fn process_options_to_args_returns_appropriate_flags() {
    let opts = Process {
        detached: true,
        user: Some("root".to_owned()),
        allocate_tty: false,
        ..Default::default()
    };
    let raw_expected = &["-d", "--user", "root", "-T"];
    let expected: Vec<OsString> = raw_expected
        .iter()
        .map(|s| OsStr::new(s).to_owned())
        .collect();
    assert_eq!(opts.to_args(), expected);
}

impl Default for Process {
    fn default() -> Process {
        Process {
            detached: false,
            user: None,
            allocate_tty: true, // Not false!
            _nonexhaustive: (),
        }
    }
}

/// Options for `docker_compose exec`.
#[derive(Debug, Clone, Default)]
pub struct Exec {
    /// Our "superclass", faked using `Deref`.
    pub process: Process,

    /// Should we run this command with elevated privileges?
    pub privileged: bool,

    /// PRIVATE: This field is a stand-in for future options.
    /// See http://stackoverflow.com/q/39277157/12089
    #[doc(hidden)]
    pub _nonexhaustive: (),
}

impl Deref for Exec {
    type Target = Process;

    fn deref(&self) -> &Self::Target {
        &self.process
    }
}

impl DerefMut for Exec {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.process
    }
}

impl ToArgs for Exec {
    fn to_args(&self) -> Vec<OsString> {
        let mut args = self.process.to_args();
        if self.privileged {
            args.push(OsStr::new("--privileged").to_owned());
        }
        args
    }
}

#[test]
fn exec_options_to_args_returns_appropriate_flags() {
    let mut opts = Exec::default();
    opts.detached = true;
    opts.privileged = true;
    let raw_expected = &["-d", "--privileged"];
    let expected: Vec<OsString> = raw_expected
        .iter()
        .map(|s| OsStr::new(s).to_owned())
        .collect();
    assert_eq!(opts.to_args(), expected);
}

/// Options for `docker_compose run`.
#[derive(Debug, Clone, Default)]
pub struct Run {
    /// Our "superclass", faked using `Deref`.
    pub process: Process,

    /// Extra environment variables to pass in.
    pub environment: BTreeMap<String, String>,

    /// Override the container's entrypoint.  Specify `Some("".to_owned())`
    /// to reset the entrypoint to the default.
    pub entrypoint: Option<String>,

    /// Don't start linked services when running specified service,
    /// default: false
    pub no_deps: bool,

    /// PRIVATE: This field is a stand-in for future options.
    /// See http://stackoverflow.com/q/39277157/12089
    #[doc(hidden)]
    pub _nonexhaustive: (),
}

impl Deref for Run {
    type Target = Process;

    fn deref(&self) -> &Self::Target {
        &self.process
    }
}

impl DerefMut for Run {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.process
    }
}

impl ToArgs for Run {
    fn to_args(&self) -> Vec<OsString> {
        let mut args = self.process.to_args();
        for (var, val) in &self.environment {
            args.push(OsStr::new("-e").to_owned());
            args.push(format!("{}={}", var, val).into());
        }
        if let Some(ref entrypoint) = self.entrypoint {
            args.push(OsStr::new("--entrypoint").to_owned());
            args.push(entrypoint.into());
        }
        if self.no_deps {
            args.push(OsStr::new("--no-deps").to_owned());
        }
        args
    }
}

#[test]
fn run_options_to_args_returns_appropriate_flags() {
    let mut opts = Run::default();
    opts.detached = true;
    opts.environment.insert("FOO".to_owned(), "foo".to_owned());
    opts.entrypoint = Some("/helper.sh".to_owned());
    let raw_expected = &["-d", "-e", "FOO=foo", "--entrypoint", "/helper.sh"];
    let expected: Vec<OsString> = raw_expected
        .iter()
        .map(|s| OsStr::new(s).to_owned())
        .collect();
    assert_eq!(opts.to_args(), expected);
}

/// Command-line flags with for `docker-compose logs`.
#[derive(Debug, Clone, Default)]
#[allow(missing_copy_implementations)]
pub struct Logs {
    /// Whether to interactively display logs as they're output
    pub follow: bool,

    /// Number of lines from end of log output to display
    pub number: Option<String>,

    /// PRIVATE: This field is a stand-in for future options.
    /// See http://stackoverflow.com/q/39277157/12089
    #[doc(hidden)]
    pub _nonexhaustive: (),
}

impl ToArgs for Logs {
    fn to_args(&self) -> Vec<OsString> {
        let mut args: Vec<OsString> = vec![];
        if self.follow {
            args.push(OsStr::new("-f").to_owned());
        }
        if let Some(ref number) = self.number {
            args.push(OsStr::new(&format!("--tail={}", number)).to_owned());
        }
        args
    }
}

#[test]
fn logs_options_to_args_returns_appropriate_flags() {
    let mut opts = Logs::default();
    opts.follow = true;
    opts.number = Some("12".to_owned());
    let raw_expected = &["-f", "--tail=12"];
    let expected: Vec<OsString> = raw_expected
        .iter()
        .map(|s| OsStr::new(s).to_owned())
        .collect();
    assert_eq!(opts.to_args(), expected);
}

/// Command-line flags for use with `docker-compose rm`.
#[derive(Debug, Clone, Default)]
#[allow(missing_copy_implementations)]
pub struct Rm {
    /// Delete containers without confirmation.
    pub force: bool,

    /// Delete any anonymous volumes attached to containers.
    pub remove_volumes: bool,

    /// PRIVATE: This field is a stand-in for future options.
    /// See http://stackoverflow.com/q/39277157/12089
    #[doc(hidden)]
    pub _nonexhaustive: (),
}

impl ToArgs for Rm {
    fn to_args(&self) -> Vec<OsString> {
        let mut args: Vec<OsString> = vec![];
        if self.force {
            args.push(OsStr::new("-f").to_owned());
        }
        if self.remove_volumes {
            args.push(OsStr::new("-v").to_owned());
        }
        args
    }
}

#[test]
fn rm_options_to_args_returns_appropriate_flags() {
    let mut opts = Rm::default();
    opts.force = true;
    opts.remove_volumes = true;
    let raw_expected = &["-f", "-v"];
    let expected: Vec<OsString> = raw_expected
        .iter()
        .map(|s| OsStr::new(s).to_owned())
        .collect();
    assert_eq!(opts.to_args(), expected);
}
<commit_msg>Fix: Only pass `pull --quiet` when requested<commit_after>//! Command-line options which can be passed to `docker-compose`.

use std::collections::BTreeMap;
use std::ffi::{OsStr, OsString};
use std::ops::{Deref, DerefMut};

use crate::args::ToArgs;

/// An empty set of options, used for `docker-compose` subcommands for
/// which don't need any.
#[derive(Debug, Clone, Copy)]
#[allow(missing_copy_implementations)]
pub struct Empty;

impl ToArgs for Empty {
    fn to_args(&self) -> Vec<OsString> {
        vec![]
    }
}

/// Options for `docker-compose pull`.
#[derive(Debug, Default, Clone)]
#[allow(missing_copy_implementations)]
#[non_exhaustive]
pub struct Pull {
    /// Hide download progress.
    pub quiet: bool,
}

impl ToArgs for Pull {
    fn to_args(&self) -> Vec<OsString> {
        let mut args = vec![];
        if self.quiet {
            args.push(OsString::from("--quiet"));
        }
        args
    }
}

/// Command-line flags with for `docker-compose up`.
#[derive(Debug, Default, Clone)]
#[allow(missing_copy_implementations)]
#[non_exhaustive]
pub struct Up {
    /// Should we initialize each pod after we start it up?
    pub init: bool,
}

impl Up {
    /// Create new `Up` options.
    pub fn new(init: bool) -> Up {
        Up { init }
    }
}

impl ToArgs for Up {
    fn to_args(&self) -> Vec<OsString> {
        // For now, this one is hard-coded.  We always pass `-d` because we
        // need to detach from each pod to launch the next.  To avoid this,
        // we'd need to use multiple parallel threads and maybe some
        // intelligent output buffering.
        vec!["-d".into()]
    }
}

/// Command-line flags which can be passed to `docker-compose exec` and `run`.
#[derive(Debug, Clone)]
pub struct Process {
    /// Should we execute this command in the background?
    pub detached: bool,

    /// An optional user as whom we should run the command.
    ///
    /// TODO LOW: Is this technically "user[:group]"?  If so, we need
    /// support for that type in `compose_yml` and use it here.
    pub user: Option<String>,

    /// Should we allocate a TTY when executing the command?
    /// Defaults to true for `docker-compose`.
    pub allocate_tty: bool,

    /// PRIVATE: This field is a stand-in for future options.
    /// See http://stackoverflow.com/q/39277157/12089
    #[doc(hidden)]
    pub _nonexhaustive: (),
}

impl ToArgs for Process {
    fn to_args(&self) -> Vec<OsString> {
        let mut args: Vec<OsString> = vec![];
        if self.detached {
            args.push(OsStr::new("-d").to_owned());
        }
        if let Some(ref user) = self.user {
            args.push(OsStr::new("--user").to_owned());
            args.push(user.into());
        }
        if !self.allocate_tty {
            args.push(OsStr::new("-T").to_owned());
        }
        args
    }
}

#[test]
fn process_options_to_args_returns_empty_for_default_opts() {
    assert_eq!(Process::default().to_args(), Vec::<OsString>::new());
}

#[test]
fn process_options_to_args_returns_appropriate_flags() {
    let opts = Process {
        detached: true,
        user: Some("root".to_owned()),
        allocate_tty: false,
        ..Default::default()
    };
    let raw_expected = &["-d", "--user", "root", "-T"];
    let expected: Vec<OsString> = raw_expected
        .iter()
        .map(|s| OsStr::new(s).to_owned())
        .collect();
    assert_eq!(opts.to_args(), expected);
}

impl Default for Process {
    fn default() -> Process {
        Process {
            detached: false,
            user: None,
            allocate_tty: true, // Not false!
            _nonexhaustive: (),
        }
    }
}

/// Options for `docker_compose exec`.
#[derive(Debug, Clone, Default)]
pub struct Exec {
    /// Our "superclass", faked using `Deref`.
    pub process: Process,

    /// Should we run this command with elevated privileges?
    pub privileged: bool,

    /// PRIVATE: This field is a stand-in for future options.
    /// See http://stackoverflow.com/q/39277157/12089
    #[doc(hidden)]
    pub _nonexhaustive: (),
}

impl Deref for Exec {
    type Target = Process;

    fn deref(&self) -> &Self::Target {
        &self.process
    }
}

impl DerefMut for Exec {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.process
    }
}

impl ToArgs for Exec {
    fn to_args(&self) -> Vec<OsString> {
        let mut args = self.process.to_args();
        if self.privileged {
            args.push(OsStr::new("--privileged").to_owned());
        }
        args
    }
}

#[test]
fn exec_options_to_args_returns_appropriate_flags() {
    let mut opts = Exec::default();
    opts.detached = true;
    opts.privileged = true;
    let raw_expected = &["-d", "--privileged"];
    let expected: Vec<OsString> = raw_expected
        .iter()
        .map(|s| OsStr::new(s).to_owned())
        .collect();
    assert_eq!(opts.to_args(), expected);
}

/// Options for `docker_compose run`.
#[derive(Debug, Clone, Default)]
pub struct Run {
    /// Our "superclass", faked using `Deref`.
    pub process: Process,

    /// Extra environment variables to pass in.
    pub environment: BTreeMap<String, String>,

    /// Override the container's entrypoint.  Specify `Some("".to_owned())`
    /// to reset the entrypoint to the default.
    pub entrypoint: Option<String>,

    /// Don't start linked services when running specified service,
    /// default: false
    pub no_deps: bool,

    /// PRIVATE: This field is a stand-in for future options.
    /// See http://stackoverflow.com/q/39277157/12089
    #[doc(hidden)]
    pub _nonexhaustive: (),
}

impl Deref for Run {
    type Target = Process;

    fn deref(&self) -> &Self::Target {
        &self.process
    }
}

impl DerefMut for Run {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.process
    }
}

impl ToArgs for Run {
    fn to_args(&self) -> Vec<OsString> {
        let mut args = self.process.to_args();
        for (var, val) in &self.environment {
            args.push(OsStr::new("-e").to_owned());
            args.push(format!("{}={}", var, val).into());
        }
        if let Some(ref entrypoint) = self.entrypoint {
            args.push(OsStr::new("--entrypoint").to_owned());
            args.push(entrypoint.into());
        }
        if self.no_deps {
            args.push(OsStr::new("--no-deps").to_owned());
        }
        args
    }
}

#[test]
fn run_options_to_args_returns_appropriate_flags() {
    let mut opts = Run::default();
    opts.detached = true;
    opts.environment.insert("FOO".to_owned(), "foo".to_owned());
    opts.entrypoint = Some("/helper.sh".to_owned());
    let raw_expected = &["-d", "-e", "FOO=foo", "--entrypoint", "/helper.sh"];
    let expected: Vec<OsString> = raw_expected
        .iter()
        .map(|s| OsStr::new(s).to_owned())
        .collect();
    assert_eq!(opts.to_args(), expected);
}

/// Command-line flags with for `docker-compose logs`.
#[derive(Debug, Clone, Default)]
#[allow(missing_copy_implementations)]
pub struct Logs {
    /// Whether to interactively display logs as they're output
    pub follow: bool,

    /// Number of lines from end of log output to display
    pub number: Option<String>,

    /// PRIVATE: This field is a stand-in for future options.
    /// See http://stackoverflow.com/q/39277157/12089
    #[doc(hidden)]
    pub _nonexhaustive: (),
}

impl ToArgs for Logs {
    fn to_args(&self) -> Vec<OsString> {
        let mut args: Vec<OsString> = vec![];
        if self.follow {
            args.push(OsStr::new("-f").to_owned());
        }
        if let Some(ref number) = self.number {
            args.push(OsStr::new(&format!("--tail={}", number)).to_owned());
        }
        args
    }
}

#[test]
fn logs_options_to_args_returns_appropriate_flags() {
    let mut opts = Logs::default();
    opts.follow = true;
    opts.number = Some("12".to_owned());
    let raw_expected = &["-f", "--tail=12"];
    let expected: Vec<OsString> = raw_expected
        .iter()
        .map(|s| OsStr::new(s).to_owned())
        .collect();
    assert_eq!(opts.to_args(), expected);
}

/// Command-line flags for use with `docker-compose rm`.
#[derive(Debug, Clone, Default)]
#[allow(missing_copy_implementations)]
pub struct Rm {
    /// Delete containers without confirmation.
    pub force: bool,

    /// Delete any anonymous volumes attached to containers.
    pub remove_volumes: bool,

    /// PRIVATE: This field is a stand-in for future options.
    /// See http://stackoverflow.com/q/39277157/12089
    #[doc(hidden)]
    pub _nonexhaustive: (),
}

impl ToArgs for Rm {
    fn to_args(&self) -> Vec<OsString> {
        let mut args: Vec<OsString> = vec![];
        if self.force {
            args.push(OsStr::new("-f").to_owned());
        }
        if self.remove_volumes {
            args.push(OsStr::new("-v").to_owned());
        }
        args
    }
}

#[test]
fn rm_options_to_args_returns_appropriate_flags() {
    let mut opts = Rm::default();
    opts.force = true;
    opts.remove_volumes = true;
    let raw_expected = &["-f", "-v"];
    let expected: Vec<OsString> = raw_expected
        .iter()
        .map(|s| OsStr::new(s).to_owned())
        .collect();
    assert_eq!(opts.to_args(), expected);
}
<|endoftext|>


<commit_before>// Copyright (C) 2018 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: Apache-2.0

#[macro_use]
mod macros;
pub(crate) mod actor;
mod download;
mod error;
mod idle;
pub(crate) mod install;
mod park;
mod poll;
mod prepare_download;
mod probe;
mod reboot;
mod transition;

use self::{
    download::Download, error::Error, idle::Idle, install::Install, park::Park, poll::Poll,
    prepare_download::PrepareDownload, probe::Probe, reboot::Reboot,
};
use crate::{firmware::Metadata, http_api, runtime_settings::RuntimeSettings, settings::Settings};
use async_trait::async_trait;
use derive_more::{Display, From};
use slog_scope::info;
use std::sync::mpsc;

pub type Result<T> = std::result::Result<T, TransitionError>;

#[derive(Debug, Display, From)]
pub enum TransitionError {
    #[display(fmt = "Not all objects are ready for use")]
    ObjectsNotReady,

    #[display(fmt = "Failed to read from channel: {}", _0)]
    MpscRecv(mpsc::TryRecvError),
    #[display(fmt = "Client error: {}", _0)]
    Client(crate::client::Error),
    #[display(fmt = "Firmware error: {}", _0)]
    Firmware(crate::firmware::Error),
    #[display(fmt = "Installation error: {}", _0)]
    Installation(crate::object::Error),
    #[display(fmt = "Runtime settings error: {}", _0)]
    RuntimeSettings(crate::runtime_settings::Error),
    #[display(fmt = "Update package error: {}", _0)]
    UpdatePackage(crate::update_package::Error),

    #[display(fmt = "Update package error: {}", _0)]
    Io(std::io::Error),
    #[display(fmt = "Mailbox error: {}", _0)]
    ActixMailbox(actix::MailboxError),
    #[display(fmt = "Process error: {}", _0)]
    Process(easy_process::Error),
}

#[async_trait]
trait StateChangeImpl {
    async fn handle(
        self,
        shared_state: &mut actor::SharedState,
    ) -> Result<(StateMachine, actor::StepTransition)>;

    fn name(&self) -> &'static str;

    fn handle_download_abort(&self) -> actor::download_abort::Response {
        actor::download_abort::Response::InvalidState
    }

    fn handle_trigger_probe(&self) -> actor::probe::Response {
        actor::probe::Response::InvalidState(self.name().to_owned())
    }
}

trait TransitionCallback: StateChangeImpl + Into<State<Idle>> {}

trait ProgressReporter: TransitionCallback {
    fn package_uid(&self) -> String;
    fn report_enter_state_name(&self) -> &'static str;
    fn report_leave_state_name(&self) -> &'static str;
}

#[derive(Debug, PartialEq)]
struct State<S>(S)
where
    State<S>: StateChangeImpl;

#[derive(Debug, PartialEq)]
enum StateMachine {
    Park(State<Park>),
    Idle(State<Idle>),
    Poll(State<Poll>),
    Probe(State<Probe>),
    PrepareDownload(State<PrepareDownload>),
    Download(State<Download>),
    Install(State<Install>),
    Reboot(State<Reboot>),
    Error(State<Error>),
}

impl<S> State<S>
where
    State<S>: TransitionCallback + ProgressReporter,
{
    async fn handle_with_callback_and_report_progress(
        self,
        shared_state: &mut actor::SharedState,
    ) -> Result<(StateMachine, actor::StepTransition)> {
        use transition::{state_change_callback, Transition};

        let transition =
            state_change_callback(&shared_state.settings.firmware.metadata_path, self.name())?;

        match transition {
            Transition::Continue => Ok(self.handle_and_report_progress(shared_state).await?),
            Transition::Cancel => {
                Ok((StateMachine::Idle(self.into()), actor::StepTransition::Immediate))
            }
        }
    }
}

impl<S> State<S>
where
    State<S>: ProgressReporter,
{
    async fn handle_and_report_progress(
        self,
        shared_state: &mut actor::SharedState,
    ) -> Result<(StateMachine, actor::StepTransition)> {
        let server = shared_state.server_address().to_owned();
        let firmware = &shared_state.firmware.clone();
        let package_uid = &self.package_uid();
        let enter_state = self.report_enter_state_name();
        let leave_state = self.report_leave_state_name();
        let api = crate::client::Api::new(&server);

        let report = |state, previous_state, error_message, current_log| {
            api.report(state, firmware, package_uid, previous_state, error_message, current_log)
        };

        report(enter_state, None, None, None).await?;
        match self.handle(shared_state).await {
            Ok((state, trans)) => {
                report(leave_state, None, None, None).await?;
                Ok((state, trans))
            }
            Err(e) => {
                report(
                    "error",
                    Some(enter_state),
                    Some(e.to_string()),
                    Some(crate::logger::buffer().lock().unwrap().to_string()),
                )
                .await?;
                Err(e)
            }
        }
    }
}

impl StateMachine {
    fn new() -> Self {
        StateMachine::Idle(State(Idle {}))
    }

    async fn move_to_next_state(
        self,
        shared_state: &mut actor::SharedState,
    ) -> Result<(Self, actor::StepTransition)> {
        match self {
            StateMachine::Error(s) => s.handle(shared_state).await,
            StateMachine::Park(s) => s.handle(shared_state).await,
            StateMachine::Idle(s) => s.handle(shared_state).await,
            StateMachine::Poll(s) => s.handle(shared_state).await,
            StateMachine::Probe(s) => s.handle(shared_state).await,
            StateMachine::PrepareDownload(s) => s.handle(shared_state).await,
            StateMachine::Download(s) => {
                s.handle_with_callback_and_report_progress(shared_state).await
            }
            StateMachine::Install(s) => {
                s.handle_with_callback_and_report_progress(shared_state).await
            }
            StateMachine::Reboot(s) => {
                s.handle_with_callback_and_report_progress(shared_state).await
            }
        }
    }

    fn for_any_state<F, A>(&self, f: F) -> A
    where
        F: Fn(&dyn StateChangeImpl) -> A,
    {
        match self {
            StateMachine::Error(s) => f(s),
            StateMachine::Park(s) => f(s),
            StateMachine::Idle(s) => f(s),
            StateMachine::Poll(s) => f(s),
            StateMachine::Probe(s) => f(s),
            StateMachine::PrepareDownload(s) => f(s),
            StateMachine::Download(s) => f(s),
            StateMachine::Install(s) => f(s),
            StateMachine::Reboot(s) => f(s),
        }
    }
}

/// Runs the state machine up to completion handling all procing
/// states without extra manual work.
///
/// It supports following states, and transitions, as shown in the
/// below diagram:
///
/// ```text
///           .--------------.
///           |              v
/// Park <- Idle -> Poll -> Probe -> Download -> Install -> Reboot
///           ^      ^        '          '          '
///           '      '        '          '          '
///           '      `--------'          '          '
///           `---------------'          '          '
///           `--------------------------'          '
///           `-------------------------------------'
/// ```
///
/// # Example
/// ```
/// # extern crate updatehub;
/// # async fn run() -> Result<(), updatehub::Error> {
/// use updatehub;
///
/// updatehub::logger::init(0);
/// let settings = updatehub::Settings::load()?;
/// updatehub::run(settings).await?;
/// # Ok(())
/// # }
/// ```
pub async fn run(settings: Settings) -> crate::Result<()> {
    let listen_socket = settings.network.listen_socket.clone();
    let mut runtime_settings = RuntimeSettings::new().load(&settings.storage.runtime_settings)?;
    if !settings.storage.read_only {
        runtime_settings.enable_persistency();
    }
    let firmware = Metadata::from_path(&settings.firmware.metadata_path)?;

    let machine_addr =
        actor::Machine::new(StateMachine::new(), settings, runtime_settings, firmware).start();
    actix_web::HttpServer::new(move || {
        actix_web::App::new().configure(|cfg| http_api::API::configure(cfg, machine_addr.clone()))
    })
    .bind(listen_socket.clone())
    .unwrap_or_else(|_| panic!("Failed to bind listen socket, {:?}, for HTTP API", listen_socket,))
    .run()
    .await?;

    info!("actix System has stopped");
    Ok(())
}
<commit_msg>updatehub: State change now tolerate faulty reports<commit_after>// Copyright (C) 2018 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: Apache-2.0

#[macro_use]
mod macros;
pub(crate) mod actor;
mod download;
mod error;
mod idle;
pub(crate) mod install;
mod park;
mod poll;
mod prepare_download;
mod probe;
mod reboot;
mod transition;

use self::{
    download::Download, error::Error, idle::Idle, install::Install, park::Park, poll::Poll,
    prepare_download::PrepareDownload, probe::Probe, reboot::Reboot,
};
use crate::{firmware::Metadata, http_api, runtime_settings::RuntimeSettings, settings::Settings};
use async_trait::async_trait;
use derive_more::{Display, From};
use slog_scope::{info, warn};
use std::sync::mpsc;

pub type Result<T> = std::result::Result<T, TransitionError>;

#[derive(Debug, Display, From)]
pub enum TransitionError {
    #[display(fmt = "Not all objects are ready for use")]
    ObjectsNotReady,

    #[display(fmt = "Failed to read from channel: {}", _0)]
    MpscRecv(mpsc::TryRecvError),
    #[display(fmt = "Client error: {}", _0)]
    Client(crate::client::Error),
    #[display(fmt = "Firmware error: {}", _0)]
    Firmware(crate::firmware::Error),
    #[display(fmt = "Installation error: {}", _0)]
    Installation(crate::object::Error),
    #[display(fmt = "Runtime settings error: {}", _0)]
    RuntimeSettings(crate::runtime_settings::Error),
    #[display(fmt = "Update package error: {}", _0)]
    UpdatePackage(crate::update_package::Error),

    #[display(fmt = "Update package error: {}", _0)]
    Io(std::io::Error),
    #[display(fmt = "Mailbox error: {}", _0)]
    ActixMailbox(actix::MailboxError),
    #[display(fmt = "Process error: {}", _0)]
    Process(easy_process::Error),
}

#[async_trait]
trait StateChangeImpl {
    async fn handle(
        self,
        shared_state: &mut actor::SharedState,
    ) -> Result<(StateMachine, actor::StepTransition)>;

    fn name(&self) -> &'static str;

    fn handle_download_abort(&self) -> actor::download_abort::Response {
        actor::download_abort::Response::InvalidState
    }

    fn handle_trigger_probe(&self) -> actor::probe::Response {
        actor::probe::Response::InvalidState(self.name().to_owned())
    }
}

trait TransitionCallback: StateChangeImpl + Into<State<Idle>> {}

trait ProgressReporter: TransitionCallback {
    fn package_uid(&self) -> String;
    fn report_enter_state_name(&self) -> &'static str;
    fn report_leave_state_name(&self) -> &'static str;
}

#[derive(Debug, PartialEq)]
struct State<S>(S)
where
    State<S>: StateChangeImpl;

#[derive(Debug, PartialEq)]
enum StateMachine {
    Park(State<Park>),
    Idle(State<Idle>),
    Poll(State<Poll>),
    Probe(State<Probe>),
    PrepareDownload(State<PrepareDownload>),
    Download(State<Download>),
    Install(State<Install>),
    Reboot(State<Reboot>),
    Error(State<Error>),
}

impl<S> State<S>
where
    State<S>: TransitionCallback + ProgressReporter,
{
    async fn handle_with_callback_and_report_progress(
        self,
        shared_state: &mut actor::SharedState,
    ) -> Result<(StateMachine, actor::StepTransition)> {
        use transition::{state_change_callback, Transition};

        let transition =
            state_change_callback(&shared_state.settings.firmware.metadata_path, self.name())?;

        match transition {
            Transition::Continue => Ok(self.handle_and_report_progress(shared_state).await?),
            Transition::Cancel => {
                Ok((StateMachine::Idle(self.into()), actor::StepTransition::Immediate))
            }
        }
    }
}

impl<S> State<S>
where
    State<S>: ProgressReporter,
{
    async fn handle_and_report_progress(
        self,
        shared_state: &mut actor::SharedState,
    ) -> Result<(StateMachine, actor::StepTransition)> {
        let server = shared_state.server_address().to_owned();
        let firmware = &shared_state.firmware.clone();
        let package_uid = &self.package_uid();
        let enter_state = self.report_enter_state_name();
        let leave_state = self.report_leave_state_name();
        let api = crate::client::Api::new(&server);

        let report = |state, previous_state, error_message, current_log| {
            api.report(state, firmware, package_uid, previous_state, error_message, current_log)
        };

        if let Err(e) = report(enter_state, None, None, None).await {
            warn!("Report failed: {}", e);
        }
        match self.handle(shared_state).await {
            Ok((state, trans)) => {
                if let Err(e) = report(leave_state, None, None, None).await {
                    warn!("Report failed: {}", e);
                };
                Ok((state, trans))
            }
            Err(e) => {
                if let Err(e) = report(
                    "error",
                    Some(enter_state),
                    Some(e.to_string()),
                    Some(crate::logger::buffer().lock().unwrap().to_string()),
                )
                .await
                {
                    warn!("Report failed: {}", e);
                }
                Err(e)
            }
        }
    }
}

impl StateMachine {
    fn new() -> Self {
        StateMachine::Idle(State(Idle {}))
    }

    async fn move_to_next_state(
        self,
        shared_state: &mut actor::SharedState,
    ) -> Result<(Self, actor::StepTransition)> {
        match self {
            StateMachine::Error(s) => s.handle(shared_state).await,
            StateMachine::Park(s) => s.handle(shared_state).await,
            StateMachine::Idle(s) => s.handle(shared_state).await,
            StateMachine::Poll(s) => s.handle(shared_state).await,
            StateMachine::Probe(s) => s.handle(shared_state).await,
            StateMachine::PrepareDownload(s) => s.handle(shared_state).await,
            StateMachine::Download(s) => {
                s.handle_with_callback_and_report_progress(shared_state).await
            }
            StateMachine::Install(s) => {
                s.handle_with_callback_and_report_progress(shared_state).await
            }
            StateMachine::Reboot(s) => {
                s.handle_with_callback_and_report_progress(shared_state).await
            }
        }
    }

    fn for_any_state<F, A>(&self, f: F) -> A
    where
        F: Fn(&dyn StateChangeImpl) -> A,
    {
        match self {
            StateMachine::Error(s) => f(s),
            StateMachine::Park(s) => f(s),
            StateMachine::Idle(s) => f(s),
            StateMachine::Poll(s) => f(s),
            StateMachine::Probe(s) => f(s),
            StateMachine::PrepareDownload(s) => f(s),
            StateMachine::Download(s) => f(s),
            StateMachine::Install(s) => f(s),
            StateMachine::Reboot(s) => f(s),
        }
    }
}

/// Runs the state machine up to completion handling all procing
/// states without extra manual work.
///
/// It supports following states, and transitions, as shown in the
/// below diagram:
///
/// ```text
///           .--------------.
///           |              v
/// Park <- Idle -> Poll -> Probe -> Download -> Install -> Reboot
///           ^      ^        '          '          '
///           '      '        '          '          '
///           '      `--------'          '          '
///           `---------------'          '          '
///           `--------------------------'          '
///           `-------------------------------------'
/// ```
///
/// # Example
/// ```
/// # extern crate updatehub;
/// # async fn run() -> Result<(), updatehub::Error> {
/// use updatehub;
///
/// updatehub::logger::init(0);
/// let settings = updatehub::Settings::load()?;
/// updatehub::run(settings).await?;
/// # Ok(())
/// # }
/// ```
pub async fn run(settings: Settings) -> crate::Result<()> {
    let listen_socket = settings.network.listen_socket.clone();
    let mut runtime_settings = RuntimeSettings::new().load(&settings.storage.runtime_settings)?;
    if !settings.storage.read_only {
        runtime_settings.enable_persistency();
    }
    let firmware = Metadata::from_path(&settings.firmware.metadata_path)?;

    let machine_addr =
        actor::Machine::new(StateMachine::new(), settings, runtime_settings, firmware).start();
    actix_web::HttpServer::new(move || {
        actix_web::App::new().configure(|cfg| http_api::API::configure(cfg, machine_addr.clone()))
    })
    .bind(listen_socket.clone())
    .unwrap_or_else(|_| panic!("Failed to bind listen socket, {:?}, for HTTP API", listen_socket,))
    .run()
    .await?;

    info!("actix System has stopped");
    Ok(())
}
<|endoftext|>


<commit_before>//! FIXME: write short doc here

// FIXME: this modules relies on strings and AST way too much, and it should be
// rewritten (matklad 2020-05-07)
use std::{
    convert::From,
    fmt::{self, Display},
};

use hir::{Docs, Documentation, HasSource, HirDisplay};
use ra_ide_db::RootDatabase;
use ra_syntax::ast::{self, AstNode, NameOwner, VisibilityOwner};
use stdx::SepBy;

use crate::display::{generic_parameters, where_predicates};

#[derive(Debug)]
pub enum CallableKind {
    Function,
    StructConstructor,
    VariantConstructor,
    Macro,
}

/// Contains information about a function signature
#[derive(Debug)]
pub struct FunctionSignature {
    pub kind: CallableKind,
    /// Optional visibility
    pub visibility: Option<String>,
    /// Qualifiers like `async`, `unsafe`, ...
    pub qualifier: FunctionQualifier,
    /// Name of the function
    pub name: Option<String>,
    /// Documentation for the function
    pub doc: Option<Documentation>,
    /// Generic parameters
    pub generic_parameters: Vec<String>,
    /// Parameters of the function
    pub parameters: Vec<String>,
    /// Parameter names of the function
    pub parameter_names: Vec<String>,
    /// Parameter types of the function
    pub parameter_types: Vec<String>,
    /// Optional return type
    pub ret_type: Option<String>,
    /// Where predicates
    pub where_predicates: Vec<String>,
    /// Self param presence
    pub has_self_param: bool,
}

#[derive(Debug, Default)]
pub struct FunctionQualifier {
    // `async` and `const` are mutually exclusive. Do we need to enforcing it here?
    pub is_async: bool,
    pub is_const: bool,
    pub is_unsafe: bool,
    /// The string `extern ".."`
    pub extern_abi: Option<String>,
}

impl FunctionSignature {
    pub(crate) fn with_doc_opt(mut self, doc: Option<Documentation>) -> Self {
        self.doc = doc;
        self
    }

    pub(crate) fn from_hir(db: &RootDatabase, function: hir::Function) -> Self {
        let doc = function.docs(db);
        let ast_node = function.source(db).value;
        FunctionSignature::from(&ast_node).with_doc_opt(doc)
    }

    pub(crate) fn from_struct(db: &RootDatabase, st: hir::Struct) -> Option<Self> {
        let node: ast::StructDef = st.source(db).value;
        if let ast::StructKind::Record(_) = node.kind() {
            return None;
        };

        let mut params = vec![];
        let mut parameter_types = vec![];
        for field in st.fields(db).into_iter() {
            let ty = field.signature_ty(db);
            let raw_param = format!("{}", ty.display(db));

            if let Some(param_type) = raw_param.split(':').nth(1).and_then(|it| it.get(1..)) {
                parameter_types.push(param_type.to_string());
            } else {
                // useful when you have tuple struct
                parameter_types.push(raw_param.clone());
            }
            params.push(raw_param);
        }

        Some(
            FunctionSignature {
                kind: CallableKind::StructConstructor,
                visibility: node.visibility().map(|n| n.syntax().text().to_string()),
                // Do we need `const`?
                qualifier: Default::default(),
                name: node.name().map(|n| n.text().to_string()),
                ret_type: node.name().map(|n| n.text().to_string()),
                parameters: params,
                parameter_names: vec![],
                parameter_types,
                generic_parameters: generic_parameters(&node),
                where_predicates: where_predicates(&node),
                doc: None,
                has_self_param: false,
            }
            .with_doc_opt(st.docs(db)),
        )
    }

    pub(crate) fn from_enum_variant(db: &RootDatabase, variant: hir::EnumVariant) -> Option<Self> {
        let node: ast::EnumVariant = variant.source(db).value;
        match node.kind() {
            ast::StructKind::Record(_) | ast::StructKind::Unit => return None,
            _ => (),
        };

        let parent_name = variant.parent_enum(db).name(db).to_string();

        let name = format!("{}::{}", parent_name, variant.name(db));

        let mut params = vec![];
        let mut parameter_types = vec![];
        for field in variant.fields(db).into_iter() {
            let ty = field.signature_ty(db);
            let raw_param = format!("{}", ty.display(db));
            if let Some(param_type) = raw_param.split(':').nth(1).and_then(|it| it.get(1..)) {
                parameter_types.push(param_type.to_string());
            } else {
                // The unwrap_or_else is useful when you have tuple
                parameter_types.push(raw_param);
            }
            let name = field.name(db);

            params.push(format!("{}: {}", name, ty.display(db)));
        }

        Some(
            FunctionSignature {
                kind: CallableKind::VariantConstructor,
                visibility: None,
                // Do we need `const`?
                qualifier: Default::default(),
                name: Some(name),
                ret_type: None,
                parameters: params,
                parameter_names: vec![],
                parameter_types,
                generic_parameters: vec![],
                where_predicates: vec![],
                doc: None,
                has_self_param: false,
            }
            .with_doc_opt(variant.docs(db)),
        )
    }

    pub(crate) fn from_macro(db: &RootDatabase, macro_def: hir::MacroDef) -> Option<Self> {
        let node: ast::MacroCall = macro_def.source(db).value;

        let params = vec![];

        Some(
            FunctionSignature {
                kind: CallableKind::Macro,
                visibility: None,
                qualifier: Default::default(),
                name: node.name().map(|n| n.text().to_string()),
                ret_type: None,
                parameters: params,
                parameter_names: vec![],
                parameter_types: vec![],
                generic_parameters: vec![],
                where_predicates: vec![],
                doc: None,
                has_self_param: false,
            }
            .with_doc_opt(macro_def.docs(db)),
        )
    }
}

impl From<&'_ ast::FnDef> for FunctionSignature {
    fn from(node: &ast::FnDef) -> FunctionSignature {
        fn param_list(node: &ast::FnDef) -> (bool, Vec<String>, Vec<String>) {
            let mut res = vec![];
            let mut res_types = vec![];
            let mut has_self_param = false;
            if let Some(param_list) = node.param_list() {
                if let Some(self_param) = param_list.self_param() {
                    has_self_param = true;
                    let raw_param = self_param.syntax().text().to_string();

                    res_types.push(
                        raw_param
                            .split(':')
                            .nth(1)
                            .and_then(|it| it.get(1..))
                            .unwrap_or_else(|| "Self")
                            .to_string(),
                    );
                    res.push(raw_param);
                }

                res.extend(param_list.params().map(|param| param.syntax().text().to_string()));
                res_types.extend(param_list.params().map(|param| {
                    let param_text = param.syntax().text().to_string();
                    match param_text.split(':').nth(1).and_then(|it| it.get(1..)) {
                        Some(it) => it.to_string(),
                        None => param_text,
                    }
                }));
            }
            (has_self_param, res, res_types)
        }

        fn param_name_list(node: &ast::FnDef) -> Vec<String> {
            let mut res = vec![];
            if let Some(param_list) = node.param_list() {
                if let Some(self_param) = param_list.self_param() {
                    res.push(self_param.syntax().text().to_string())
                }

                res.extend(
                    param_list
                        .params()
                        .map(|param| {
                            Some(
                                param
                                    .pat()?
                                    .syntax()
                                    .descendants()
                                    .find_map(ast::Name::cast)?
                                    .text()
                                    .to_string(),
                            )
                        })
                        .map(|param| param.unwrap_or_default()),
                );
            }
            res
        }

        let (has_self_param, parameters, parameter_types) = param_list(node);

        FunctionSignature {
            kind: CallableKind::Function,
            visibility: node.visibility().map(|n| n.syntax().text().to_string()),
            qualifier: FunctionQualifier {
                is_async: node.async_token().is_some(),
                is_const: node.const_token().is_some(),
                is_unsafe: node.unsafe_token().is_some(),
                extern_abi: node.abi().map(|n| n.to_string()),
            },
            name: node.name().map(|n| n.text().to_string()),
            ret_type: node
                .ret_type()
                .and_then(|r| r.type_ref())
                .map(|n| n.syntax().text().to_string()),
            parameters,
            parameter_names: param_name_list(node),
            parameter_types,
            generic_parameters: generic_parameters(node),
            where_predicates: where_predicates(node),
            // docs are processed separately
            doc: None,
            has_self_param,
        }
    }
}

impl Display for FunctionSignature {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if let Some(t) = &self.visibility {
            write!(f, "{} ", t)?;
        }

        if self.qualifier.is_async {
            write!(f, "async ")?;
        }

        if self.qualifier.is_const {
            write!(f, "const ")?;
        }

        if self.qualifier.is_unsafe {
            write!(f, "unsafe ")?;
        }

        if let Some(extern_abi) = &self.qualifier.extern_abi {
            // Keyword `extern` is included in the string.
            write!(f, "{} ", extern_abi)?;
        }

        if let Some(name) = &self.name {
            match self.kind {
                CallableKind::Function => write!(f, "fn {}", name)?,
                CallableKind::StructConstructor => write!(f, "struct {}", name)?,
                CallableKind::VariantConstructor => write!(f, "{}", name)?,
                CallableKind::Macro => write!(f, "{}!", name)?,
            }
        }

        if !self.generic_parameters.is_empty() {
            write!(f, "{}", self.generic_parameters.iter().sep_by(", ").surround_with("<", ">"))?;
        }

        write!(f, "{}", self.parameters.iter().sep_by(", ").surround_with("(", ")"))?;

        if let Some(t) = &self.ret_type {
            write!(f, " -> {}", t)?;
        }

        if !self.where_predicates.is_empty() {
            write!(f, "\nwhere {}", self.where_predicates.iter().sep_by(",\n      "))?;
        }

        Ok(())
    }
}
<commit_msg>Strip leading underscore<commit_after>//! FIXME: write short doc here

// FIXME: this modules relies on strings and AST way too much, and it should be
// rewritten (matklad 2020-05-07)
use std::{
    convert::From,
    fmt::{self, Display},
};

use hir::{Docs, Documentation, HasSource, HirDisplay};
use ra_ide_db::RootDatabase;
use ra_syntax::ast::{self, AstNode, NameOwner, VisibilityOwner};
use stdx::SepBy;

use crate::display::{generic_parameters, where_predicates};

#[derive(Debug)]
pub enum CallableKind {
    Function,
    StructConstructor,
    VariantConstructor,
    Macro,
}

/// Contains information about a function signature
#[derive(Debug)]
pub struct FunctionSignature {
    pub kind: CallableKind,
    /// Optional visibility
    pub visibility: Option<String>,
    /// Qualifiers like `async`, `unsafe`, ...
    pub qualifier: FunctionQualifier,
    /// Name of the function
    pub name: Option<String>,
    /// Documentation for the function
    pub doc: Option<Documentation>,
    /// Generic parameters
    pub generic_parameters: Vec<String>,
    /// Parameters of the function
    pub parameters: Vec<String>,
    /// Parameter names of the function
    pub parameter_names: Vec<String>,
    /// Parameter types of the function
    pub parameter_types: Vec<String>,
    /// Optional return type
    pub ret_type: Option<String>,
    /// Where predicates
    pub where_predicates: Vec<String>,
    /// Self param presence
    pub has_self_param: bool,
}

#[derive(Debug, Default)]
pub struct FunctionQualifier {
    // `async` and `const` are mutually exclusive. Do we need to enforcing it here?
    pub is_async: bool,
    pub is_const: bool,
    pub is_unsafe: bool,
    /// The string `extern ".."`
    pub extern_abi: Option<String>,
}

impl FunctionSignature {
    pub(crate) fn with_doc_opt(mut self, doc: Option<Documentation>) -> Self {
        self.doc = doc;
        self
    }

    pub(crate) fn from_hir(db: &RootDatabase, function: hir::Function) -> Self {
        let doc = function.docs(db);
        let ast_node = function.source(db).value;
        FunctionSignature::from(&ast_node).with_doc_opt(doc)
    }

    pub(crate) fn from_struct(db: &RootDatabase, st: hir::Struct) -> Option<Self> {
        let node: ast::StructDef = st.source(db).value;
        if let ast::StructKind::Record(_) = node.kind() {
            return None;
        };

        let mut params = vec![];
        let mut parameter_types = vec![];
        for field in st.fields(db).into_iter() {
            let ty = field.signature_ty(db);
            let raw_param = format!("{}", ty.display(db));

            if let Some(param_type) = raw_param.split(':').nth(1).and_then(|it| it.get(1..)) {
                parameter_types.push(param_type.to_string());
            } else {
                // useful when you have tuple struct
                parameter_types.push(raw_param.clone());
            }
            params.push(raw_param);
        }

        Some(
            FunctionSignature {
                kind: CallableKind::StructConstructor,
                visibility: node.visibility().map(|n| n.syntax().text().to_string()),
                // Do we need `const`?
                qualifier: Default::default(),
                name: node.name().map(|n| n.text().to_string()),
                ret_type: node.name().map(|n| n.text().to_string()),
                parameters: params,
                parameter_names: vec![],
                parameter_types,
                generic_parameters: generic_parameters(&node),
                where_predicates: where_predicates(&node),
                doc: None,
                has_self_param: false,
            }
            .with_doc_opt(st.docs(db)),
        )
    }

    pub(crate) fn from_enum_variant(db: &RootDatabase, variant: hir::EnumVariant) -> Option<Self> {
        let node: ast::EnumVariant = variant.source(db).value;
        match node.kind() {
            ast::StructKind::Record(_) | ast::StructKind::Unit => return None,
            _ => (),
        };

        let parent_name = variant.parent_enum(db).name(db).to_string();

        let name = format!("{}::{}", parent_name, variant.name(db));

        let mut params = vec![];
        let mut parameter_types = vec![];
        for field in variant.fields(db).into_iter() {
            let ty = field.signature_ty(db);
            let raw_param = format!("{}", ty.display(db));
            if let Some(param_type) = raw_param.split(':').nth(1).and_then(|it| it.get(1..)) {
                parameter_types.push(param_type.to_string());
            } else {
                // The unwrap_or_else is useful when you have tuple
                parameter_types.push(raw_param);
            }
            let name = field.name(db);

            params.push(format!("{}: {}", name, ty.display(db)));
        }

        Some(
            FunctionSignature {
                kind: CallableKind::VariantConstructor,
                visibility: None,
                // Do we need `const`?
                qualifier: Default::default(),
                name: Some(name),
                ret_type: None,
                parameters: params,
                parameter_names: vec![],
                parameter_types,
                generic_parameters: vec![],
                where_predicates: vec![],
                doc: None,
                has_self_param: false,
            }
            .with_doc_opt(variant.docs(db)),
        )
    }

    pub(crate) fn from_macro(db: &RootDatabase, macro_def: hir::MacroDef) -> Option<Self> {
        let node: ast::MacroCall = macro_def.source(db).value;

        let params = vec![];

        Some(
            FunctionSignature {
                kind: CallableKind::Macro,
                visibility: None,
                qualifier: Default::default(),
                name: node.name().map(|n| n.text().to_string()),
                ret_type: None,
                parameters: params,
                parameter_names: vec![],
                parameter_types: vec![],
                generic_parameters: vec![],
                where_predicates: vec![],
                doc: None,
                has_self_param: false,
            }
            .with_doc_opt(macro_def.docs(db)),
        )
    }
}

impl From<&'_ ast::FnDef> for FunctionSignature {
    fn from(node: &ast::FnDef) -> FunctionSignature {
        fn strip_leading_underscore(name: String) -> String {
            if name.starts_with("_") {
                name.get(1..).unwrap_or_default().to_string()
            } else {
                name
            }
        }

        fn param_list(node: &ast::FnDef) -> (bool, Vec<String>, Vec<String>) {
            let mut res = vec![];
            let mut res_types = vec![];
            let mut has_self_param = false;
            if let Some(param_list) = node.param_list() {
                if let Some(self_param) = param_list.self_param() {
                    has_self_param = true;
                    let raw_param = self_param.syntax().text().to_string();

                    res_types.push(
                        raw_param
                            .split(':')
                            .nth(1)
                            .and_then(|it| it.get(1..))
                            .unwrap_or_else(|| "Self")
                            .to_string(),
                    );
                    res.push(raw_param);
                }

                res.extend(param_list.params().map(|param| param.syntax().text().to_string()));
                res_types.extend(param_list.params().map(|param| {
                    let param_text = param.syntax().text().to_string();
                    match param_text.split(':').nth(1).and_then(|it| it.get(1..)) {
                        Some(it) => it.to_string(),
                        None => param_text,
                    }
                }));
            }
            (has_self_param, res, res_types)
        }

        fn param_name_list(node: &ast::FnDef) -> Vec<String> {
            let mut res = vec![];
            if let Some(param_list) = node.param_list() {
                if let Some(self_param) = param_list.self_param() {
                    res.push(self_param.syntax().text().to_string())
                }

                res.extend(
                    param_list
                        .params()
                        .map(|param| {
                            Some(strip_leading_underscore(
                                param
                                    .pat()?
                                    .syntax()
                                    .descendants()
                                    .find_map(ast::Name::cast)?
                                    .text()
                                    .to_string(),
                            ))
                        })
                        .map(|param| param.unwrap_or_default()),
                );
            }
            res
        }

        let (has_self_param, parameters, parameter_types) = param_list(node);

        FunctionSignature {
            kind: CallableKind::Function,
            visibility: node.visibility().map(|n| n.syntax().text().to_string()),
            qualifier: FunctionQualifier {
                is_async: node.async_token().is_some(),
                is_const: node.const_token().is_some(),
                is_unsafe: node.unsafe_token().is_some(),
                extern_abi: node.abi().map(|n| n.to_string()),
            },
            name: node.name().map(|n| n.text().to_string()),
            ret_type: node
                .ret_type()
                .and_then(|r| r.type_ref())
                .map(|n| n.syntax().text().to_string()),
            parameters,
            parameter_names: param_name_list(node),
            parameter_types,
            generic_parameters: generic_parameters(node),
            where_predicates: where_predicates(node),
            // docs are processed separately
            doc: None,
            has_self_param,
        }
    }
}

impl Display for FunctionSignature {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if let Some(t) = &self.visibility {
            write!(f, "{} ", t)?;
        }

        if self.qualifier.is_async {
            write!(f, "async ")?;
        }

        if self.qualifier.is_const {
            write!(f, "const ")?;
        }

        if self.qualifier.is_unsafe {
            write!(f, "unsafe ")?;
        }

        if let Some(extern_abi) = &self.qualifier.extern_abi {
            // Keyword `extern` is included in the string.
            write!(f, "{} ", extern_abi)?;
        }

        if let Some(name) = &self.name {
            match self.kind {
                CallableKind::Function => write!(f, "fn {}", name)?,
                CallableKind::StructConstructor => write!(f, "struct {}", name)?,
                CallableKind::VariantConstructor => write!(f, "{}", name)?,
                CallableKind::Macro => write!(f, "{}!", name)?,
            }
        }

        if !self.generic_parameters.is_empty() {
            write!(f, "{}", self.generic_parameters.iter().sep_by(", ").surround_with("<", ">"))?;
        }

        write!(f, "{}", self.parameters.iter().sep_by(", ").surround_with("(", ")"))?;

        if let Some(t) = &self.ret_type {
            write!(f, " -> {}", t)?;
        }

        if !self.where_predicates.is_empty() {
            write!(f, "\nwhere {}", self.where_predicates.iter().sep_by(",\n      "))?;
        }

        Ok(())
    }
}
<|endoftext|>