<commit_before>use super::{HasResponse, HasValue};
use chrono::{DateTime, Utc};
use method::{Create};
use request::CustomImageRequest;
use request::Request;
use serde::Serialize;
use std::fmt::Display;
use {ROOT_URL, STATIC_URL_ERROR};

const IMAGES_SEGMENT: &'static str = "images";

/// A custom image is an image with an user-supplied raw image.
/// The body must contain a url attribute pointing to a Linux virtual machine image to be imported into DigitalOcean.
/// The image must be in the raw, qcow2, vhdx, vdi, or vmdk format.
/// It may be compressed using gzip or bzip2 and must be smaller than 100 GB after being decompressed.
///
/// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#create-a-custom-image)
#[derive(Deserialize, Serialize, Debug, Clone, Getters, Setters)]
pub struct CustomImage {
    /// A unique number that can be used to identify and reference a specific
    /// image.
    #[get = "pub"]
    id: usize,
    /// The display name that has been given to an image. This is what is shown
    /// in the control panel and is generally a descriptive title for the image
    /// in question.
    #[get = "pub"]
    name: String,
    /// The kind of image, describing the duration of how long the image is
    /// stored. This is either "snapshot" or "backup".
    ///
    /// *Note:* Since `type` is a keyword in Rust `kind` is used instead.
    #[serde(rename = "type")]
    #[get = "pub"]
    kind: String, // 'type' is reserved in Rust.
    /// This attribute describes the base distribution used for this image.
    #[get = "pub"]
    distribution: String,
    /// This attribute is an array of the regions that the image is available
    /// in. The regions are represented by their identifying slug values.
    #[get = "pub"]
    regions: Vec<String>,
    /// Tags to quickly find an image or to group multiple images
    /// under a common name
    #[get = "pub"]
    tags: Vec<String>,
    /// A time value given in ISO8601 combined date and time format that
    /// represents when the Image was created.
    #[get = "pub"]
    created_at: DateTime<Utc>,
    /// A brief description about the image
    #[get = "pub"]
    description: String,
    /// The status of the image
    #[get = "pub"]
    status: String,
}

impl CustomImage {
    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#create-a-custom-image)
    pub fn create<S>(
        name: S,
        image_url: S,
        region: S,
        distribution: S,
        desc: S,
        tags: Vec<S>,
    ) -> CustomImageRequest<Create, CustomImage>
    where
        S: AsRef<str> + Display + Serialize,
    {
        let mut url = ROOT_URL.clone();
        url.path_segments_mut()
            .expect(STATIC_URL_ERROR)
            .push(IMAGES_SEGMENT);

        let mut req = Request::new(url);
        req.set_body(json!({
            "name": name,
            "url": image_url,
            "region": region,
            "distribution": distribution,
            "description": desc,
            "tags": tags
        }));
        req
    }
}

/// Response type returned from Digital Ocean.
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct CustomImageResponse {
    image: CustomImage,
}

impl HasResponse for CustomImage {
    type Response = CustomImageResponse;
}

impl HasValue for CustomImageResponse {
    type Value = CustomImage;
    fn value(self) -> CustomImage {
        self.image
    }
}
<commit_msg>Fix Doc for CustomImage<commit_after>use super::{HasResponse, HasValue};
use chrono::{DateTime, Utc};
use method::{Create};
use request::CustomImageRequest;
use request::Request;
use serde::Serialize;
use std::fmt::Display;
use {ROOT_URL, STATIC_URL_ERROR};

const IMAGES_SEGMENT: &'static str = "images";

/// A custom image is an image with an user-supplied raw image.
/// The body must contain a url attribute pointing to a Linux virtual machine image to be imported into DigitalOcean.
/// The image must be in the raw, qcow2, vhdx, vdi, or vmdk format.
/// It may be compressed using gzip or bzip2 and must be smaller than 100 GB after being decompressed.
///
/// [Digital Ocean Documentation.](https://www.digitalocean.com/docs/images/custom-images/)
#[derive(Deserialize, Serialize, Debug, Clone, Getters, Setters)]
pub struct CustomImage {
    /// A unique number that can be used to identify and reference a specific
    /// image.
    #[get = "pub"]
    id: usize,
    /// The display name that has been given to an image. This is what is shown
    /// in the control panel and is generally a descriptive title for the image
    /// in question.
    #[get = "pub"]
    name: String,
    /// The kind of image, describing the duration of how long the image is
    /// stored. This is either "snapshot" or "backup".
    ///
    /// *Note:* Since `type` is a keyword in Rust `kind` is used instead.
    #[serde(rename = "type")]
    #[get = "pub"]
    kind: String, // 'type' is reserved in Rust.
    /// This attribute describes the base distribution used for this image.
    #[get = "pub"]
    distribution: String,
    /// This attribute is an array of the regions that the image is available
    /// in. The regions are represented by their identifying slug values.
    #[get = "pub"]
    regions: Vec<String>,
    /// Tags to quickly find an image or to group multiple images
    /// under a common name
    #[get = "pub"]
    tags: Vec<String>,
    /// A time value given in ISO8601 combined date and time format that
    /// represents when the Image was created.
    #[get = "pub"]
    created_at: DateTime<Utc>,
    /// A brief description about the image
    #[get = "pub"]
    description: String,
    /// The status of the image
    #[get = "pub"]
    status: String,
}

impl CustomImage {
    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#create-a-custom-image)
    pub fn create<S>(
        name: S,
        image_url: S,
        region: S,
        distribution: S,
        desc: S,
        tags: Vec<S>,
    ) -> CustomImageRequest<Create, CustomImage>
    where
        S: AsRef<str> + Display + Serialize,
    {
        let mut url = ROOT_URL.clone();
        url.path_segments_mut()
            .expect(STATIC_URL_ERROR)
            .push(IMAGES_SEGMENT);

        let mut req = Request::new(url);
        req.set_body(json!({
            "name": name,
            "url": image_url,
            "region": region,
            "distribution": distribution,
            "description": desc,
            "tags": tags
        }));
        req
    }
}

/// Response type returned from Digital Ocean.
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct CustomImageResponse {
    image: CustomImage,
}

impl HasResponse for CustomImage {
    type Response = CustomImageResponse;
}

impl HasValue for CustomImageResponse {
    type Value = CustomImage;
    fn value(self) -> CustomImage {
        self.image
    }
}
<|endoftext|>


<commit_before>use crate::syntax::attrs::OtherAttrs;
use crate::syntax::namespace::Namespace;
use crate::syntax::report::Errors;
use crate::syntax::{Api, Discriminant, Doc, Enum, EnumRepr, ForeignName, Pair, Variant};
use flate2::write::GzDecoder;
use memmap::Mmap;
use proc_macro2::{Delimiter, Group, Ident, TokenStream};
use quote::{format_ident, quote, quote_spanned};
use serde::Deserialize;
use std::env;
use std::fmt::{self, Display};
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
use std::str::FromStr;
use syn::{parse_quote, Path};

const CXX_CLANG_AST: &str = "CXX_CLANG_AST";

type Node = clang_ast::Node<Clang>;

#[derive(Deserialize)]
enum Clang {
    NamespaceDecl(NamespaceDecl),
    EnumDecl(EnumDecl),
    EnumConstantDecl(EnumConstantDecl),
    ImplicitCastExpr,
    ConstantExpr(ConstantExpr),
    Unknown,
}

#[derive(Deserialize)]
struct NamespaceDecl {
    name: Option<String>,
}

#[derive(Deserialize)]
struct EnumDecl {
    name: Option<String>,
    #[serde(rename = "fixedUnderlyingType")]
    fixed_underlying_type: Option<Type>,
}

#[derive(Deserialize)]
struct EnumConstantDecl {
    name: String,
}

#[derive(Deserialize)]
struct ConstantExpr {
    value: String,
}

#[derive(Deserialize)]
struct Type {
    #[serde(rename = "qualType")]
    qual_type: String,
    #[serde(rename = "desugaredQualType")]
    desugared_qual_type: Option<String>,
}

pub fn load(cx: &mut Errors, apis: &mut [Api]) {
    let ref mut variants_from_header = Vec::new();
    for api in apis {
        if let Api::Enum(enm) = api {
            if enm.variants_from_header {
                if enm.variants.is_empty() {
                    variants_from_header.push(enm);
                } else {
                    let span = span_for_enum_error(enm);
                    cx.error(
                        span,
                        "enum with #![variants_from_header] must be written with no explicit variants",
                    );
                }
            }
        }
    }

    let span = match variants_from_header.get(0) {
        None => return,
        Some(enm) => enm.variants_from_header_attr.clone().unwrap(),
    };

    let ast_dump_path = match env::var_os(CXX_CLANG_AST) {
        Some(ast_dump_path) => PathBuf::from(ast_dump_path),
        None => {
            let msg = format!(
                "environment variable ${} has not been provided",
                CXX_CLANG_AST,
            );
            return cx.error(span, msg);
        }
    };

    let memmap = match File::open(&ast_dump_path).and_then(|file| unsafe { Mmap::map(&file) }) {
        Ok(memmap) => memmap,
        Err(error) => {
            let msg = format!("failed to read {}: {}", ast_dump_path.display(), error);
            return cx.error(span, msg);
        }
    };

    let is_gzipped = memmap.get(..2) == Some(b"\x1f\x8b");
    let mut gunzipped;
    let ast_dump_bytes: &[u8] = if is_gzipped {
        gunzipped = Vec::new();
        GzDecoder::new(&mut gunzipped).write_all(&memmap).unwrap();
        &gunzipped
    } else {
        &memmap
    };

    let ref root: Node = match serde_json::from_slice(ast_dump_bytes) {
        Ok(root) => root,
        Err(error) => {
            let msg = format!("failed to read {}: {}", ast_dump_path.display(), error);
            return cx.error(span, msg);
        }
    };

    let ref mut namespace = Vec::new();
    traverse(cx, root, namespace, variants_from_header, None);

    for enm in variants_from_header {
        if enm.variants.is_empty() {
            let span = &enm.variants_from_header_attr;
            let name = CxxName(&enm.name);
            let msg = format!("failed to find any C++ definition of enum {}", name);
            cx.error(span, msg);
        }
    }
}

fn traverse<'a>(
    cx: &mut Errors,
    node: &'a Node,
    namespace: &mut Vec<&'a str>,
    variants_from_header: &mut [&mut Enum],
    mut idx: Option<usize>,
) {
    match &node.kind {
        Clang::NamespaceDecl(decl) => {
            let name = match &decl.name {
                Some(name) => name,
                // Can ignore enums inside an anonymous namespace.
                None => return,
            };
            namespace.push(name);
            idx = None;
        }
        Clang::EnumDecl(decl) => {
            let name = match &decl.name {
                Some(name) => name,
                None => return,
            };
            idx = None;
            for (i, enm) in variants_from_header.iter_mut().enumerate() {
                if enm.name.cxx == **name && enm.name.namespace.iter().eq(&*namespace) {
                    if !enm.variants.is_empty() {
                        let span = &enm.variants_from_header_attr;
                        let qual_name = CxxName(&enm.name);
                        let msg = format!("found multiple C++ definitions of enum {}", qual_name);
                        cx.error(span, msg);
                        return;
                    }
                    let fixed_underlying_type = match &decl.fixed_underlying_type {
                        Some(fixed_underlying_type) => fixed_underlying_type,
                        None => {
                            let span = &enm.variants_from_header_attr;
                            let name = &enm.name.cxx;
                            let qual_name = CxxName(&enm.name);
                            let msg = format!(
                                "implicit implementation-defined repr for enum {} is not supported yet; consider changing its C++ definition to `enum {}: int {{...}}",
                                qual_name, name,
                            );
                            cx.error(span, msg);
                            return;
                        }
                    };
                    let repr = translate_qual_type(
                        cx,
                        &enm,
                        fixed_underlying_type
                            .desugared_qual_type
                            .as_ref()
                            .unwrap_or(&fixed_underlying_type.qual_type),
                    );
                    enm.repr = EnumRepr::Foreign { rust_type: repr };
                    idx = Some(i);
                    break;
                }
            }
            if idx.is_none() {
                return;
            }
        }
        Clang::EnumConstantDecl(decl) => {
            if let Some(idx) = idx {
                let enm = &mut *variants_from_header[idx];
                let span = enm
                    .variants_from_header_attr
                    .as_ref()
                    .unwrap()
                    .path
                    .get_ident()
                    .unwrap()
                    .span();
                let cxx_name = match ForeignName::parse(&decl.name, span) {
                    Ok(foreign_name) => foreign_name,
                    Err(_) => {
                        let span = &enm.variants_from_header_attr;
                        let msg = format!("unsupported C++ variant name: {}", decl.name);
                        return cx.error(span, msg);
                    }
                };
                let rust_name: Ident = match syn::parse_str(&decl.name) {
                    Ok(ident) => ident,
                    Err(_) => format_ident!("__Variant{}", enm.variants.len()),
                };
                let discriminant = match discriminant_value(&node.inner) {
                    ParsedDiscriminant::Constant(discriminant) => discriminant,
                    ParsedDiscriminant::Successor => match enm.variants.last() {
                        None => Discriminant::zero(),
                        Some(last) => match last.discriminant.checked_succ() {
                            Some(discriminant) => discriminant,
                            None => {
                                let span = &enm.variants_from_header_attr;
                                let msg = format!(
                                    "overflow processing discriminant value for variant: {}",
                                    decl.name,
                                );
                                return cx.error(span, msg);
                            }
                        },
                    },
                    ParsedDiscriminant::Fail => {
                        let span = &enm.variants_from_header_attr;
                        let msg = format!(
                            "failed to obtain discriminant value for variant: {}",
                            decl.name,
                        );
                        cx.error(span, msg);
                        Discriminant::zero()
                    }
                };
                enm.variants.push(Variant {
                    doc: Doc::new(),
                    attrs: OtherAttrs::none(),
                    name: Pair {
                        namespace: Namespace::ROOT,
                        cxx: cxx_name,
                        rust: rust_name,
                    },
                    discriminant,
                    expr: None,
                });
            }
        }
        _ => {}
    }
    for inner in &node.inner {
        traverse(cx, inner, namespace, variants_from_header, idx);
    }
    if let Clang::NamespaceDecl(_) = &node.kind {
        let _ = namespace.pop().unwrap();
    }
}

fn translate_qual_type(cx: &mut Errors, enm: &Enum, qual_type: &str) -> Path {
    let rust_std_name = match qual_type {
        "char" => "c_char",
        "int" => "c_int",
        "long" => "c_long",
        "long long" => "c_longlong",
        "signed char" => "c_schar",
        "short" => "c_short",
        "unsigned char" => "c_uchar",
        "unsigned int" => "c_uint",
        "unsigned long" => "c_ulong",
        "unsigned long long" => "c_ulonglong",
        "unsigned short" => "c_ushort",
        unsupported => {
            let span = &enm.variants_from_header_attr;
            let qual_name = CxxName(&enm.name);
            let msg = format!(
                "unsupported underlying type for {}: {}",
                qual_name, unsupported,
            );
            cx.error(span, msg);
            "c_int"
        }
    };
    let span = enm
        .variants_from_header_attr
        .as_ref()
        .unwrap()
        .path
        .get_ident()
        .unwrap()
        .span();
    let ident = Ident::new(rust_std_name, span);
    let path = quote_spanned!(span=> ::std::os::raw::#ident);
    parse_quote!(#path)
}

enum ParsedDiscriminant {
    Constant(Discriminant),
    Successor,
    Fail,
}

fn discriminant_value(mut clang: &[Node]) -> ParsedDiscriminant {
    if clang.is_empty() {
        // No discriminant expression provided; use successor of previous
        // descriminant.
        return ParsedDiscriminant::Successor;
    }

    loop {
        if clang.len() != 1 {
            return ParsedDiscriminant::Fail;
        }

        let node = &clang[0];
        match &node.kind {
            Clang::ImplicitCastExpr => clang = &node.inner,
            Clang::ConstantExpr(expr) => match Discriminant::from_str(&expr.value) {
                Ok(discriminant) => return ParsedDiscriminant::Constant(discriminant),
                Err(_) => return ParsedDiscriminant::Fail,
            },
            _ => return ParsedDiscriminant::Fail,
        }
    }
}

fn span_for_enum_error(enm: &Enum) -> TokenStream {
    let enum_token = enm.enum_token;
    let mut brace_token = Group::new(Delimiter::Brace, TokenStream::new());
    brace_token.set_span(enm.brace_token.span);
    quote!(#enum_token #brace_token)
}

struct CxxName<'a>(&'a Pair);

impl<'a> Display for CxxName<'a> {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        for namespace in &self.0.namespace {
            Display::fmt(namespace, formatter)?;
            formatter.write_str("::")?;
        }
        Display::fmt(&self.0.cxx, formatter)
    }
}
<commit_msg>Handle gz decoder errors<commit_after>use crate::syntax::attrs::OtherAttrs;
use crate::syntax::namespace::Namespace;
use crate::syntax::report::Errors;
use crate::syntax::{Api, Discriminant, Doc, Enum, EnumRepr, ForeignName, Pair, Variant};
use flate2::write::GzDecoder;
use memmap::Mmap;
use proc_macro2::{Delimiter, Group, Ident, TokenStream};
use quote::{format_ident, quote, quote_spanned};
use serde::Deserialize;
use std::env;
use std::fmt::{self, Display};
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
use std::str::FromStr;
use syn::{parse_quote, Path};

const CXX_CLANG_AST: &str = "CXX_CLANG_AST";

type Node = clang_ast::Node<Clang>;

#[derive(Deserialize)]
enum Clang {
    NamespaceDecl(NamespaceDecl),
    EnumDecl(EnumDecl),
    EnumConstantDecl(EnumConstantDecl),
    ImplicitCastExpr,
    ConstantExpr(ConstantExpr),
    Unknown,
}

#[derive(Deserialize)]
struct NamespaceDecl {
    name: Option<String>,
}

#[derive(Deserialize)]
struct EnumDecl {
    name: Option<String>,
    #[serde(rename = "fixedUnderlyingType")]
    fixed_underlying_type: Option<Type>,
}

#[derive(Deserialize)]
struct EnumConstantDecl {
    name: String,
}

#[derive(Deserialize)]
struct ConstantExpr {
    value: String,
}

#[derive(Deserialize)]
struct Type {
    #[serde(rename = "qualType")]
    qual_type: String,
    #[serde(rename = "desugaredQualType")]
    desugared_qual_type: Option<String>,
}

pub fn load(cx: &mut Errors, apis: &mut [Api]) {
    let ref mut variants_from_header = Vec::new();
    for api in apis {
        if let Api::Enum(enm) = api {
            if enm.variants_from_header {
                if enm.variants.is_empty() {
                    variants_from_header.push(enm);
                } else {
                    let span = span_for_enum_error(enm);
                    cx.error(
                        span,
                        "enum with #![variants_from_header] must be written with no explicit variants",
                    );
                }
            }
        }
    }

    let span = match variants_from_header.get(0) {
        None => return,
        Some(enm) => enm.variants_from_header_attr.clone().unwrap(),
    };

    let ast_dump_path = match env::var_os(CXX_CLANG_AST) {
        Some(ast_dump_path) => PathBuf::from(ast_dump_path),
        None => {
            let msg = format!(
                "environment variable ${} has not been provided",
                CXX_CLANG_AST,
            );
            return cx.error(span, msg);
        }
    };

    let memmap = File::open(&ast_dump_path).and_then(|file| unsafe { Mmap::map(&file) });
    let mut gunzipped;
    let ast_dump_bytes = match match memmap {
        Ok(ref memmap) => {
            let is_gzipped = memmap.get(..2) == Some(b"\x1f\x8b");
            if is_gzipped {
                gunzipped = Vec::new();
                let decode_result = GzDecoder::new(&mut gunzipped).write_all(&memmap);
                decode_result.map(|_| gunzipped.as_slice())
            } else {
                Ok(&memmap as &[u8])
            }
        }
        Err(error) => Err(error),
    } {
        Ok(bytes) => bytes,
        Err(error) => {
            let msg = format!("failed to read {}: {}", ast_dump_path.display(), error);
            return cx.error(span, msg);
        }
    };

    let ref root: Node = match serde_json::from_slice(ast_dump_bytes) {
        Ok(root) => root,
        Err(error) => {
            let msg = format!("failed to read {}: {}", ast_dump_path.display(), error);
            return cx.error(span, msg);
        }
    };

    let ref mut namespace = Vec::new();
    traverse(cx, root, namespace, variants_from_header, None);

    for enm in variants_from_header {
        if enm.variants.is_empty() {
            let span = &enm.variants_from_header_attr;
            let name = CxxName(&enm.name);
            let msg = format!("failed to find any C++ definition of enum {}", name);
            cx.error(span, msg);
        }
    }
}

fn traverse<'a>(
    cx: &mut Errors,
    node: &'a Node,
    namespace: &mut Vec<&'a str>,
    variants_from_header: &mut [&mut Enum],
    mut idx: Option<usize>,
) {
    match &node.kind {
        Clang::NamespaceDecl(decl) => {
            let name = match &decl.name {
                Some(name) => name,
                // Can ignore enums inside an anonymous namespace.
                None => return,
            };
            namespace.push(name);
            idx = None;
        }
        Clang::EnumDecl(decl) => {
            let name = match &decl.name {
                Some(name) => name,
                None => return,
            };
            idx = None;
            for (i, enm) in variants_from_header.iter_mut().enumerate() {
                if enm.name.cxx == **name && enm.name.namespace.iter().eq(&*namespace) {
                    if !enm.variants.is_empty() {
                        let span = &enm.variants_from_header_attr;
                        let qual_name = CxxName(&enm.name);
                        let msg = format!("found multiple C++ definitions of enum {}", qual_name);
                        cx.error(span, msg);
                        return;
                    }
                    let fixed_underlying_type = match &decl.fixed_underlying_type {
                        Some(fixed_underlying_type) => fixed_underlying_type,
                        None => {
                            let span = &enm.variants_from_header_attr;
                            let name = &enm.name.cxx;
                            let qual_name = CxxName(&enm.name);
                            let msg = format!(
                                "implicit implementation-defined repr for enum {} is not supported yet; consider changing its C++ definition to `enum {}: int {{...}}",
                                qual_name, name,
                            );
                            cx.error(span, msg);
                            return;
                        }
                    };
                    let repr = translate_qual_type(
                        cx,
                        &enm,
                        fixed_underlying_type
                            .desugared_qual_type
                            .as_ref()
                            .unwrap_or(&fixed_underlying_type.qual_type),
                    );
                    enm.repr = EnumRepr::Foreign { rust_type: repr };
                    idx = Some(i);
                    break;
                }
            }
            if idx.is_none() {
                return;
            }
        }
        Clang::EnumConstantDecl(decl) => {
            if let Some(idx) = idx {
                let enm = &mut *variants_from_header[idx];
                let span = enm
                    .variants_from_header_attr
                    .as_ref()
                    .unwrap()
                    .path
                    .get_ident()
                    .unwrap()
                    .span();
                let cxx_name = match ForeignName::parse(&decl.name, span) {
                    Ok(foreign_name) => foreign_name,
                    Err(_) => {
                        let span = &enm.variants_from_header_attr;
                        let msg = format!("unsupported C++ variant name: {}", decl.name);
                        return cx.error(span, msg);
                    }
                };
                let rust_name: Ident = match syn::parse_str(&decl.name) {
                    Ok(ident) => ident,
                    Err(_) => format_ident!("__Variant{}", enm.variants.len()),
                };
                let discriminant = match discriminant_value(&node.inner) {
                    ParsedDiscriminant::Constant(discriminant) => discriminant,
                    ParsedDiscriminant::Successor => match enm.variants.last() {
                        None => Discriminant::zero(),
                        Some(last) => match last.discriminant.checked_succ() {
                            Some(discriminant) => discriminant,
                            None => {
                                let span = &enm.variants_from_header_attr;
                                let msg = format!(
                                    "overflow processing discriminant value for variant: {}",
                                    decl.name,
                                );
                                return cx.error(span, msg);
                            }
                        },
                    },
                    ParsedDiscriminant::Fail => {
                        let span = &enm.variants_from_header_attr;
                        let msg = format!(
                            "failed to obtain discriminant value for variant: {}",
                            decl.name,
                        );
                        cx.error(span, msg);
                        Discriminant::zero()
                    }
                };
                enm.variants.push(Variant {
                    doc: Doc::new(),
                    attrs: OtherAttrs::none(),
                    name: Pair {
                        namespace: Namespace::ROOT,
                        cxx: cxx_name,
                        rust: rust_name,
                    },
                    discriminant,
                    expr: None,
                });
            }
        }
        _ => {}
    }
    for inner in &node.inner {
        traverse(cx, inner, namespace, variants_from_header, idx);
    }
    if let Clang::NamespaceDecl(_) = &node.kind {
        let _ = namespace.pop().unwrap();
    }
}

fn translate_qual_type(cx: &mut Errors, enm: &Enum, qual_type: &str) -> Path {
    let rust_std_name = match qual_type {
        "char" => "c_char",
        "int" => "c_int",
        "long" => "c_long",
        "long long" => "c_longlong",
        "signed char" => "c_schar",
        "short" => "c_short",
        "unsigned char" => "c_uchar",
        "unsigned int" => "c_uint",
        "unsigned long" => "c_ulong",
        "unsigned long long" => "c_ulonglong",
        "unsigned short" => "c_ushort",
        unsupported => {
            let span = &enm.variants_from_header_attr;
            let qual_name = CxxName(&enm.name);
            let msg = format!(
                "unsupported underlying type for {}: {}",
                qual_name, unsupported,
            );
            cx.error(span, msg);
            "c_int"
        }
    };
    let span = enm
        .variants_from_header_attr
        .as_ref()
        .unwrap()
        .path
        .get_ident()
        .unwrap()
        .span();
    let ident = Ident::new(rust_std_name, span);
    let path = quote_spanned!(span=> ::std::os::raw::#ident);
    parse_quote!(#path)
}

enum ParsedDiscriminant {
    Constant(Discriminant),
    Successor,
    Fail,
}

fn discriminant_value(mut clang: &[Node]) -> ParsedDiscriminant {
    if clang.is_empty() {
        // No discriminant expression provided; use successor of previous
        // descriminant.
        return ParsedDiscriminant::Successor;
    }

    loop {
        if clang.len() != 1 {
            return ParsedDiscriminant::Fail;
        }

        let node = &clang[0];
        match &node.kind {
            Clang::ImplicitCastExpr => clang = &node.inner,
            Clang::ConstantExpr(expr) => match Discriminant::from_str(&expr.value) {
                Ok(discriminant) => return ParsedDiscriminant::Constant(discriminant),
                Err(_) => return ParsedDiscriminant::Fail,
            },
            _ => return ParsedDiscriminant::Fail,
        }
    }
}

fn span_for_enum_error(enm: &Enum) -> TokenStream {
    let enum_token = enm.enum_token;
    let mut brace_token = Group::new(Delimiter::Brace, TokenStream::new());
    brace_token.set_span(enm.brace_token.span);
    quote!(#enum_token #brace_token)
}

struct CxxName<'a>(&'a Pair);

impl<'a> Display for CxxName<'a> {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        for namespace in &self.0.namespace {
            Display::fmt(namespace, formatter)?;
            formatter.write_str("::")?;
        }
        Display::fmt(&self.0.cxx, formatter)
    }
}
<|endoftext|>


<commit_before>// This file is part of the uutils coreutils package.
//
// (c) Jian Zeng <anonymousknight96@gmail.com>
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

// spell-checker:ignore (ToDO) BUFSIZE gecos fullname, mesg iobuf

#[macro_use]
extern crate uucore;
use uucore::entries::{Locate, Passwd};
use uucore::libc::S_IWGRP;
use uucore::utmpx::{self, time, Utmpx};

use std::io::prelude::*;
use std::io::BufReader;

use std::fs::File;
use std::os::unix::fs::MetadataExt;

use clap::{crate_version, App, Arg};
use std::path::PathBuf;
use uucore::InvalidEncodingHandling;

const BUFSIZE: usize = 1024;

static ABOUT: &str = "pinky - lightweight finger";

mod options {
    pub const LONG_FORMAT: &str = "long_format";
    pub const OMIT_HOME_DIR: &str = "omit_home_dir";
    pub const OMIT_PROJECT_FILE: &str = "omit_project_file";
    pub const OMIT_PLAN_FILE: &str = "omit_plan_file";
    pub const SHORT_FORMAT: &str = "short_format";
    pub const OMIT_HEADINGS: &str = "omit_headings";
    pub const OMIT_NAME: &str = "omit_name";
    pub const OMIT_NAME_HOST: &str = "omit_name_host";
    pub const OMIT_NAME_HOST_TIME: &str = "omit_name_host_time";
    pub const USER: &str = "user";
}

fn get_usage() -> String {
    format!("{0} [OPTION]... [USER]...", executable!())
}

fn get_long_usage() -> String {
    format!(
        "A lightweight 'finger' program;  print user information.\n\
         The utmp file will be {}.",
        utmpx::DEFAULT_FILE
    )
}

pub fn uumain(args: impl uucore::Args) -> i32 {
    let args = args
        .collect_str(InvalidEncodingHandling::Ignore)
        .accept_any();

    let usage = get_usage();
    let after_help = get_long_usage();

    let matches = App::new(executable!())
        .version(crate_version!())
        .about(ABOUT)
        .usage(&usage[..])
        .after_help(&after_help[..])
        .arg(
            Arg::with_name(options::LONG_FORMAT)
                .short("l")
                .requires(options::USER)
                .help("produce long format output for the specified USERs"),
        )
        .arg(
            Arg::with_name(options::OMIT_HOME_DIR)
                .short("b")
                .help("omit the user's home directory and shell in long format"),
        )
        .arg(
            Arg::with_name(options::OMIT_PROJECT_FILE)
                .short("h")
                .help("omit the user's project file in long format"),
        )
        .arg(
            Arg::with_name(options::OMIT_PLAN_FILE)
                .short("p")
                .help("omit the user's plan file in long format"),
        )
        .arg(
            Arg::with_name(options::SHORT_FORMAT)
                .short("s")
                .help("do short format output, this is the default"),
        )
        .arg(
            Arg::with_name(options::OMIT_HEADINGS)
                .short("f")
                .help("omit the line of column headings in short format"),
        )
        .arg(
            Arg::with_name(options::OMIT_NAME)
                .short("w")
                .help("omit the user's full name in short format"),
        )
        .arg(
            Arg::with_name(options::OMIT_NAME_HOST)
                .short("i")
                .help("omit the user's full name and remote host in short format"),
        )
        .arg(
            Arg::with_name(options::OMIT_NAME_HOST_TIME)
                .short("q")
                .help("omit the user's full name, remote host and idle time in short format"),
        )
        .arg(
            Arg::with_name(options::USER)
                .takes_value(true)
                .multiple(true),
        )
        .get_matches_from(args);

    let users: Vec<String> = matches
        .values_of(options::USER)
        .map(|v| v.map(ToString::to_string).collect())
        .unwrap_or_default();

    // If true, display the hours:minutes since each user has touched
    // the keyboard, or blank if within the last minute, or days followed
    // by a 'd' if not within the last day.
    let mut include_idle = true;

    // If true, display a line at the top describing each field.
    let include_heading = !matches.is_present(options::OMIT_HEADINGS);

    // if true, display the user's full name from pw_gecos.
    let mut include_fullname = true;

    // if true, display the user's ~/.project file when doing long format.
    let include_project = !matches.is_present(options::OMIT_PROJECT_FILE);

    // if true, display the user's ~/.plan file when doing long format.
    let include_plan = !matches.is_present(options::OMIT_PLAN_FILE);

    // if true, display the user's home directory and shell
    // when doing long format.
    let include_home_and_shell = !matches.is_present(options::OMIT_HOME_DIR);

    // if true, use the "short" output format.
    let do_short_format = !matches.is_present(options::LONG_FORMAT);

    /* if true, display the ut_host field. */
    let mut include_where = true;

    if matches.is_present(options::OMIT_NAME) {
        include_fullname = false;
    }
    if matches.is_present(options::OMIT_NAME_HOST) {
        include_fullname = false;
        include_where = false;
    }
    if matches.is_present(options::OMIT_NAME_HOST_TIME) {
        include_fullname = false;
        include_idle = false;
        include_where = false;
    }

    let pk = Pinky {
        include_idle,
        include_heading,
        include_fullname,
        include_project,
        include_plan,
        include_home_and_shell,
        include_where,
        names: users,
    };

    if do_short_format {
        pk.short_pinky();
        0
    } else {
        pk.long_pinky()
    }
}

struct Pinky {
    include_idle: bool,
    include_heading: bool,
    include_fullname: bool,
    include_project: bool,
    include_plan: bool,
    include_where: bool,
    include_home_and_shell: bool,
    names: Vec<String>,
}

pub trait Capitalize {
    fn capitalize(&self) -> String;
}

impl Capitalize for str {
    fn capitalize(&self) -> String {
        self.char_indices()
            .fold(String::with_capacity(self.len()), |mut acc, x| {
                if x.0 != 0 {
                    acc.push(x.1)
                } else {
                    acc.push(x.1.to_ascii_uppercase())
                }
                acc
            })
    }
}

fn idle_string(when: i64) -> String {
    thread_local! {
        static NOW: time::Tm = time::now()
    }
    NOW.with(|n| {
        let duration = n.to_timespec().sec - when;
        if duration < 60 {
            // less than 1min
            "     ".to_owned()
        } else if duration < 24 * 3600 {
            // less than 1day
            let hours = duration / (60 * 60);
            let minutes = (duration % (60 * 60)) / 60;
            format!("{:02}:{:02}", hours, minutes)
        } else {
            // more than 1day
            let days = duration / (24 * 3600);
            format!("{}d", days)
        }
    })
}

fn time_string(ut: &Utmpx) -> String {
    time::strftime("%Y-%m-%d %H:%M", &ut.login_time()).unwrap()
}

impl Pinky {
    fn print_entry(&self, ut: &Utmpx) {
        let mut pts_path = PathBuf::from("/dev");
        pts_path.push(ut.tty_device().as_str());

        let mesg;
        let last_change;
        match pts_path.metadata() {
            Ok(meta) => {
                mesg = if meta.mode() & (S_IWGRP as u32) != 0 {
                    ' '
                } else {
                    '*'
                };
                last_change = meta.atime();
            }
            _ => {
                mesg = '?';
                last_change = 0;
            }
        }

        print!("{1:<8.0$}", utmpx::UT_NAMESIZE, ut.user());

        if self.include_fullname {
            if let Ok(pw) = Passwd::locate(ut.user().as_ref()) {
                let mut gecos = pw.user_info().into_owned();
                if let Some(n) = gecos.find(',') {
                    gecos.truncate(n + 1);
                }
                print!(" {:<19.19}", gecos.replace("&", &pw.name().capitalize()));
            } else {
                print!(" {:19}", "        ???");
            }
        }

        print!(" {}{:<8.*}", mesg, utmpx::UT_LINESIZE, ut.tty_device());

        if self.include_idle {
            if last_change != 0 {
                print!(" {:<6}", idle_string(last_change));
            } else {
                print!(" {:<6}", "?????");
            }
        }

        print!(" {}", time_string(&ut));

        let mut s = ut.host();
        if self.include_where && !s.is_empty() {
            s = safe_unwrap!(ut.canon_host());
            print!(" {}", s);
        }

        println!();
    }

    fn print_heading(&self) {
        print!("{:<8}", "Login");
        if self.include_fullname {
            print!(" {:<19}", "Name");
        }
        print!(" {:<9}", " TTY");
        if self.include_idle {
            print!(" {:<6}", "Idle");
        }
        print!(" {:<16}", "When");
        if self.include_where {
            print!(" Where");
        }
        println!();
    }

    fn short_pinky(&self) {
        if self.include_heading {
            self.print_heading();
        }
        for ut in Utmpx::iter_all_records() {
            if ut.is_user_process() {
                if self.names.is_empty() {
                    self.print_entry(&ut)
                } else if self.names.iter().any(|n| n.as_str() == ut.user()) {
                    self.print_entry(&ut);
                }
            }
        }
    }

    fn long_pinky(&self) -> i32 {
        for u in &self.names {
            print!("Login name: {:<28}In real life: ", u);
            if let Ok(pw) = Passwd::locate(u.as_str()) {
                println!(" {}", pw.user_info().replace("&", &pw.name().capitalize()));
                if self.include_home_and_shell {
                    print!("Directory: {:<29}", pw.user_dir());
                    println!("Shell:  {}", pw.user_shell());
                }
                if self.include_project {
                    let mut p = PathBuf::from(pw.user_dir().as_ref());
                    p.push(".project");
                    if let Ok(f) = File::open(p) {
                        print!("Project: ");
                        read_to_console(f);
                    }
                }
                if self.include_plan {
                    let mut p = PathBuf::from(pw.user_dir().as_ref());
                    p.push(".plan");
                    if let Ok(f) = File::open(p) {
                        println!("Plan:");
                        read_to_console(f);
                    }
                }
                println!();
            } else {
                println!(" ???");
            }
        }
        0
    }
}

fn read_to_console<F: Read>(f: F) {
    let mut reader = BufReader::new(f);
    let mut iobuf = [0_u8; BUFSIZE];
    while let Ok(n) = reader.read(&mut iobuf) {
        if n == 0 {
            break;
        }
        let s = String::from_utf8_lossy(&iobuf);
        print!("{}", s);
    }
}
<commit_msg>refactor/pinky ~ fix `cargo clippy` complaint (clippy::needless_borrow)<commit_after>// This file is part of the uutils coreutils package.
//
// (c) Jian Zeng <anonymousknight96@gmail.com>
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

// spell-checker:ignore (ToDO) BUFSIZE gecos fullname, mesg iobuf

#[macro_use]
extern crate uucore;
use uucore::entries::{Locate, Passwd};
use uucore::libc::S_IWGRP;
use uucore::utmpx::{self, time, Utmpx};

use std::io::prelude::*;
use std::io::BufReader;

use std::fs::File;
use std::os::unix::fs::MetadataExt;

use clap::{crate_version, App, Arg};
use std::path::PathBuf;
use uucore::InvalidEncodingHandling;

const BUFSIZE: usize = 1024;

static ABOUT: &str = "pinky - lightweight finger";

mod options {
    pub const LONG_FORMAT: &str = "long_format";
    pub const OMIT_HOME_DIR: &str = "omit_home_dir";
    pub const OMIT_PROJECT_FILE: &str = "omit_project_file";
    pub const OMIT_PLAN_FILE: &str = "omit_plan_file";
    pub const SHORT_FORMAT: &str = "short_format";
    pub const OMIT_HEADINGS: &str = "omit_headings";
    pub const OMIT_NAME: &str = "omit_name";
    pub const OMIT_NAME_HOST: &str = "omit_name_host";
    pub const OMIT_NAME_HOST_TIME: &str = "omit_name_host_time";
    pub const USER: &str = "user";
}

fn get_usage() -> String {
    format!("{0} [OPTION]... [USER]...", executable!())
}

fn get_long_usage() -> String {
    format!(
        "A lightweight 'finger' program;  print user information.\n\
         The utmp file will be {}.",
        utmpx::DEFAULT_FILE
    )
}

pub fn uumain(args: impl uucore::Args) -> i32 {
    let args = args
        .collect_str(InvalidEncodingHandling::Ignore)
        .accept_any();

    let usage = get_usage();
    let after_help = get_long_usage();

    let matches = App::new(executable!())
        .version(crate_version!())
        .about(ABOUT)
        .usage(&usage[..])
        .after_help(&after_help[..])
        .arg(
            Arg::with_name(options::LONG_FORMAT)
                .short("l")
                .requires(options::USER)
                .help("produce long format output for the specified USERs"),
        )
        .arg(
            Arg::with_name(options::OMIT_HOME_DIR)
                .short("b")
                .help("omit the user's home directory and shell in long format"),
        )
        .arg(
            Arg::with_name(options::OMIT_PROJECT_FILE)
                .short("h")
                .help("omit the user's project file in long format"),
        )
        .arg(
            Arg::with_name(options::OMIT_PLAN_FILE)
                .short("p")
                .help("omit the user's plan file in long format"),
        )
        .arg(
            Arg::with_name(options::SHORT_FORMAT)
                .short("s")
                .help("do short format output, this is the default"),
        )
        .arg(
            Arg::with_name(options::OMIT_HEADINGS)
                .short("f")
                .help("omit the line of column headings in short format"),
        )
        .arg(
            Arg::with_name(options::OMIT_NAME)
                .short("w")
                .help("omit the user's full name in short format"),
        )
        .arg(
            Arg::with_name(options::OMIT_NAME_HOST)
                .short("i")
                .help("omit the user's full name and remote host in short format"),
        )
        .arg(
            Arg::with_name(options::OMIT_NAME_HOST_TIME)
                .short("q")
                .help("omit the user's full name, remote host and idle time in short format"),
        )
        .arg(
            Arg::with_name(options::USER)
                .takes_value(true)
                .multiple(true),
        )
        .get_matches_from(args);

    let users: Vec<String> = matches
        .values_of(options::USER)
        .map(|v| v.map(ToString::to_string).collect())
        .unwrap_or_default();

    // If true, display the hours:minutes since each user has touched
    // the keyboard, or blank if within the last minute, or days followed
    // by a 'd' if not within the last day.
    let mut include_idle = true;

    // If true, display a line at the top describing each field.
    let include_heading = !matches.is_present(options::OMIT_HEADINGS);

    // if true, display the user's full name from pw_gecos.
    let mut include_fullname = true;

    // if true, display the user's ~/.project file when doing long format.
    let include_project = !matches.is_present(options::OMIT_PROJECT_FILE);

    // if true, display the user's ~/.plan file when doing long format.
    let include_plan = !matches.is_present(options::OMIT_PLAN_FILE);

    // if true, display the user's home directory and shell
    // when doing long format.
    let include_home_and_shell = !matches.is_present(options::OMIT_HOME_DIR);

    // if true, use the "short" output format.
    let do_short_format = !matches.is_present(options::LONG_FORMAT);

    /* if true, display the ut_host field. */
    let mut include_where = true;

    if matches.is_present(options::OMIT_NAME) {
        include_fullname = false;
    }
    if matches.is_present(options::OMIT_NAME_HOST) {
        include_fullname = false;
        include_where = false;
    }
    if matches.is_present(options::OMIT_NAME_HOST_TIME) {
        include_fullname = false;
        include_idle = false;
        include_where = false;
    }

    let pk = Pinky {
        include_idle,
        include_heading,
        include_fullname,
        include_project,
        include_plan,
        include_home_and_shell,
        include_where,
        names: users,
    };

    if do_short_format {
        pk.short_pinky();
        0
    } else {
        pk.long_pinky()
    }
}

struct Pinky {
    include_idle: bool,
    include_heading: bool,
    include_fullname: bool,
    include_project: bool,
    include_plan: bool,
    include_where: bool,
    include_home_and_shell: bool,
    names: Vec<String>,
}

pub trait Capitalize {
    fn capitalize(&self) -> String;
}

impl Capitalize for str {
    fn capitalize(&self) -> String {
        self.char_indices()
            .fold(String::with_capacity(self.len()), |mut acc, x| {
                if x.0 != 0 {
                    acc.push(x.1)
                } else {
                    acc.push(x.1.to_ascii_uppercase())
                }
                acc
            })
    }
}

fn idle_string(when: i64) -> String {
    thread_local! {
        static NOW: time::Tm = time::now()
    }
    NOW.with(|n| {
        let duration = n.to_timespec().sec - when;
        if duration < 60 {
            // less than 1min
            "     ".to_owned()
        } else if duration < 24 * 3600 {
            // less than 1day
            let hours = duration / (60 * 60);
            let minutes = (duration % (60 * 60)) / 60;
            format!("{:02}:{:02}", hours, minutes)
        } else {
            // more than 1day
            let days = duration / (24 * 3600);
            format!("{}d", days)
        }
    })
}

fn time_string(ut: &Utmpx) -> String {
    time::strftime("%Y-%m-%d %H:%M", &ut.login_time()).unwrap()
}

impl Pinky {
    fn print_entry(&self, ut: &Utmpx) {
        let mut pts_path = PathBuf::from("/dev");
        pts_path.push(ut.tty_device().as_str());

        let mesg;
        let last_change;
        match pts_path.metadata() {
            Ok(meta) => {
                mesg = if meta.mode() & (S_IWGRP as u32) != 0 {
                    ' '
                } else {
                    '*'
                };
                last_change = meta.atime();
            }
            _ => {
                mesg = '?';
                last_change = 0;
            }
        }

        print!("{1:<8.0$}", utmpx::UT_NAMESIZE, ut.user());

        if self.include_fullname {
            if let Ok(pw) = Passwd::locate(ut.user().as_ref()) {
                let mut gecos = pw.user_info().into_owned();
                if let Some(n) = gecos.find(',') {
                    gecos.truncate(n + 1);
                }
                print!(" {:<19.19}", gecos.replace("&", &pw.name().capitalize()));
            } else {
                print!(" {:19}", "        ???");
            }
        }

        print!(" {}{:<8.*}", mesg, utmpx::UT_LINESIZE, ut.tty_device());

        if self.include_idle {
            if last_change != 0 {
                print!(" {:<6}", idle_string(last_change));
            } else {
                print!(" {:<6}", "?????");
            }
        }

        print!(" {}", time_string(ut));

        let mut s = ut.host();
        if self.include_where && !s.is_empty() {
            s = safe_unwrap!(ut.canon_host());
            print!(" {}", s);
        }

        println!();
    }

    fn print_heading(&self) {
        print!("{:<8}", "Login");
        if self.include_fullname {
            print!(" {:<19}", "Name");
        }
        print!(" {:<9}", " TTY");
        if self.include_idle {
            print!(" {:<6}", "Idle");
        }
        print!(" {:<16}", "When");
        if self.include_where {
            print!(" Where");
        }
        println!();
    }

    fn short_pinky(&self) {
        if self.include_heading {
            self.print_heading();
        }
        for ut in Utmpx::iter_all_records() {
            if ut.is_user_process() {
                if self.names.is_empty() {
                    self.print_entry(&ut)
                } else if self.names.iter().any(|n| n.as_str() == ut.user()) {
                    self.print_entry(&ut);
                }
            }
        }
    }

    fn long_pinky(&self) -> i32 {
        for u in &self.names {
            print!("Login name: {:<28}In real life: ", u);
            if let Ok(pw) = Passwd::locate(u.as_str()) {
                println!(" {}", pw.user_info().replace("&", &pw.name().capitalize()));
                if self.include_home_and_shell {
                    print!("Directory: {:<29}", pw.user_dir());
                    println!("Shell:  {}", pw.user_shell());
                }
                if self.include_project {
                    let mut p = PathBuf::from(pw.user_dir().as_ref());
                    p.push(".project");
                    if let Ok(f) = File::open(p) {
                        print!("Project: ");
                        read_to_console(f);
                    }
                }
                if self.include_plan {
                    let mut p = PathBuf::from(pw.user_dir().as_ref());
                    p.push(".plan");
                    if let Ok(f) = File::open(p) {
                        println!("Plan:");
                        read_to_console(f);
                    }
                }
                println!();
            } else {
                println!(" ???");
            }
        }
        0
    }
}

fn read_to_console<F: Read>(f: F) {
    let mut reader = BufReader::new(f);
    let mut iobuf = [0_u8; BUFSIZE];
    while let Ok(n) = reader.read(&mut iobuf) {
        if n == 0 {
            break;
        }
        let s = String::from_utf8_lossy(&iobuf);
        print!("{}", s);
    }
}
<|endoftext|>


<commit_before>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Ben Eggers <ben.eggers36@gmail.com>
//  * (c) Akira Hayakawa <ruby.wktk@gmail.com>
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.

#[macro_use]
extern crate uucore;

use clap::{crate_version, App, Arg};
use std::collections::{HashMap, HashSet};
use std::fs::File;
use std::io::{stdin, BufRead, BufReader, Read};
use std::path::Path;
use uucore::display::Quotable;
use uucore::InvalidEncodingHandling;

static SUMMARY: &str = "Topological sort the strings in FILE.
Strings are defined as any sequence of tokens separated by whitespace (tab, space, or newline).
If FILE is not passed in, stdin is used instead.";
static USAGE: &str = "tsort [OPTIONS] FILE";

mod options {
    pub const FILE: &str = "file";
}

pub fn uumain(args: impl uucore::Args) -> i32 {
    let args = args
        .collect_str(InvalidEncodingHandling::ConvertLossy)
        .accept_any();

    let matches = uu_app().get_matches_from(args);

    let input = matches
        .value_of(options::FILE)
        .expect("Value is required by clap");

    let mut stdin_buf;
    let mut file_buf;
    let mut reader = BufReader::new(if input == "-" {
        stdin_buf = stdin();
        &mut stdin_buf as &mut dyn Read
    } else {
        file_buf = match File::open(Path::new(&input)) {
            Ok(a) => a,
            _ => {
                show_error!("{}: No such file or directory", input.maybe_quote());
                return 1;
            }
        };
        &mut file_buf as &mut dyn Read
    });

    let mut g = Graph::new();
    loop {
        let mut line = String::new();
        match reader.read_line(&mut line) {
            Ok(_) => {
                let tokens: Vec<String> = line
                    .trim_end()
                    .split_whitespace()
                    .map(|s| s.to_owned())
                    .collect();
                if tokens.is_empty() {
                    break;
                }
                for ab in tokens.chunks(2) {
                    match ab.len() {
                        2 => g.add_edge(&ab[0], &ab[1]),
                        _ => crash!(
                            1,
                            "{}: input contains an odd number of tokens",
                            input.maybe_quote()
                        ),
                    }
                }
            }
            _ => break,
        }
    }

    g.run_tsort();

    if !g.is_acyclic() {
        crash!(1, "{}, input contains a loop:", input);
    }

    for x in &g.result {
        println!("{}", x);
    }

    0
}

pub fn uu_app() -> App<'static, 'static> {
    App::new(uucore::util_name())
        .version(crate_version!())
        .usage(USAGE)
        .about(SUMMARY)
        .arg(
            Arg::with_name(options::FILE)
                .default_value("-")
                .hidden(true),
        )
}

// We use String as a representation of node here
// but using integer may improve performance.
struct Graph {
    in_edges: HashMap<String, HashSet<String>>,
    out_edges: HashMap<String, Vec<String>>,
    result: Vec<String>,
}

impl Graph {
    fn new() -> Graph {
        Graph {
            in_edges: HashMap::new(),
            out_edges: HashMap::new(),
            result: vec![],
        }
    }

    fn has_node(&self, n: &str) -> bool {
        self.in_edges.contains_key(n)
    }

    fn has_edge(&self, from: &str, to: &str) -> bool {
        self.in_edges[to].contains(from)
    }

    fn init_node(&mut self, n: &str) {
        self.in_edges.insert(n.to_string(), HashSet::new());
        self.out_edges.insert(n.to_string(), vec![]);
    }

    fn add_edge(&mut self, from: &str, to: &str) {
        if !self.has_node(to) {
            self.init_node(to);
        }

        if !self.has_node(from) {
            self.init_node(from);
        }

        if from != to && !self.has_edge(from, to) {
            self.in_edges.get_mut(to).unwrap().insert(from.to_string());
            self.out_edges.get_mut(from).unwrap().push(to.to_string());
        }
    }

    // Kahn's algorithm
    // O(|V|+|E|)
    fn run_tsort(&mut self) {
        let mut start_nodes = vec![];
        for (n, edges) in &self.in_edges {
            if edges.is_empty() {
                start_nodes.push(n.clone());
            }
        }

        while !start_nodes.is_empty() {
            let n = start_nodes.remove(0);

            self.result.push(n.clone());

            let n_out_edges = self.out_edges.get_mut(&n).unwrap();
            for m in n_out_edges.iter() {
                let m_in_edges = self.in_edges.get_mut(m).unwrap();
                m_in_edges.remove(&n);

                // If m doesn't have other in-coming edges add it to start_nodes
                if m_in_edges.is_empty() {
                    start_nodes.push(m.clone());
                }
            }
            n_out_edges.clear();
        }
    }

    fn is_acyclic(&self) -> bool {
        for edges in self.out_edges.values() {
            if !edges.is_empty() {
                return false;
            }
        }
        true
    }
}
<commit_msg>tsort: return UResult from uumain() function<commit_after>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Ben Eggers <ben.eggers36@gmail.com>
//  * (c) Akira Hayakawa <ruby.wktk@gmail.com>
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.
use clap::{crate_version, App, Arg};
use std::collections::{HashMap, HashSet};
use std::fs::File;
use std::io::{stdin, BufRead, BufReader, Read};
use std::path::Path;
use uucore::display::Quotable;
use uucore::error::{FromIo, UResult, USimpleError};
use uucore::InvalidEncodingHandling;

static SUMMARY: &str = "Topological sort the strings in FILE.
Strings are defined as any sequence of tokens separated by whitespace (tab, space, or newline).
If FILE is not passed in, stdin is used instead.";
static USAGE: &str = "tsort [OPTIONS] FILE";

mod options {
    pub const FILE: &str = "file";
}

#[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
    let args = args
        .collect_str(InvalidEncodingHandling::ConvertLossy)
        .accept_any();

    let matches = uu_app().get_matches_from(args);

    let input = matches
        .value_of(options::FILE)
        .expect("Value is required by clap");

    let mut stdin_buf;
    let mut file_buf;
    let mut reader = BufReader::new(if input == "-" {
        stdin_buf = stdin();
        &mut stdin_buf as &mut dyn Read
    } else {
        file_buf = File::open(Path::new(&input)).map_err_context(|| input.to_string())?;
        &mut file_buf as &mut dyn Read
    });

    let mut g = Graph::new();
    loop {
        let mut line = String::new();
        match reader.read_line(&mut line) {
            Ok(_) => {
                let tokens: Vec<String> = line
                    .trim_end()
                    .split_whitespace()
                    .map(|s| s.to_owned())
                    .collect();
                if tokens.is_empty() {
                    break;
                }
                for ab in tokens.chunks(2) {
                    match ab.len() {
                        2 => g.add_edge(&ab[0], &ab[1]),
                        _ => {
                            return Err(USimpleError::new(
                                1,
                                format!(
                                    "{}: input contains an odd number of tokens",
                                    input.maybe_quote()
                                ),
                            ))
                        }
                    }
                }
            }
            _ => break,
        }
    }

    g.run_tsort();

    if !g.is_acyclic() {
        return Err(USimpleError::new(
            1,
            format!("{}, input contains a loop:", input),
        ));
    }

    for x in &g.result {
        println!("{}", x);
    }

    Ok(())
}

pub fn uu_app() -> App<'static, 'static> {
    App::new(uucore::util_name())
        .version(crate_version!())
        .usage(USAGE)
        .about(SUMMARY)
        .arg(
            Arg::with_name(options::FILE)
                .default_value("-")
                .hidden(true),
        )
}

// We use String as a representation of node here
// but using integer may improve performance.
struct Graph {
    in_edges: HashMap<String, HashSet<String>>,
    out_edges: HashMap<String, Vec<String>>,
    result: Vec<String>,
}

impl Graph {
    fn new() -> Graph {
        Graph {
            in_edges: HashMap::new(),
            out_edges: HashMap::new(),
            result: vec![],
        }
    }

    fn has_node(&self, n: &str) -> bool {
        self.in_edges.contains_key(n)
    }

    fn has_edge(&self, from: &str, to: &str) -> bool {
        self.in_edges[to].contains(from)
    }

    fn init_node(&mut self, n: &str) {
        self.in_edges.insert(n.to_string(), HashSet::new());
        self.out_edges.insert(n.to_string(), vec![]);
    }

    fn add_edge(&mut self, from: &str, to: &str) {
        if !self.has_node(to) {
            self.init_node(to);
        }

        if !self.has_node(from) {
            self.init_node(from);
        }

        if from != to && !self.has_edge(from, to) {
            self.in_edges.get_mut(to).unwrap().insert(from.to_string());
            self.out_edges.get_mut(from).unwrap().push(to.to_string());
        }
    }

    // Kahn's algorithm
    // O(|V|+|E|)
    fn run_tsort(&mut self) {
        let mut start_nodes = vec![];
        for (n, edges) in &self.in_edges {
            if edges.is_empty() {
                start_nodes.push(n.clone());
            }
        }

        while !start_nodes.is_empty() {
            let n = start_nodes.remove(0);

            self.result.push(n.clone());

            let n_out_edges = self.out_edges.get_mut(&n).unwrap();
            for m in n_out_edges.iter() {
                let m_in_edges = self.in_edges.get_mut(m).unwrap();
                m_in_edges.remove(&n);

                // If m doesn't have other in-coming edges add it to start_nodes
                if m_in_edges.is_empty() {
                    start_nodes.push(m.clone());
                }
            }
            n_out_edges.clear();
        }
    }

    fn is_acyclic(&self) -> bool {
        for edges in self.out_edges.values() {
            if !edges.is_empty() {
                return false;
            }
        }
        true
    }
}
<|endoftext|>


<commit_before>use crate::schema::*;
#[cfg(not(feature = "mysql"))]
use diesel::result::DatabaseErrorKind::CheckViolation;
use diesel::result::DatabaseErrorKind::{ForeignKeyViolation, NotNullViolation, UniqueViolation};
use diesel::result::Error::DatabaseError;
use diesel::*;

#[test]
fn unique_constraints_are_detected() {
    let connection = &mut connection();
    insert_into(users::table)
        .values(&User::new(1, "Sean"))
        .execute(connection)
        .unwrap();

    let failure = insert_into(users::table)
        .values(&User::new(1, "Jim"))
        .execute(connection);
    assert_matches!(failure, Err(DatabaseError(UniqueViolation, _)));
}

#[test]
#[cfg(feature = "postgres")]
fn unique_constraints_report_correct_constraint_name() {
    let connection = &mut connection();
    diesel::sql_query("CREATE UNIQUE INDEX users_name ON users (name)")
        .execute(connection)
        .unwrap();
    insert_into(users::table)
        .values(&User::new(1, "Sean"))
        .execute(connection)
        .unwrap();

    let failure = insert_into(users::table)
        .values(&User::new(2, "Sean"))
        .execute(connection);
    match failure {
        Err(DatabaseError(UniqueViolation, e)) => {
            assert_eq!(Some("users"), e.table_name());
            assert_eq!(None, e.column_name());
            assert_eq!(Some("users_name"), e.constraint_name());
        }
        _ => panic!(
            "{:?} did not match Err(DatabaseError(UniqueViolation, e))",
            failure
        ),
    };
}

macro_rules! try_no_coerce {
    ($e:expr) => {{
        match $e {
            Ok(e) => e,
            Err(e) => return Err(e),
        }
    }};
}

#[test]
fn cached_prepared_statements_can_be_reused_after_error() {
    let connection = &mut connection_without_transaction();
    let user = User::new(1, "Sean");
    let query = insert_into(users::table).values(&user);

    connection.test_transaction(|connection| {
        try_no_coerce!(query.execute(connection));

        let failure = query.execute(connection);
        assert_matches!(failure, Err(DatabaseError(UniqueViolation, _)));
        Ok(())
    });

    connection.test_transaction(|connection| query.execute(connection));
}

#[test]
fn foreign_key_violation_detected() {
    let connection = &mut connection();

    let failure = insert_into(fk_tests::table)
        .values(&FkTest::new(1, 100))
        .execute(connection);
    assert_matches!(failure, Err(DatabaseError(ForeignKeyViolation, _)));
}

#[test]
#[cfg(feature = "postgres")]
fn foreign_key_violation_correct_constraint_name() {
    let connection = &mut connection();

    let failure = insert_into(fk_tests::table)
        .values(&FkTest::new(1, 100))
        .execute(connection);
    match failure {
        Err(DatabaseError(ForeignKeyViolation, e)) => {
            assert_eq!(Some("fk_tests"), e.table_name());
            assert_eq!(None, e.column_name());
            assert_eq!(Some("fk_tests_fk_id_fkey"), e.constraint_name());
        }
        _ => panic!(
            "{:?} did not match Err(DatabaseError(ForeignKeyViolation, e))",
            failure
        ),
    }
}

#[test]
#[cfg(feature = "postgres")]
// This is a false positive as there is a side effect of this collect (spawning threads)
#[allow(clippy::needless_collect)]
fn isolation_errors_are_detected() {
    use diesel::result::DatabaseErrorKind::SerializationFailure;
    use diesel::result::Error::DatabaseError;
    use std::sync::{Arc, Barrier};
    use std::thread;

    table! {
        #[sql_name = "isolation_errors_are_detected"]
        isolation_example {
            id -> Serial,
            class -> Integer,
        }
    }

    let conn = &mut connection_without_transaction();

    sql_query("DROP TABLE IF EXISTS isolation_errors_are_detected;")
        .execute(conn)
        .unwrap();
    sql_query(
        r#"
        CREATE TABLE isolation_errors_are_detected (
            id SERIAL PRIMARY KEY,
            class INTEGER NOT NULL
        )
    "#,
    )
    .execute(conn)
    .unwrap();

    insert_into(isolation_example::table)
        .values(&vec![
            isolation_example::class.eq(1),
            isolation_example::class.eq(2),
        ])
        .execute(conn)
        .unwrap();

    let before_barrier = Arc::new(Barrier::new(2));
    let after_barrier = Arc::new(Barrier::new(2));
    let threads = (1..3)
        .map(|i| {
            let before_barrier = before_barrier.clone();
            let after_barrier = after_barrier.clone();
            thread::spawn(move || {
                let conn = &mut connection_without_transaction();

                conn.build_transaction().serializable().run(|conn| {
                    let _ = isolation_example::table
                        .filter(isolation_example::class.eq(i))
                        .count()
                        .execute(conn)?;

                    before_barrier.wait();

                    let other_i = if i == 1 { 2 } else { 1 };
                    let r = insert_into(isolation_example::table)
                        .values(isolation_example::class.eq(other_i))
                        .execute(conn);
                    after_barrier.wait();
                    r
                })
            })
        })
        .collect::<Vec<_>>();

    let mut results = threads
        .into_iter()
        .map(|t| t.join().unwrap())
        .collect::<Vec<_>>();

    results.sort_by_key(|r| r.is_err());

    assert_matches!(results[0], Ok(_));
    assert_matches!(results[1], Err(DatabaseError(SerializationFailure, _)));
}

#[test]
#[cfg(not(feature = "sqlite"))]
fn read_only_errors_are_detected() {
    use diesel::result::DatabaseErrorKind::ReadOnlyTransaction;

    let conn = &mut connection_without_transaction();
    diesel::sql_query("START TRANSACTION READ ONLY")
        .execute(conn)
        .unwrap();

    let result = users::table.for_update().load::<User>(conn);

    assert_matches!(result, Err(DatabaseError(ReadOnlyTransaction, _)));
}

#[test]
fn not_null_constraints_are_detected() {
    let connection = &mut connection();

    let failure = insert_into(users::table)
        .values(users::columns::hair_color.eq("black"))
        .execute(connection);

    assert_matches!(failure, Err(DatabaseError(NotNullViolation, _)));
}

#[test]
#[cfg(feature = "postgres")]
fn not_null_constraints_correct_column_name() {
    let connection = &mut connection();

    let failure = insert_into(users::table)
        .values(users::columns::hair_color.eq("black"))
        .execute(connection);

    match failure {
        Err(DatabaseError(NotNullViolation, e)) => {
            assert_eq!(Some("users"), e.table_name());
            assert_eq!(Some("name"), e.column_name());
        }
        _ => panic!(
            "{:?} did not match Err(DatabaseError(NotNullViolation, e))",
            failure
        ),
    };
}

#[test]
#[cfg(not(feature = "mysql"))]
/// MySQL < 8.0.16 doesn't enforce check constraints
fn check_constraints_are_detected() {
    let connection = &mut connection();

    insert_into(users::table)
        .values(&User::new(1, "Sean"))
        .execute(connection)
        .unwrap();

    let failure = insert_into(pokes::table)
        .values((
            pokes::columns::user_id.eq(1),
            pokes::columns::poke_count.eq(-1),
        ))
        .execute(connection);

    assert_matches!(failure, Err(DatabaseError(CheckViolation, _)));
}

#[test]
#[cfg(feature = "postgres")]
fn check_constraints_correct_constraint_name() {
    let connection = &mut connection();

    insert_into(users::table)
        .values(&User::new(1, "Sean"))
        .execute(connection)
        .unwrap();

    let failure = insert_into(pokes::table)
        .values((
            pokes::columns::user_id.eq(1),
            pokes::columns::poke_count.eq(-1),
        ))
        .execute(connection);

    match failure {
        Err(DatabaseError(CheckViolation, e)) => {
            assert_eq!(Some("pokes"), e.table_name());
            assert_eq!(None, e.column_name());
            assert_eq!(Some("pokes_poke_count_check"), e.constraint_name());
        }
        _ => panic!(
            "{:?} did not match Err(DatabaseError(CheckViolation, e))",
            failure
        ),
    };
}
<commit_msg>For whatever reason mysql does not like transaction releated in sql_query anymore<commit_after>use crate::schema::*;
#[cfg(not(feature = "mysql"))]
use diesel::result::DatabaseErrorKind::CheckViolation;
use diesel::result::DatabaseErrorKind::{ForeignKeyViolation, NotNullViolation, UniqueViolation};
use diesel::result::Error::DatabaseError;
use diesel::*;

#[test]
fn unique_constraints_are_detected() {
    let connection = &mut connection();
    insert_into(users::table)
        .values(&User::new(1, "Sean"))
        .execute(connection)
        .unwrap();

    let failure = insert_into(users::table)
        .values(&User::new(1, "Jim"))
        .execute(connection);
    assert_matches!(failure, Err(DatabaseError(UniqueViolation, _)));
}

#[test]
#[cfg(feature = "postgres")]
fn unique_constraints_report_correct_constraint_name() {
    let connection = &mut connection();
    diesel::sql_query("CREATE UNIQUE INDEX users_name ON users (name)")
        .execute(connection)
        .unwrap();
    insert_into(users::table)
        .values(&User::new(1, "Sean"))
        .execute(connection)
        .unwrap();

    let failure = insert_into(users::table)
        .values(&User::new(2, "Sean"))
        .execute(connection);
    match failure {
        Err(DatabaseError(UniqueViolation, e)) => {
            assert_eq!(Some("users"), e.table_name());
            assert_eq!(None, e.column_name());
            assert_eq!(Some("users_name"), e.constraint_name());
        }
        _ => panic!(
            "{:?} did not match Err(DatabaseError(UniqueViolation, e))",
            failure
        ),
    };
}

macro_rules! try_no_coerce {
    ($e:expr) => {{
        match $e {
            Ok(e) => e,
            Err(e) => return Err(e),
        }
    }};
}

#[test]
fn cached_prepared_statements_can_be_reused_after_error() {
    let connection = &mut connection_without_transaction();
    let user = User::new(1, "Sean");
    let query = insert_into(users::table).values(&user);

    connection.test_transaction(|connection| {
        try_no_coerce!(query.execute(connection));

        let failure = query.execute(connection);
        assert_matches!(failure, Err(DatabaseError(UniqueViolation, _)));
        Ok(())
    });

    connection.test_transaction(|connection| query.execute(connection));
}

#[test]
fn foreign_key_violation_detected() {
    let connection = &mut connection();

    let failure = insert_into(fk_tests::table)
        .values(&FkTest::new(1, 100))
        .execute(connection);
    assert_matches!(failure, Err(DatabaseError(ForeignKeyViolation, _)));
}

#[test]
#[cfg(feature = "postgres")]
fn foreign_key_violation_correct_constraint_name() {
    let connection = &mut connection();

    let failure = insert_into(fk_tests::table)
        .values(&FkTest::new(1, 100))
        .execute(connection);
    match failure {
        Err(DatabaseError(ForeignKeyViolation, e)) => {
            assert_eq!(Some("fk_tests"), e.table_name());
            assert_eq!(None, e.column_name());
            assert_eq!(Some("fk_tests_fk_id_fkey"), e.constraint_name());
        }
        _ => panic!(
            "{:?} did not match Err(DatabaseError(ForeignKeyViolation, e))",
            failure
        ),
    }
}

#[test]
#[cfg(feature = "postgres")]
// This is a false positive as there is a side effect of this collect (spawning threads)
#[allow(clippy::needless_collect)]
fn isolation_errors_are_detected() {
    use diesel::result::DatabaseErrorKind::SerializationFailure;
    use diesel::result::Error::DatabaseError;
    use std::sync::{Arc, Barrier};
    use std::thread;

    table! {
        #[sql_name = "isolation_errors_are_detected"]
        isolation_example {
            id -> Serial,
            class -> Integer,
        }
    }

    let conn = &mut connection_without_transaction();

    sql_query("DROP TABLE IF EXISTS isolation_errors_are_detected;")
        .execute(conn)
        .unwrap();
    sql_query(
        r#"
        CREATE TABLE isolation_errors_are_detected (
            id SERIAL PRIMARY KEY,
            class INTEGER NOT NULL
        )
    "#,
    )
    .execute(conn)
    .unwrap();

    insert_into(isolation_example::table)
        .values(&vec![
            isolation_example::class.eq(1),
            isolation_example::class.eq(2),
        ])
        .execute(conn)
        .unwrap();

    let before_barrier = Arc::new(Barrier::new(2));
    let after_barrier = Arc::new(Barrier::new(2));
    let threads = (1..3)
        .map(|i| {
            let before_barrier = before_barrier.clone();
            let after_barrier = after_barrier.clone();
            thread::spawn(move || {
                let conn = &mut connection_without_transaction();

                conn.build_transaction().serializable().run(|conn| {
                    let _ = isolation_example::table
                        .filter(isolation_example::class.eq(i))
                        .count()
                        .execute(conn)?;

                    before_barrier.wait();

                    let other_i = if i == 1 { 2 } else { 1 };
                    let r = insert_into(isolation_example::table)
                        .values(isolation_example::class.eq(other_i))
                        .execute(conn);
                    after_barrier.wait();
                    r
                })
            })
        })
        .collect::<Vec<_>>();

    let mut results = threads
        .into_iter()
        .map(|t| t.join().unwrap())
        .collect::<Vec<_>>();

    results.sort_by_key(|r| r.is_err());

    assert_matches!(results[0], Ok(_));
    assert_matches!(results[1], Err(DatabaseError(SerializationFailure, _)));
}

#[test]
#[cfg(not(feature = "sqlite"))]
fn read_only_errors_are_detected() {
    use diesel::connection::SimpleConnection;
    use diesel::result::DatabaseErrorKind::ReadOnlyTransaction;

    let conn = &mut connection_without_transaction();
    conn.batch_execute("START TRANSACTION READ ONLY").unwrap();

    let result = users::table.for_update().load::<User>(conn);

    assert_matches!(result, Err(DatabaseError(ReadOnlyTransaction, _)));
}

#[test]
fn not_null_constraints_are_detected() {
    let connection = &mut connection();

    let failure = insert_into(users::table)
        .values(users::columns::hair_color.eq("black"))
        .execute(connection);

    assert_matches!(failure, Err(DatabaseError(NotNullViolation, _)));
}

#[test]
#[cfg(feature = "postgres")]
fn not_null_constraints_correct_column_name() {
    let connection = &mut connection();

    let failure = insert_into(users::table)
        .values(users::columns::hair_color.eq("black"))
        .execute(connection);

    match failure {
        Err(DatabaseError(NotNullViolation, e)) => {
            assert_eq!(Some("users"), e.table_name());
            assert_eq!(Some("name"), e.column_name());
        }
        _ => panic!(
            "{:?} did not match Err(DatabaseError(NotNullViolation, e))",
            failure
        ),
    };
}

#[test]
#[cfg(not(feature = "mysql"))]
/// MySQL < 8.0.16 doesn't enforce check constraints
fn check_constraints_are_detected() {
    let connection = &mut connection();

    insert_into(users::table)
        .values(&User::new(1, "Sean"))
        .execute(connection)
        .unwrap();

    let failure = insert_into(pokes::table)
        .values((
            pokes::columns::user_id.eq(1),
            pokes::columns::poke_count.eq(-1),
        ))
        .execute(connection);

    assert_matches!(failure, Err(DatabaseError(CheckViolation, _)));
}

#[test]
#[cfg(feature = "postgres")]
fn check_constraints_correct_constraint_name() {
    let connection = &mut connection();

    insert_into(users::table)
        .values(&User::new(1, "Sean"))
        .execute(connection)
        .unwrap();

    let failure = insert_into(pokes::table)
        .values((
            pokes::columns::user_id.eq(1),
            pokes::columns::poke_count.eq(-1),
        ))
        .execute(connection);

    match failure {
        Err(DatabaseError(CheckViolation, e)) => {
            assert_eq!(Some("pokes"), e.table_name());
            assert_eq!(None, e.column_name());
            assert_eq!(Some("pokes_poke_count_check"), e.constraint_name());
        }
        _ => panic!(
            "{:?} did not match Err(DatabaseError(CheckViolation, e))",
            failure
        ),
    };
}
<|endoftext|>