<commit_before>use super::namespaces;
use crate::{library::Library, nameutil::crate_name, version::Version};
use std::collections::btree_map::{BTreeMap, Iter};

/// Provides assistance in generating use declarations.
///
/// It takes into account that use declaration referring to names within the
/// same crate will look differently. It also avoids generating spurious
/// declarations referring to names from within the same module as the one we
/// are generating code for.
#[derive(Clone, Debug, Default)]
pub struct Imports {
    /// Name of the current crate.
    crate_name: String,
    /// Name defined within current module. It doesn't need use declaration.
    ///
    /// NOTE: Currently we don't need to support more than one such name.
    defined: Option<String>,
    map: BTreeMap<String, (Option<Version>, Vec<String>)>,
}

impl Imports {
    pub fn new(gir: &Library) -> Imports {
        Imports {
            crate_name: make_crate_name(gir),
            defined: None,
            map: BTreeMap::new(),
        }
    }

    pub fn with_defined(gir: &Library, name: &str) -> Imports {
        Imports {
            crate_name: make_crate_name(gir),
            defined: Some(name.to_owned()),
            map: BTreeMap::new(),
        }
    }

    /// Declares that name should be available through its last path component.
    ///
    /// For example, if name is `X::Y::Z` then it will be available as `Z`.
    pub fn add(&mut self, name: &str, version: Option<Version>) {
        if let Some(ref defined) = self.defined {
            if name == defined {
                return;
            }
        }
        if let Some(name) = self.strip_crate_name(name) {
            let entry = self
                .map
                .entry(name.to_owned())
                .or_insert((version, Vec::new()));
            if version < entry.0 {
                *entry = (version, Vec::new());
            } else {
                *entry = (entry.0, Vec::new());
            }
        }
    }

    /// Declares that name should be available through its last path component and provides
    /// an optional feature constraint.
    ///
    /// For example, if name is `X::Y::Z` then it will be available as `Z`.
    pub fn add_with_constraint(
        &mut self,
        name: &str,
        version: Option<Version>,
        constraint: Option<&str>,
    ) {
        if let Some(ref defined) = self.defined {
            if name == defined {
                return;
            }
        }
        if let Some(name) = self.strip_crate_name(name) {
            let entry = self
                .map
                .entry(name.to_owned())
                .or_insert((version, Vec::new()));
            if version < entry.0 {
                *entry = (version, Vec::new());
            } else {
                *entry = (entry.0, Vec::new());
            }

            if let Some(constraint) = constraint {
                let constraint = String::from(constraint);
                if !entry.1.contains(&constraint) {
                    entry.1.push(constraint);
                }
            }
        }
    }

    /// Declares that name should be available through its full path.
    ///
    /// For example, if name is `X::Y` then it will be available as `X::Y`.
    pub fn add_used_type(&mut self, used_type: &str, version: Option<Version>) {
        if let Some(i) = used_type.find("::") {
            if i == 0 {
                self.add(&used_type[2..], version);
            } else {
                self.add(&used_type[..i], version);
            }
        } else {
            self.add(used_type, version);
        }
    }

    pub fn add_used_types(&mut self, used_types: &[String], version: Option<Version>) {
        for s in used_types {
            self.add_used_type(s, version);
        }
    }

    /// Tries to strip crate name prefix from given name.
    ///
    /// Returns `None` if name matches crate name exactly. Otherwise returns
    /// name with crate name prefix stripped or full name if there was no match.
    fn strip_crate_name<'a>(&self, name: &'a str) -> Option<&'a str> {
        let prefix = &self.crate_name;
        if !name.starts_with(prefix) {
            return Some(name);
        }
        let rest = &name[prefix.len()..];
        if rest.is_empty() {
            None
        } else if rest.starts_with("::") {
            Some(&rest["::".len()..])
        } else {
            // It was false positive, return the whole name.
            Some(name)
        }
    }

    pub fn iter(&self) -> Iter<'_, String, (Option<Version>, Vec<String>)> {
        self.map.iter()
    }
}

fn make_crate_name(gir: &Library) -> String {
    let name = gir.namespace(namespaces::MAIN).name.as_str();
    if name == "GObject" {
        crate_name("GLib")
    } else {
        crate_name(name)
    }
}
<commit_msg>Fix imports handling<commit_after>use super::namespaces;
use crate::{library::Library, nameutil::crate_name, version::Version};
use std::collections::btree_map::{BTreeMap, Iter};

/// Provides assistance in generating use declarations.
///
/// It takes into account that use declaration referring to names within the
/// same crate will look differently. It also avoids generating spurious
/// declarations referring to names from within the same module as the one we
/// are generating code for.
#[derive(Clone, Debug, Default)]
pub struct Imports {
    /// Name of the current crate.
    crate_name: String,
    /// Name defined within current module. It doesn't need use declaration.
    ///
    /// NOTE: Currently we don't need to support more than one such name.
    defined: Option<String>,
    map: BTreeMap<String, (Option<Version>, Vec<String>)>,
}

impl Imports {
    pub fn new(gir: &Library) -> Imports {
        Imports {
            crate_name: make_crate_name(gir),
            defined: None,
            map: BTreeMap::new(),
        }
    }

    pub fn with_defined(gir: &Library, name: &str) -> Imports {
        Imports {
            crate_name: make_crate_name(gir),
            defined: Some(name.to_owned()),
            map: BTreeMap::new(),
        }
    }

    /// Declares that name should be available through its last path component.
    ///
    /// For example, if name is `X::Y::Z` then it will be available as `Z`.
    pub fn add(&mut self, name: &str, version: Option<Version>) {
        if let Some(ref defined) = self.defined {
            if name == defined {
                return;
            }
        }
        if let Some(name) = self.strip_crate_name(name) {
            let entry = self
                .map
                .entry(name.to_owned())
                .or_insert((version, Vec::new()));
            if version < entry.0 {
                *entry = (version, Vec::new());
            } else {
                *entry = (entry.0, Vec::new());
            }
            // Since there is no constraint on this import, if any constraint
            // is present, we can just remove it.
            entry.1.clear();
        }
    }

    /// Declares that name should be available through its last path component and provides
    /// an optional feature constraint.
    ///
    /// For example, if name is `X::Y::Z` then it will be available as `Z`.
    pub fn add_with_constraint(
        &mut self,
        name: &str,
        version: Option<Version>,
        constraint: Option<&str>,
    ) {
        if let Some(ref defined) = self.defined {
            if name == defined {
                return;
            }
        }
        if let Some(name) = self.strip_crate_name(name) {
            let entry = if let Some(constraint) = constraint {
                let constraint = String::from(constraint);
                let entry = self
                    .map
                    .entry(name.to_owned())
                    .or_insert((version, vec![constraint.clone()]));
                // If the import is already present but doesn't have any constraint,
                // we don't want to add one. Otherwise, we just check if the constraint
                // is already present or not before adding it.
                if !entry.1.is_empty() && !entry.1.iter().any(|x| x == &constraint) {
                    entry.1.push(constraint);
                }
                entry
            } else {
                let entry = self
                    .map
                    .entry(name.to_owned())
                    .or_insert((version, Vec::new()));
                // Since there is no constraint on this import, if any constraint
                // is present, we can just remove it.
                entry.1.clear();
                entry
            };
            if version < entry.0 {
                entry.0 = version;
            }
        }
    }

    /// Declares that name should be available through its full path.
    ///
    /// For example, if name is `X::Y` then it will be available as `X::Y`.
    pub fn add_used_type(&mut self, used_type: &str, version: Option<Version>) {
        if let Some(i) = used_type.find("::") {
            if i == 0 {
                self.add(&used_type[2..], version);
            } else {
                self.add(&used_type[..i], version);
            }
        } else {
            self.add(used_type, version);
        }
    }

    pub fn add_used_types(&mut self, used_types: &[String], version: Option<Version>) {
        for s in used_types {
            self.add_used_type(s, version);
        }
    }

    /// Tries to strip crate name prefix from given name.
    ///
    /// Returns `None` if name matches crate name exactly. Otherwise returns
    /// name with crate name prefix stripped or full name if there was no match.
    fn strip_crate_name<'a>(&self, name: &'a str) -> Option<&'a str> {
        let prefix = &self.crate_name;
        if !name.starts_with(prefix) {
            return Some(name);
        }
        let rest = &name[prefix.len()..];
        if rest.is_empty() {
            None
        } else if rest.starts_with("::") {
            Some(&rest["::".len()..])
        } else {
            // It was false positive, return the whole name.
            Some(name)
        }
    }

    pub fn iter(&self) -> Iter<'_, String, (Option<Version>, Vec<String>)> {
        self.map.iter()
    }
}

fn make_crate_name(gir: &Library) -> String {
    let name = gir.namespace(namespaces::MAIN).name.as_str();
    if name == "GObject" {
        crate_name("GLib")
    } else {
        crate_name(name)
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

//! Describes a graphical or compute operation.
//!
//! In Vulkan before you can add a draw or a compute command to a command buffer, you have to
//! create a *pipeline object* that describes it.
//!
//! There are two kinds of pipelines:
//!
//! - `ComputePipeline`s, for compute operations that reads/write raw pixels from images and
//!   buffers.
//! - `GraphicsPipeline`s, for graphical operations.
//!
//! All the sub-modules of this module (with the exception of `cache`) correspond to the various
//! steps of pipelines.

pub use self::compute_pipeline::ComputePipeline;
pub use self::graphics_pipeline::GraphicsPipeline;
pub use self::graphics_pipeline::GraphicsPipelineParams;
pub use self::graphics_pipeline::GraphicsPipelineParamsTess;
pub use self::graphics_pipeline::GraphicsPipelineCreationError;

mod compute_pipeline;
mod graphics_pipeline;

pub mod blend;
pub mod cache;
pub mod depth_stencil;
pub mod input_assembly;
pub mod multisample;
pub mod raster;
pub mod shader;
pub mod vertex;
pub mod viewport;
<commit_msg>Fix ComputePipelineCreationError not reexported<commit_after>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

//! Describes a graphical or compute operation.
//!
//! In Vulkan before you can add a draw or a compute command to a command buffer, you have to
//! create a *pipeline object* that describes it.
//!
//! There are two kinds of pipelines:
//!
//! - `ComputePipeline`s, for compute operations that reads/write raw pixels from images and
//!   buffers.
//! - `GraphicsPipeline`s, for graphical operations.
//!
//! All the sub-modules of this module (with the exception of `cache`) correspond to the various
//! steps of pipelines.

pub use self::compute_pipeline::ComputePipeline;
pub use self::compute_pipeline::ComputePipelineCreationError;
pub use self::graphics_pipeline::GraphicsPipeline;
pub use self::graphics_pipeline::GraphicsPipelineParams;
pub use self::graphics_pipeline::GraphicsPipelineParamsTess;
pub use self::graphics_pipeline::GraphicsPipelineCreationError;

mod compute_pipeline;
mod graphics_pipeline;

pub mod blend;
pub mod cache;
pub mod depth_stencil;
pub mod input_assembly;
pub mod multisample;
pub mod raster;
pub mod shader;
pub mod vertex;
pub mod viewport;
<|endoftext|>


<commit_before>#![warn(rust_2018_idioms)]

use futures::stream::StreamExt;
use std::{env, io, str};
use tokio_util::codec::{Decoder, Encoder};

use bytes::BytesMut;
use tokio_serial::SerialPortBuilderExt;

#[cfg(unix)]
const DEFAULT_TTY: &str = "/dev/ttyUSB0";
#[cfg(windows)]
const DEFAULT_TTY: &str = "COM1";

struct LineCodec;

impl Decoder for LineCodec {
    type Item = String;
    type Error = io::Error;

    fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
        let newline = src.as_ref().iter().position(|b| *b == b'\n');
        if let Some(n) = newline {
            let line = src.split_to(n + 1);
            return match str::from_utf8(line.as_ref()) {
                Ok(s) => Ok(Some(s.to_string())),
                Err(_) => Err(io::Error::new(io::ErrorKind::Other, "Invalid String")),
            };
        }
        Ok(None)
    }
}

impl Encoder<String> for LineCodec {
    type Error = io::Error;

    fn encode(&mut self, _item: String, _dst: &mut BytesMut) -> Result<(), Self::Error> {
        Ok(())
    }
}

#[tokio::main]
async fn main() -> tokio_serial::Result<()> {
    let mut args = env::args();
    let tty_path = args.nth(1).unwrap_or_else(|| DEFAULT_TTY.into());

    let mut port = tokio_serial::new(tty_path, 9600).open_async()?;

    #[cfg(unix)]
    port.set_exclusive(false)
        .expect("Unable to set serial port exclusive to false");

    let mut reader = LineCodec.framed(port);

    while let Some(line_result) = reader.next().await {
        let line = line_result.expect("Failed to read line");
        println!("{}", line);
    }
    Ok(())
}
<commit_msg>Fix building the example after the rename to `open_native_async`<commit_after>#![warn(rust_2018_idioms)]

use futures::stream::StreamExt;
use std::{env, io, str};
use tokio_util::codec::{Decoder, Encoder};

use bytes::BytesMut;
use tokio_serial::SerialPortBuilderExt;

#[cfg(unix)]
const DEFAULT_TTY: &str = "/dev/ttyUSB0";
#[cfg(windows)]
const DEFAULT_TTY: &str = "COM1";

struct LineCodec;

impl Decoder for LineCodec {
    type Item = String;
    type Error = io::Error;

    fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
        let newline = src.as_ref().iter().position(|b| *b == b'\n');
        if let Some(n) = newline {
            let line = src.split_to(n + 1);
            return match str::from_utf8(line.as_ref()) {
                Ok(s) => Ok(Some(s.to_string())),
                Err(_) => Err(io::Error::new(io::ErrorKind::Other, "Invalid String")),
            };
        }
        Ok(None)
    }
}

impl Encoder<String> for LineCodec {
    type Error = io::Error;

    fn encode(&mut self, _item: String, _dst: &mut BytesMut) -> Result<(), Self::Error> {
        Ok(())
    }
}

#[tokio::main]
async fn main() -> tokio_serial::Result<()> {
    let mut args = env::args();
    let tty_path = args.nth(1).unwrap_or_else(|| DEFAULT_TTY.into());

    let mut port = tokio_serial::new(tty_path, 9600).open_native_async()?;

    #[cfg(unix)]
    port.set_exclusive(false)
        .expect("Unable to set serial port exclusive to false");

    let mut reader = LineCodec.framed(port);

    while let Some(line_result) = reader.next().await {
        let line = line_result.expect("Failed to read line");
        println!("{}", line);
    }
    Ok(())
}
<|endoftext|>


<commit_before>use clippy_utils::{
    diagnostics::span_lint_and_sugg,
    get_async_fn_body, is_async_fn,
    source::{snippet_with_applicability, snippet_with_context, walk_span_to_context},
    visitors::visit_break_exprs,
};
use rustc_errors::Applicability;
use rustc_hir::intravisit::FnKind;
use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, FnRetTy, HirId};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::{Span, SyntaxContext};

declare_clippy_lint! {
    /// **What it does:** Checks for missing return statements at the end of a block.
    ///
    /// **Why is this bad?** Actually omitting the return keyword is idiomatic Rust code. Programmers
    /// coming from other languages might prefer the expressiveness of `return`. It's possible to miss
    /// the last returning statement because the only difference is a missing `;`. Especially in bigger
    /// code with multiple return paths having a `return` keyword makes it easier to find the
    /// corresponding statements.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// fn foo(x: usize) -> usize {
    ///     x
    /// }
    /// ```
    /// add return
    /// ```rust
    /// fn foo(x: usize) -> usize {
    ///     return x;
    /// }
    /// ```
    pub IMPLICIT_RETURN,
    restriction,
    "use a return statement like `return expr` instead of an expression"
}

declare_lint_pass!(ImplicitReturn => [IMPLICIT_RETURN]);

fn lint_return(cx: &LateContext<'_>, span: Span) {
    let mut app = Applicability::MachineApplicable;
    let snip = snippet_with_applicability(cx, span, "..", &mut app);
    span_lint_and_sugg(
        cx,
        IMPLICIT_RETURN,
        span,
        "missing `return` statement",
        "add `return` as shown",
        format!("return {}", snip),
        app,
    );
}

fn lint_break(cx: &LateContext<'_>, break_span: Span, expr_span: Span) {
    let mut app = Applicability::MachineApplicable;
    let snip = snippet_with_context(cx, expr_span, break_span.ctxt(), "..", &mut app).0;
    span_lint_and_sugg(
        cx,
        IMPLICIT_RETURN,
        break_span,
        "missing `return` statement",
        "change `break` to `return` as shown",
        format!("return {}", snip),
        app,
    )
}

enum LintLocation {
    /// The lint was applied to a parent expression.
    Parent,
    /// The lint was applied to this expression, a child, or not applied.
    Inner,
}
impl LintLocation {
    fn still_parent(self, b: bool) -> Self {
        if b { self } else { Self::Inner }
    }

    fn is_parent(&self) -> bool {
        matches!(*self, Self::Parent)
    }
}

// Gets the call site if the span is in a child context. Otherwise returns `None`.
fn get_call_site(span: Span, ctxt: SyntaxContext) -> Option<Span> {
    (span.ctxt() != ctxt).then(|| walk_span_to_context(span, ctxt).unwrap_or(span))
}

fn lint_implicit_returns(
    cx: &LateContext<'tcx>,
    expr: &'tcx Expr<'_>,
    // The context of the function body.
    ctxt: SyntaxContext,
    // Whether the expression is from a macro expansion.
    call_site_span: Option<Span>,
) -> LintLocation {
    match expr.kind {
        ExprKind::Block(
            Block {
                expr: Some(block_expr), ..
            },
            _,
        ) => lint_implicit_returns(
            cx,
            block_expr,
            ctxt,
            call_site_span.or_else(|| get_call_site(block_expr.span, ctxt)),
        )
        .still_parent(call_site_span.is_some()),

        ExprKind::If(_, then_expr, Some(else_expr)) => {
            // Both `then_expr` or `else_expr` are required to be blocks in the same context as the `if`. Don't
            // bother checking.
            let res = lint_implicit_returns(cx, then_expr, ctxt, call_site_span).still_parent(call_site_span.is_some());
            if res.is_parent() {
                // The return was added as a parent of this if expression.
                return res;
            }
            lint_implicit_returns(cx, else_expr, ctxt, call_site_span).still_parent(call_site_span.is_some())
        },

        ExprKind::Match(_, arms, _) => {
            for arm in arms {
                let res = lint_implicit_returns(
                    cx,
                    arm.body,
                    ctxt,
                    call_site_span.or_else(|| get_call_site(arm.body.span, ctxt)),
                )
                .still_parent(call_site_span.is_some());
                if res.is_parent() {
                    // The return was added as a parent of this match expression.
                    return res;
                }
            }
            LintLocation::Inner
        },

        ExprKind::Loop(block, ..) => {
            let mut add_return = false;
            visit_break_exprs(block, |break_expr, dest, sub_expr| {
                if dest.target_id.ok() == Some(expr.hir_id) {
                    if call_site_span.is_none() && break_expr.span.ctxt() == ctxt {
                        lint_break(cx, break_expr.span, sub_expr.unwrap().span);
                    } else {
                        // the break expression is from a macro call, add a return to the loop
                        add_return = true;
                    }
                }
            });
            if add_return {
                #[allow(clippy::option_if_let_else)]
                if let Some(span) = call_site_span {
                    lint_return(cx, span);
                    LintLocation::Parent
                } else {
                    lint_return(cx, expr.span);
                    LintLocation::Inner
                }
            } else {
                LintLocation::Inner
            }
        },

        // If expressions without an else clause, and blocks without a final expression can only be the final expression
        // if they are divergent, or return the unit type.
        ExprKind::If(_, _, None) | ExprKind::Block(Block { expr: None, .. }, _) | ExprKind::Ret(_) => {
            LintLocation::Inner
        },

        // Any divergent expression doesn't need a return statement.
        ExprKind::MethodCall(..)
        | ExprKind::Call(..)
        | ExprKind::Binary(..)
        | ExprKind::Unary(..)
        | ExprKind::Index(..)
            if cx.typeck_results().expr_ty(expr).is_never() =>
        {
            LintLocation::Inner
        },

        _ =>
        {
            #[allow(clippy::option_if_let_else)]
            if let Some(span) = call_site_span {
                lint_return(cx, span);
                LintLocation::Parent
            } else {
                lint_return(cx, expr.span);
                LintLocation::Inner
            }
        },
    }
}

impl<'tcx> LateLintPass<'tcx> for ImplicitReturn {
    fn check_fn(
        &mut self,
        cx: &LateContext<'tcx>,
        kind: FnKind<'tcx>,
        decl: &'tcx FnDecl<'_>,
        body: &'tcx Body<'_>,
        span: Span,
        _: HirId,
    ) {
        if (!matches!(kind, FnKind::Closure) && matches!(decl.output, FnRetTy::DefaultReturn(_)))
            || span.ctxt() != body.value.span.ctxt()
            || in_external_macro(cx.sess(), span)
        {
            return;
        }

        let res_ty = cx.typeck_results().expr_ty(&body.value);
        if res_ty.is_unit() || res_ty.is_never() {
            return;
        }

        let expr = if is_async_fn(kind) {
            match get_async_fn_body(cx.tcx, body) {
                Some(e) => e,
                None => return,
            }
        } else {
            &body.value
        };
        lint_implicit_returns(cx, expr, expr.span.ctxt(), None);
    }
}
<commit_msg>Minor cleanup of `implicit_return`<commit_after>use clippy_utils::{
    diagnostics::span_lint_and_sugg,
    get_async_fn_body, is_async_fn,
    source::{snippet_with_applicability, snippet_with_context, walk_span_to_context},
    visitors::visit_break_exprs,
};
use rustc_errors::Applicability;
use rustc_hir::intravisit::FnKind;
use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, FnRetTy, HirId};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::{Span, SyntaxContext};

declare_clippy_lint! {
    /// **What it does:** Checks for missing return statements at the end of a block.
    ///
    /// **Why is this bad?** Actually omitting the return keyword is idiomatic Rust code. Programmers
    /// coming from other languages might prefer the expressiveness of `return`. It's possible to miss
    /// the last returning statement because the only difference is a missing `;`. Especially in bigger
    /// code with multiple return paths having a `return` keyword makes it easier to find the
    /// corresponding statements.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// fn foo(x: usize) -> usize {
    ///     x
    /// }
    /// ```
    /// add return
    /// ```rust
    /// fn foo(x: usize) -> usize {
    ///     return x;
    /// }
    /// ```
    pub IMPLICIT_RETURN,
    restriction,
    "use a return statement like `return expr` instead of an expression"
}

declare_lint_pass!(ImplicitReturn => [IMPLICIT_RETURN]);

fn lint_return(cx: &LateContext<'_>, span: Span) {
    let mut app = Applicability::MachineApplicable;
    let snip = snippet_with_applicability(cx, span, "..", &mut app);
    span_lint_and_sugg(
        cx,
        IMPLICIT_RETURN,
        span,
        "missing `return` statement",
        "add `return` as shown",
        format!("return {}", snip),
        app,
    );
}

fn lint_break(cx: &LateContext<'_>, break_span: Span, expr_span: Span) {
    let mut app = Applicability::MachineApplicable;
    let snip = snippet_with_context(cx, expr_span, break_span.ctxt(), "..", &mut app).0;
    span_lint_and_sugg(
        cx,
        IMPLICIT_RETURN,
        break_span,
        "missing `return` statement",
        "change `break` to `return` as shown",
        format!("return {}", snip),
        app,
    )
}

#[derive(Clone, Copy, PartialEq, Eq)]
enum LintLocation {
    /// The lint was applied to a parent expression.
    Parent,
    /// The lint was applied to this expression, a child, or not applied.
    Inner,
}
impl LintLocation {
    fn still_parent(self, b: bool) -> Self {
        if b { self } else { Self::Inner }
    }

    fn is_parent(self) -> bool {
        self == Self::Parent
    }
}

// Gets the call site if the span is in a child context. Otherwise returns `None`.
fn get_call_site(span: Span, ctxt: SyntaxContext) -> Option<Span> {
    (span.ctxt() != ctxt).then(|| walk_span_to_context(span, ctxt).unwrap_or(span))
}

fn lint_implicit_returns(
    cx: &LateContext<'tcx>,
    expr: &'tcx Expr<'_>,
    // The context of the function body.
    ctxt: SyntaxContext,
    // Whether the expression is from a macro expansion.
    call_site_span: Option<Span>,
) -> LintLocation {
    match expr.kind {
        ExprKind::Block(
            Block {
                expr: Some(block_expr), ..
            },
            _,
        ) => lint_implicit_returns(
            cx,
            block_expr,
            ctxt,
            call_site_span.or_else(|| get_call_site(block_expr.span, ctxt)),
        )
        .still_parent(call_site_span.is_some()),

        ExprKind::If(_, then_expr, Some(else_expr)) => {
            // Both `then_expr` or `else_expr` are required to be blocks in the same context as the `if`. Don't
            // bother checking.
            let res = lint_implicit_returns(cx, then_expr, ctxt, call_site_span).still_parent(call_site_span.is_some());
            if res.is_parent() {
                // The return was added as a parent of this if expression.
                return res;
            }
            lint_implicit_returns(cx, else_expr, ctxt, call_site_span).still_parent(call_site_span.is_some())
        },

        ExprKind::Match(_, arms, _) => {
            for arm in arms {
                let res = lint_implicit_returns(
                    cx,
                    arm.body,
                    ctxt,
                    call_site_span.or_else(|| get_call_site(arm.body.span, ctxt)),
                )
                .still_parent(call_site_span.is_some());
                if res.is_parent() {
                    // The return was added as a parent of this match expression.
                    return res;
                }
            }
            LintLocation::Inner
        },

        ExprKind::Loop(block, ..) => {
            let mut add_return = false;
            visit_break_exprs(block, |break_expr, dest, sub_expr| {
                if dest.target_id.ok() == Some(expr.hir_id) {
                    if call_site_span.is_none() && break_expr.span.ctxt() == ctxt {
                        lint_break(cx, break_expr.span, sub_expr.unwrap().span);
                    } else {
                        // the break expression is from a macro call, add a return to the loop
                        add_return = true;
                    }
                }
            });
            if add_return {
                #[allow(clippy::option_if_let_else)]
                if let Some(span) = call_site_span {
                    lint_return(cx, span);
                    LintLocation::Parent
                } else {
                    lint_return(cx, expr.span);
                    LintLocation::Inner
                }
            } else {
                LintLocation::Inner
            }
        },

        // If expressions without an else clause, and blocks without a final expression can only be the final expression
        // if they are divergent, or return the unit type.
        ExprKind::If(_, _, None) | ExprKind::Block(Block { expr: None, .. }, _) | ExprKind::Ret(_) => {
            LintLocation::Inner
        },

        // Any divergent expression doesn't need a return statement.
        ExprKind::MethodCall(..)
        | ExprKind::Call(..)
        | ExprKind::Binary(..)
        | ExprKind::Unary(..)
        | ExprKind::Index(..)
            if cx.typeck_results().expr_ty(expr).is_never() =>
        {
            LintLocation::Inner
        },

        _ =>
        {
            #[allow(clippy::option_if_let_else)]
            if let Some(span) = call_site_span {
                lint_return(cx, span);
                LintLocation::Parent
            } else {
                lint_return(cx, expr.span);
                LintLocation::Inner
            }
        },
    }
}

impl<'tcx> LateLintPass<'tcx> for ImplicitReturn {
    fn check_fn(
        &mut self,
        cx: &LateContext<'tcx>,
        kind: FnKind<'tcx>,
        decl: &'tcx FnDecl<'_>,
        body: &'tcx Body<'_>,
        span: Span,
        _: HirId,
    ) {
        if (!matches!(kind, FnKind::Closure) && matches!(decl.output, FnRetTy::DefaultReturn(_)))
            || span.ctxt() != body.value.span.ctxt()
            || in_external_macro(cx.sess(), span)
        {
            return;
        }

        let res_ty = cx.typeck_results().expr_ty(&body.value);
        if res_ty.is_unit() || res_ty.is_never() {
            return;
        }

        let expr = if is_async_fn(kind) {
            match get_async_fn_body(cx.tcx, body) {
                Some(e) => e,
                None => return,
            }
        } else {
            &body.value
        };
        lint_implicit_returns(cx, expr, expr.span.ctxt(), None);
    }
}
<|endoftext|>


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

use os_pipe;
use tempfile::TempPath;

use super::args::{Arg, Arguments};

use error::{Error, ResultExt};

#[derive(
    Serialize, Deserialize, Ord, PartialOrd, Eq, PartialEq, Hash, Clone, Debug,
)]
pub struct Process {
    /// Program name.
    pub program: PathBuf,

    /// Program arguments.
    pub args: Arguments,

    /// Optional working directory to spawn the process in. If `None`, uses the
    /// working directory of the parent process (i.e., the build process).
    pub cwd: Option<PathBuf>,

    /// Optional environment variables.
    pub env: Option<BTreeMap<String, String>>,

    /// File to send to standard input. If `None`, the standard input stream
    /// reads from `/dev/null` or equivalent.
    pub stdin: Option<PathBuf>,

    /// Redirect standard output to a file instead. If the path is `/dev/null`,
    /// a cross-platform way of sending the output to a black hole is used. If
    /// `None`, the output is logged by this task.
    pub stdout: Option<PathBuf>,

    /// Redirect standard error to a file instead. If the path is `/dev/null`,
    /// a cross-platform way of sending the output to a black hole is
    /// used. If `None`, the output is logged by this task.
    pub stderr: Option<PathBuf>,
}

impl Process {
    /// If this path is given for stdin, stdout, or stderr, then I/O is
    /// redirected to a cross-platform blackhole.
    pub const DEV_NULL: &'static str = "/dev/null";

    pub fn new(program: PathBuf, args: Arguments) -> Process {
        Process {
            program,
            args,
            cwd: None,
            env: None,
            stdin: None,
            stdout: None,
            stderr: None,
        }
    }

    /// Replaces the arguments with a temporary response file and returns the
    /// temporary path.
    pub fn response_file(&mut self) -> Result<TempPath, io::Error> {
        let temp = self.args.response_file()?;

        let mut arg = OsString::new();
        arg.push("@");
        arg.push(&temp);

        // TODO: Handle the unlikely error.
        let mut args = Arguments::new();
        args.push(arg.into_string().unwrap().into());

        self.args = args;

        Ok(temp)
    }

    /// Spawns the process.
    ///
    /// The I/O pipes are handled in special ways:
    ///
    ///  - `stdin` is always redirected from `/dev/null` (or platform-specific
    ///    equivalent) unless a file path is given.
    ///  - `stderr` and `stdout` are always interleaved unless one (or both) are
    ///    redirected to a file path.
    pub fn spawn(
        &self,
        root: &Path,
    ) -> Result<(os_pipe::PipeReader, Child), Error> {
        let mut child = process::Command::new(&self.program);

        if let Some(ref path) = self.stdin {
            if path == Path::new(Self::DEV_NULL) {
                child.stdin(process::Stdio::null());
            } else {
                child.stdin(fs::File::open(path)?);
            }
        } else {
            // We don't ever want the build system to pause waiting for user
            // input from the parent process' input stream.
            child.stdin(process::Stdio::null());
        }

        let (reader, writer) = os_pipe::pipe()?;

        {
            // Make sure the writer is dropped even if it isn't used below.
            // Otherwise, the parent process will hang reading from the child
            // process.
            let writer = writer;

            if let Some(ref path) = self.stdout {
                if path == Path::new(Self::DEV_NULL) {
                    // Use cross-platform method.
                    child.stdout(process::Stdio::null());
                } else {
                    child.stdout(fs::File::create(path)?);
                }
            } else {
                child.stdout(writer.try_clone()?);
            }

            if let Some(ref path) = self.stderr {
                if path == Path::new(Self::DEV_NULL) {
                    // Use cross-platform method.
                    child.stderr(process::Stdio::null());
                } else {
                    child.stderr(fs::File::create(path)?);
                }
            } else {
                child.stderr(writer);
            }
        }

        if let Some(ref cwd) = self.cwd {
            child.current_dir(root.join(cwd));
        } else if !root.as_os_str().is_empty() {
            child.current_dir(root);
        }

        if let Some(ref env) = self.env {
            child.envs(env);
        }

        child.args(&self.args);

        let handle = child.spawn().context("Failed to spawn process")?;

        Ok((reader, Child(handle)))
    }
}

impl fmt::Display for Process {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", Arg::new(&self.program.to_string_lossy().as_ref()))?;

        for arg in &self.args {
            write!(f, " {}", arg)?;
        }

        Ok(())
    }
}

pub struct Child(process::Child);

impl Child {
    /// Wait for the child to exit. An error is returned if the process exited
    /// with a code other than 0.
    pub fn wait(mut self) -> Result<(), io::Error> {
        let status = self.0.wait()?;
        match status.code() {
            Some(code) => {
                if code == 0 {
                    Ok(())
                } else {
                    Err(io::Error::new(
                        io::ErrorKind::Other,
                        format!("Process exited with error code {}", code),
                    ))
                }
            }
            None => {
                // Handle signals on Unix platforms.
                #[cfg(unix)]
                {
                    use std::os::unix::process::ExitStatusExt;

                    Err(io::Error::new(
                        io::ErrorKind::Other,
                        format!(
                            "Process terminated by signal {}",
                            status.signal().unwrap()
                        ),
                    ))
                }

                #[cfg(windows)]
                Ok(())
            }
        }
    }
}
<commit_msg>Minor cleanup of Process spawning impl<commit_after>// Copyright (c) 2018 Jason White
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
use std::collections::BTreeMap;
use std::ffi::OsString;
use std::fmt;
use std::fs;
use std::io;
use std::path::{Path, PathBuf};
use std::process;

use os_pipe::{pipe, PipeReader};
use tempfile::TempPath;

use super::args::{Arg, Arguments};

use error::{Error, ResultExt};

#[derive(
    Serialize, Deserialize, Ord, PartialOrd, Eq, PartialEq, Hash, Clone, Debug,
)]
pub struct Process {
    /// Program name.
    pub program: PathBuf,

    /// Program arguments.
    pub args: Arguments,

    /// Optional working directory to spawn the process in. If `None`, uses the
    /// working directory of the parent process (i.e., the build process).
    pub cwd: Option<PathBuf>,

    /// Optional environment variables.
    pub env: Option<BTreeMap<String, String>>,

    /// File to send to standard input. If `None`, the standard input stream
    /// reads from `/dev/null` or equivalent.
    pub stdin: Option<PathBuf>,

    /// Redirect standard output to a file instead. If the path is `/dev/null`,
    /// a cross-platform way of sending the output to a black hole is used. If
    /// `None`, the output is logged by this task.
    pub stdout: Option<PathBuf>,

    /// Redirect standard error to a file instead. If the path is `/dev/null`,
    /// a cross-platform way of sending the output to a black hole is
    /// used. If `None`, the output is logged by this task.
    pub stderr: Option<PathBuf>,
}

impl Process {
    /// If this path is given for stdin, stdout, or stderr, then I/O is
    /// redirected to a cross-platform blackhole.
    pub const DEV_NULL: &'static str = "/dev/null";

    pub fn new(program: PathBuf, args: Arguments) -> Process {
        Process {
            program,
            args,
            cwd: None,
            env: None,
            stdin: None,
            stdout: None,
            stderr: None,
        }
    }

    /// Replaces the arguments with a temporary response file and returns the
    /// temporary path.
    pub fn response_file(&mut self) -> Result<TempPath, io::Error> {
        let temp = self.args.response_file()?;

        let mut arg = OsString::new();
        arg.push("@");
        arg.push(&temp);

        // TODO: Handle the unlikely error.
        let mut args = Arguments::new();
        args.push(arg.into_string().unwrap().into());

        self.args = args;

        Ok(temp)
    }

    /// Creates the child process data structure, but does not spawn it.
    fn child(
        &self,
        root: &Path,
    ) -> Result<(PipeReader, process::Command), io::Error> {
        let mut child = process::Command::new(&self.program);

        if let Some(ref path) = self.stdin {
            if path == Path::new(Self::DEV_NULL) {
                child.stdin(process::Stdio::null());
            } else {
                child.stdin(fs::File::open(path)?);
            }
        } else {
            // We don't ever want the build system to pause waiting for user
            // input from the parent process' input stream.
            child.stdin(process::Stdio::null());
        }

        let (reader, writer) = pipe()?;

        {
            // Make sure the writer is dropped even if it isn't used below.
            // Otherwise, the parent process will hang reading from the child
            // process.
            let writer = writer;

            if let Some(ref path) = self.stdout {
                if path == Path::new(Self::DEV_NULL) {
                    // Use cross-platform method.
                    child.stdout(process::Stdio::null());
                } else {
                    child.stdout(fs::File::create(path)?);
                }
            } else {
                child.stdout(writer.try_clone()?);
            }

            if let Some(ref path) = self.stderr {
                if path == Path::new(Self::DEV_NULL) {
                    // Use cross-platform method.
                    child.stderr(process::Stdio::null());
                } else {
                    child.stderr(fs::File::create(path)?);
                }
            } else {
                child.stderr(writer);
            }
        }

        if let Some(ref cwd) = self.cwd {
            child.current_dir(root.join(cwd));
        } else if !root.as_os_str().is_empty() {
            child.current_dir(root);
        }

        if let Some(ref env) = self.env {
            child.envs(env);
        }

        child.args(&self.args);

        Ok((reader, child))
    }

    /// Creates and spawns the child process.
    ///
    /// The I/O pipes are handled in special ways:
    ///
    ///  - `stdin` is always redirected from `/dev/null` (or platform-specific
    ///    equivalent) unless a file path is given.
    ///  - `stderr` and `stdout` are always interleaved unless one (or both) are
    ///    redirected to a file path.
    pub fn spawn(&self, root: &Path) -> Result<(PipeReader, Child), Error> {
        let (reader, mut child) = self.child(root)?;

        let handle = child.spawn().context("Failed to spawn process")?;

        Ok((reader, Child(handle)))
    }
}

impl fmt::Display for Process {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", Arg::new(&self.program.to_string_lossy().as_ref()))?;

        for arg in &self.args {
            write!(f, " {}", arg)?;
        }

        Ok(())
    }
}

pub struct Child(process::Child);

impl Child {
    /// Wait for the child to exit. An error is returned if the process exited
    /// with a code other than 0.
    pub fn wait(mut self) -> Result<(), io::Error> {
        let status = self.0.wait()?;
        match status.code() {
            Some(code) => {
                if code == 0 {
                    Ok(())
                } else {
                    Err(io::Error::new(
                        io::ErrorKind::Other,
                        format!("Process exited with error code {}", code),
                    ))
                }
            }
            None => {
                // Handle signals on Unix platforms.
                #[cfg(unix)]
                {
                    use std::os::unix::process::ExitStatusExt;

                    Err(io::Error::new(
                        io::ErrorKind::Other,
                        format!(
                            "Process terminated by signal {}",
                            status.signal().unwrap()
                        ),
                    ))
                }

                #[cfg(windows)]
                Ok(())
            }
        }
    }
}
<|endoftext|>


<commit_before>// Each integration test uses only a handful of the functions here, and generates the "unused code"
// warnings for the rest. This is annoying and useless, so we suppress the warning.
#![allow(dead_code)]

// `libc` contains file mode constants, like S_IXUSR, which are useful for `struct Chmod`. Thus it
// makes sense to re-export the crate.
pub use libc;

use libnewsboat::configpaths::ConfigPaths;
use rand::random;
use std::io::{Read, Write};
use std::os::unix::fs::PermissionsExt;
use std::{fs, path};
use tempfile::TempDir;

/// Strings that are placed in files before running commands, to see if commands modify those
/// files.
pub struct FileSentries {
    /// Sentry for the config file.
    pub config: String,
    /// Sentry for the urls file.
    pub urls: String,
    /// Sentry for the cache file.
    pub cache: String,
    /// Sentry for the queue file.
    pub queue: String,
    /// Sentry for the search history file.
    pub search: String,
    /// Sentry for the command-line history file.
    pub cmdline: String,
}

impl FileSentries {
    /// Create a new struct with random strings for sentries.
    pub fn new() -> FileSentries {
        FileSentries {
            config: random::<u32>().to_string() + &"config",
            urls: random::<u32>().to_string() + &"urls",
            cache: random::<u32>().to_string() + &"cache",
            queue: random::<u32>().to_string() + &"queue",
            search: random::<u32>().to_string() + &"search",
            cmdline: random::<u32>().to_string() + &"cmdline",
        }
    }
}

fn mock_dotdir(dotdir_path: &path::Path, sentries: &FileSentries) {
    assert!(fs::create_dir(&dotdir_path).is_ok());
    assert!(create_file(&dotdir_path.join("config"), &sentries.config));
    assert!(create_file(&dotdir_path.join("urls"), &sentries.urls));
    assert!(create_file(&dotdir_path.join("cache.db"), &sentries.cache));
    assert!(create_file(&dotdir_path.join("queue"), &sentries.queue));
    assert!(create_file(
        &dotdir_path.join("history.search"),
        &sentries.search
    ));
    assert!(create_file(
        &dotdir_path.join("history.cmdline"),
        &sentries.cmdline
    ));
}

pub fn mock_newsbeuter_dotdir(tmp: &TempDir) -> FileSentries {
    let sentries = FileSentries::new();

    let dotdir_path = tmp.path().join(".newsbeuter");
    mock_dotdir(&dotdir_path, &sentries);

    sentries
}

pub fn mock_newsboat_dotdir(tmp: &TempDir) -> FileSentries {
    let sentries = FileSentries::new();

    let dotdir_path = tmp.path().join(".newsboat");
    mock_dotdir(&dotdir_path, &sentries);

    sentries
}

pub fn mock_xdg_dirs(config_dir: &path::Path, data_dir: &path::Path) -> FileSentries {
    let sentries = FileSentries::new();

    assert!(fs::create_dir_all(&config_dir).is_ok());
    assert!(create_file(&config_dir.join("config"), &sentries.config));
    assert!(create_file(&config_dir.join("urls"), &sentries.urls));

    assert!(fs::create_dir_all(&data_dir).is_ok());
    assert!(create_file(&data_dir.join("cache.db"), &sentries.cache));
    assert!(create_file(&data_dir.join("queue"), &sentries.queue));
    assert!(create_file(
        &data_dir.join("history.search"),
        &sentries.search
    ));
    assert!(create_file(
        &data_dir.join("history.cmdline"),
        &sentries.cmdline
    ));

    sentries
}

pub fn mock_newsbeuter_xdg_dirs(tmp: &TempDir) -> FileSentries {
    let config_dir_path = tmp.path().join(".config").join("newsbeuter");
    let data_dir_path = tmp.path().join(".local").join("share").join("newsbeuter");
    mock_xdg_dirs(&config_dir_path, &data_dir_path)
}

pub fn mock_newsboat_xdg_dirs(tmp: &TempDir) -> FileSentries {
    let config_dir_path = tmp.path().join(".config").join("newsboat");
    let data_dir_path = tmp.path().join(".local").join("share").join("newsboat");
    mock_xdg_dirs(&config_dir_path, &data_dir_path)
}

/// Creates a file at given `filepath` and writes `content` into it.
///
/// Returns `true` if successful, `false` otherwise.
pub fn create_file(path: &path::Path, content: &str) -> bool {
    fs::File::create(path)
        .and_then(|mut f| f.write_all(content.as_bytes()))
        .is_ok()
}

pub fn file_contents(path: &path::Path) -> String {
    fs::File::open(path)
        .and_then(|mut f| {
            let mut buf = String::new();
            let _ = f.read_to_string(&mut buf);
            Ok(buf)
        })
        // If failed to open/read file, return an empty string
        .unwrap_or_else(|_| String::new())
}

fn is_readable(filepath: &path::Path) -> bool {
    fs::File::open(&filepath).is_ok()
}

pub fn assert_xdg_not_migrated(config_dir: &path::Path, data_dir: &path::Path) {
    let mut paths = ConfigPaths::new();
    assert!(paths.initialized());

    // Shouldn't migrate anything, so should return false.
    assert!(!paths.try_migrate_from_newsbeuter());

    assert!(!is_readable(&config_dir.join("config")));
    assert!(!is_readable(&config_dir.join("urls")));

    assert!(!is_readable(&data_dir.join("cache.db")));
    assert!(!is_readable(&data_dir.join("queue")));
    assert!(!is_readable(&data_dir.join("history.search")));
    assert!(!is_readable(&data_dir.join("history.cmdline")));
}

/// Sets new permissions on a given path, and restores them back when the
/// object is destroyed.
pub struct Chmod<'a> {
    path: &'a path::Path,
    original_mode: u32,
}

impl<'a> Chmod<'a> {
    pub fn new(path: &'a path::Path, new_mode: libc::mode_t) -> Chmod<'a> {
        let original_mode = fs::metadata(path)
            .expect(&format!(
                "Chmod: couldn't obtain metadata for `{}'",
                path.display()
            ))
            .permissions()
            .mode();

        fs::set_permissions(path, fs::Permissions::from_mode(new_mode.into())).expect(&format!(
            "Chmod: couldn't change mode for `{}'",
            path.display()
        ));

        Chmod {
            path,
            original_mode,
        }
    }
}

impl<'a> Drop for Chmod<'a> {
    fn drop(self: &mut Self) {
        fs::set_permissions(self.path, fs::Permissions::from_mode(self.original_mode)).expect(
            &format!(
                "Chmod: couldn't change back the mode for `{}'",
                self.path.display()
            ),
        );
    }
}

pub fn assert_dotdir_not_migrated(dotdir: &path::Path) {
    let mut paths = ConfigPaths::new();
    assert!(paths.initialized());

    // Shouldn't migrate anything, so should return false.
    assert!(!paths.try_migrate_from_newsbeuter());

    assert!(!is_readable(&dotdir.join("config")));
    assert!(!is_readable(&dotdir.join("urls")));

    assert!(!is_readable(&dotdir.join("cache.db")));
    assert!(!is_readable(&dotdir.join("queue")));
    assert!(!is_readable(&dotdir.join("history.search")));
    assert!(!is_readable(&dotdir.join("history.cmdline")));
}

pub fn assert_create_dirs_returns_false(tmp: &TempDir) {
    let readonly = libc::S_IRUSR | libc::S_IXUSR;
    let _home_chmod = Chmod::new(&tmp.path(), readonly);

    let paths = ConfigPaths::new();
    assert!(paths.initialized());
    assert!(!paths.create_dirs());
}
<commit_msg>Fix clippy::expect_fun_call<commit_after>// Each integration test uses only a handful of the functions here, and generates the "unused code"
// warnings for the rest. This is annoying and useless, so we suppress the warning.
#![allow(dead_code)]

// `libc` contains file mode constants, like S_IXUSR, which are useful for `struct Chmod`. Thus it
// makes sense to re-export the crate.
pub use libc;

use libnewsboat::configpaths::ConfigPaths;
use rand::random;
use std::io::{Read, Write};
use std::os::unix::fs::PermissionsExt;
use std::{fs, path};
use tempfile::TempDir;

/// Strings that are placed in files before running commands, to see if commands modify those
/// files.
pub struct FileSentries {
    /// Sentry for the config file.
    pub config: String,
    /// Sentry for the urls file.
    pub urls: String,
    /// Sentry for the cache file.
    pub cache: String,
    /// Sentry for the queue file.
    pub queue: String,
    /// Sentry for the search history file.
    pub search: String,
    /// Sentry for the command-line history file.
    pub cmdline: String,
}

impl FileSentries {
    /// Create a new struct with random strings for sentries.
    pub fn new() -> FileSentries {
        FileSentries {
            config: random::<u32>().to_string() + &"config",
            urls: random::<u32>().to_string() + &"urls",
            cache: random::<u32>().to_string() + &"cache",
            queue: random::<u32>().to_string() + &"queue",
            search: random::<u32>().to_string() + &"search",
            cmdline: random::<u32>().to_string() + &"cmdline",
        }
    }
}

fn mock_dotdir(dotdir_path: &path::Path, sentries: &FileSentries) {
    assert!(fs::create_dir(&dotdir_path).is_ok());
    assert!(create_file(&dotdir_path.join("config"), &sentries.config));
    assert!(create_file(&dotdir_path.join("urls"), &sentries.urls));
    assert!(create_file(&dotdir_path.join("cache.db"), &sentries.cache));
    assert!(create_file(&dotdir_path.join("queue"), &sentries.queue));
    assert!(create_file(
        &dotdir_path.join("history.search"),
        &sentries.search
    ));
    assert!(create_file(
        &dotdir_path.join("history.cmdline"),
        &sentries.cmdline
    ));
}

pub fn mock_newsbeuter_dotdir(tmp: &TempDir) -> FileSentries {
    let sentries = FileSentries::new();

    let dotdir_path = tmp.path().join(".newsbeuter");
    mock_dotdir(&dotdir_path, &sentries);

    sentries
}

pub fn mock_newsboat_dotdir(tmp: &TempDir) -> FileSentries {
    let sentries = FileSentries::new();

    let dotdir_path = tmp.path().join(".newsboat");
    mock_dotdir(&dotdir_path, &sentries);

    sentries
}

pub fn mock_xdg_dirs(config_dir: &path::Path, data_dir: &path::Path) -> FileSentries {
    let sentries = FileSentries::new();

    assert!(fs::create_dir_all(&config_dir).is_ok());
    assert!(create_file(&config_dir.join("config"), &sentries.config));
    assert!(create_file(&config_dir.join("urls"), &sentries.urls));

    assert!(fs::create_dir_all(&data_dir).is_ok());
    assert!(create_file(&data_dir.join("cache.db"), &sentries.cache));
    assert!(create_file(&data_dir.join("queue"), &sentries.queue));
    assert!(create_file(
        &data_dir.join("history.search"),
        &sentries.search
    ));
    assert!(create_file(
        &data_dir.join("history.cmdline"),
        &sentries.cmdline
    ));

    sentries
}

pub fn mock_newsbeuter_xdg_dirs(tmp: &TempDir) -> FileSentries {
    let config_dir_path = tmp.path().join(".config").join("newsbeuter");
    let data_dir_path = tmp.path().join(".local").join("share").join("newsbeuter");
    mock_xdg_dirs(&config_dir_path, &data_dir_path)
}

pub fn mock_newsboat_xdg_dirs(tmp: &TempDir) -> FileSentries {
    let config_dir_path = tmp.path().join(".config").join("newsboat");
    let data_dir_path = tmp.path().join(".local").join("share").join("newsboat");
    mock_xdg_dirs(&config_dir_path, &data_dir_path)
}

/// Creates a file at given `filepath` and writes `content` into it.
///
/// Returns `true` if successful, `false` otherwise.
pub fn create_file(path: &path::Path, content: &str) -> bool {
    fs::File::create(path)
        .and_then(|mut f| f.write_all(content.as_bytes()))
        .is_ok()
}

pub fn file_contents(path: &path::Path) -> String {
    fs::File::open(path)
        .and_then(|mut f| {
            let mut buf = String::new();
            let _ = f.read_to_string(&mut buf);
            Ok(buf)
        })
        // If failed to open/read file, return an empty string
        .unwrap_or_else(|_| String::new())
}

fn is_readable(filepath: &path::Path) -> bool {
    fs::File::open(&filepath).is_ok()
}

pub fn assert_xdg_not_migrated(config_dir: &path::Path, data_dir: &path::Path) {
    let mut paths = ConfigPaths::new();
    assert!(paths.initialized());

    // Shouldn't migrate anything, so should return false.
    assert!(!paths.try_migrate_from_newsbeuter());

    assert!(!is_readable(&config_dir.join("config")));
    assert!(!is_readable(&config_dir.join("urls")));

    assert!(!is_readable(&data_dir.join("cache.db")));
    assert!(!is_readable(&data_dir.join("queue")));
    assert!(!is_readable(&data_dir.join("history.search")));
    assert!(!is_readable(&data_dir.join("history.cmdline")));
}

/// Sets new permissions on a given path, and restores them back when the
/// object is destroyed.
pub struct Chmod<'a> {
    path: &'a path::Path,
    original_mode: u32,
}

impl<'a> Chmod<'a> {
    pub fn new(path: &'a path::Path, new_mode: libc::mode_t) -> Chmod<'a> {
        let original_mode = fs::metadata(path)
            .unwrap_or_else(|_| panic!("Chmod: couldn't obtain metadata for `{}'", path.display()))
            .permissions()
            .mode();

        fs::set_permissions(path, fs::Permissions::from_mode(new_mode.into()))
            .unwrap_or_else(|_| panic!("Chmod: couldn't change mode for `{}'", path.display()));

        Chmod {
            path,
            original_mode,
        }
    }
}

impl<'a> Drop for Chmod<'a> {
    fn drop(self: &mut Self) {
        fs::set_permissions(self.path, fs::Permissions::from_mode(self.original_mode))
            .unwrap_or_else(|_| {
                panic!(
                    "Chmod: couldn't change back the mode for `{}'",
                    self.path.display()
                )
            });
    }
}

pub fn assert_dotdir_not_migrated(dotdir: &path::Path) {
    let mut paths = ConfigPaths::new();
    assert!(paths.initialized());

    // Shouldn't migrate anything, so should return false.
    assert!(!paths.try_migrate_from_newsbeuter());

    assert!(!is_readable(&dotdir.join("config")));
    assert!(!is_readable(&dotdir.join("urls")));

    assert!(!is_readable(&dotdir.join("cache.db")));
    assert!(!is_readable(&dotdir.join("queue")));
    assert!(!is_readable(&dotdir.join("history.search")));
    assert!(!is_readable(&dotdir.join("history.cmdline")));
}

pub fn assert_create_dirs_returns_false(tmp: &TempDir) {
    let readonly = libc::S_IRUSR | libc::S_IXUSR;
    let _home_chmod = Chmod::new(&tmp.path(), readonly);

    let paths = ConfigPaths::new();
    assert!(paths.initialized());
    assert!(!paths.create_dirs());
}
<|endoftext|>


<commit_before>use crate::error::{Error, Result};
use crate::gen::fs;
use crate::Project;
use std::env;
use std::ffi::{OsStr, OsString};
use std::path::{Component, Path, PathBuf};

pub(crate) enum TargetDir {
    Path(PathBuf),
    Unknown,
}

pub(crate) fn out_dir() -> Result<PathBuf> {
    env::var_os("OUT_DIR")
        .map(PathBuf::from)
        .ok_or(Error::MissingOutDir)
}

// Given a path provided by the user, determines where generated files related
// to that path should go in our out dir. In particular we don't want to
// accidentally write generated code upward of our out dir, even if the user
// passed a path containing lots of `..` or an absolute path.
pub(crate) fn local_relative_path(path: &Path) -> PathBuf {
    let mut rel_path = PathBuf::new();
    for component in path.components() {
        match component {
            Component::Prefix(_) | Component::RootDir | Component::CurDir => {}
            Component::ParentDir => drop(rel_path.pop()), // noop if empty
            Component::Normal(name) => rel_path.push(name),
        }
    }
    rel_path
}

pub(crate) fn namespaced(base: &Path, rel_path: &Path) -> PathBuf {
    let mut path = base.to_owned();
    path.push("cxxbridge");
    path.extend(package_name());
    path.push(rel_path);
    path
}

pub(crate) trait PathExt {
    fn with_appended_extension(&self, suffix: impl AsRef<OsStr>) -> PathBuf;
}

impl PathExt for Path {
    fn with_appended_extension(&self, suffix: impl AsRef<OsStr>) -> PathBuf {
        let mut file_name = self.file_name().unwrap().to_owned();
        file_name.push(suffix);
        self.with_file_name(file_name)
    }
}

pub(crate) fn include_dir(prj: &Project) -> PathBuf {
    match &prj.target_dir {
        TargetDir::Path(target_dir) => target_dir.join("cxxbridge"),
        TargetDir::Unknown => prj.out_dir.join("cxxbridge"),
    }
}

pub(crate) fn manifest_dir() -> Option<PathBuf> {
    env::var_os("CARGO_MANIFEST_DIR").map(PathBuf::from)
}

pub(crate) fn package_name() -> Option<OsString> {
    env::var_os("CARGO_PKG_NAME")
}

pub(crate) fn search_parents_for_target_dir(out_dir: &Path) -> TargetDir {
    // fs::canonicalize on Windows produces UNC paths which cl.exe is unable to
    // handle in includes.
    // https://github.com/rust-lang/rust/issues/42869
    // https://github.com/alexcrichton/cc-rs/issues/169
    let mut also_try_canonical = cfg!(not(windows));

    let mut dir = out_dir.to_owned();
    loop {
        let is_target = dir.ends_with("target");
        let parent_contains_cargo_toml = dir.with_file_name("Cargo.toml").exists();
        if is_target && parent_contains_cargo_toml {
            return TargetDir::Path(dir);
        }
        if dir.pop() {
            continue;
        }
        if also_try_canonical {
            if let Ok(canonical_dir) = out_dir.canonicalize() {
                dir = canonical_dir;
                also_try_canonical = false;
                continue;
            }
        }
        return TargetDir::Unknown;
    }
}

#[cfg(unix)]
pub(crate) use self::fs::symlink_file as symlink_or_copy;

#[cfg(windows)]
pub(crate) fn symlink_or_copy(src: &Path, dst: &Path) -> Result<()> {
    // Pre-Windows 10, symlinks require admin privileges. Since Windows 10, they
    // require Developer Mode. If it fails, fall back to copying the file.
    if fs::symlink_file(src, dst).is_err() {
        fs::copy(src, dst)?;
    }
    Ok(())
}

#[cfg(not(any(unix, windows)))]
pub(crate) use self::fs::copy as symlink_or_copy;

#[cfg(any(unix, windows))]
pub(crate) use self::fs::symlink_dir;

#[cfg(not(any(unix, windows)))]
pub(crate) fn symlink_dir(_src: impl AsRef<Path>, _dst: impl AsRef<Path>) -> Result<()> {
    Ok(())
}
<commit_msg>Match symlink_file's signature on windows<commit_after>use crate::error::{Error, Result};
use crate::gen::fs;
use crate::Project;
use std::env;
use std::ffi::{OsStr, OsString};
use std::path::{Component, Path, PathBuf};

pub(crate) enum TargetDir {
    Path(PathBuf),
    Unknown,
}

pub(crate) fn out_dir() -> Result<PathBuf> {
    env::var_os("OUT_DIR")
        .map(PathBuf::from)
        .ok_or(Error::MissingOutDir)
}

// Given a path provided by the user, determines where generated files related
// to that path should go in our out dir. In particular we don't want to
// accidentally write generated code upward of our out dir, even if the user
// passed a path containing lots of `..` or an absolute path.
pub(crate) fn local_relative_path(path: &Path) -> PathBuf {
    let mut rel_path = PathBuf::new();
    for component in path.components() {
        match component {
            Component::Prefix(_) | Component::RootDir | Component::CurDir => {}
            Component::ParentDir => drop(rel_path.pop()), // noop if empty
            Component::Normal(name) => rel_path.push(name),
        }
    }
    rel_path
}

pub(crate) fn namespaced(base: &Path, rel_path: &Path) -> PathBuf {
    let mut path = base.to_owned();
    path.push("cxxbridge");
    path.extend(package_name());
    path.push(rel_path);
    path
}

pub(crate) trait PathExt {
    fn with_appended_extension(&self, suffix: impl AsRef<OsStr>) -> PathBuf;
}

impl PathExt for Path {
    fn with_appended_extension(&self, suffix: impl AsRef<OsStr>) -> PathBuf {
        let mut file_name = self.file_name().unwrap().to_owned();
        file_name.push(suffix);
        self.with_file_name(file_name)
    }
}

pub(crate) fn include_dir(prj: &Project) -> PathBuf {
    match &prj.target_dir {
        TargetDir::Path(target_dir) => target_dir.join("cxxbridge"),
        TargetDir::Unknown => prj.out_dir.join("cxxbridge"),
    }
}

pub(crate) fn manifest_dir() -> Option<PathBuf> {
    env::var_os("CARGO_MANIFEST_DIR").map(PathBuf::from)
}

pub(crate) fn package_name() -> Option<OsString> {
    env::var_os("CARGO_PKG_NAME")
}

pub(crate) fn search_parents_for_target_dir(out_dir: &Path) -> TargetDir {
    // fs::canonicalize on Windows produces UNC paths which cl.exe is unable to
    // handle in includes.
    // https://github.com/rust-lang/rust/issues/42869
    // https://github.com/alexcrichton/cc-rs/issues/169
    let mut also_try_canonical = cfg!(not(windows));

    let mut dir = out_dir.to_owned();
    loop {
        let is_target = dir.ends_with("target");
        let parent_contains_cargo_toml = dir.with_file_name("Cargo.toml").exists();
        if is_target && parent_contains_cargo_toml {
            return TargetDir::Path(dir);
        }
        if dir.pop() {
            continue;
        }
        if also_try_canonical {
            if let Ok(canonical_dir) = out_dir.canonicalize() {
                dir = canonical_dir;
                also_try_canonical = false;
                continue;
            }
        }
        return TargetDir::Unknown;
    }
}

#[cfg(unix)]
pub(crate) use self::fs::symlink_file as symlink_or_copy;

#[cfg(windows)]
pub(crate) fn symlink_or_copy(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> Result<()> {
    // Pre-Windows 10, symlinks require admin privileges. Since Windows 10, they
    // require Developer Mode. If it fails, fall back to copying the file.
    let src = src.as_ref();
    let dst = dst.as_ref();
    if fs::symlink_file(src, dst).is_err() {
        fs::copy(src, dst)?;
    }
    Ok(())
}

#[cfg(not(any(unix, windows)))]
pub(crate) use self::fs::copy as symlink_or_copy;

#[cfg(any(unix, windows))]
pub(crate) use self::fs::symlink_dir;

#[cfg(not(any(unix, windows)))]
pub(crate) fn symlink_dir(_src: impl AsRef<Path>, _dst: impl AsRef<Path>) -> Result<()> {
    Ok(())
}
<|endoftext|>


<commit_before>// Functionality that is shared between the cxx_build::bridge entry point and
// the cxxbridge CLI command.

mod error;
mod file;
pub(super) mod include;
pub(super) mod out;
mod write;

#[cfg(test)]
mod tests;

pub(super) use self::error::Error;
use self::error::{format_err, Result};
use self::file::File;
use crate::syntax::report::Errors;
use crate::syntax::{self, check, Types};
use std::fs;
use std::path::Path;

/// Options for C++ code generation.
///
/// We expect options to be added over time, so this is a non-exhaustive struct.
/// To instantiate one you need to crate a default value and mutate those fields
/// that you want to modify.
///
/// ```
/// # use cxx_gen::Opt;
/// #
/// let impl_annotations = r#"__attribute__((visibility("default")))"#.to_owned();
///
/// let mut opt = Opt::default();
/// opt.cxx_impl_annotations = Some(impl_annotations);
/// ```
#[derive(Default)]
#[non_exhaustive]
pub struct Opt {
    /// Any additional headers to #include. The cxxbridge tool does not parse or
    /// even require the given paths to exist; they simply go into the generated
    /// C++ code as #include lines.
    pub include: Vec<String>,
    /// Optional annotation for implementations of C++ function wrappers that
    /// may be exposed to Rust. You may for example need to provide
    /// `__declspec(dllexport)` or `__attribute__((visibility("default")))` if
    /// Rust code from one shared object or executable depends on these C++
    /// functions in another.
    pub cxx_impl_annotations: Option<String>,
}

/// Results of code generation.
pub struct GeneratedCode {
    /// The bytes of a C++ header file.
    pub header: Vec<u8>,
    /// The bytes of a C++ implementation file (e.g. .cc, cpp etc.)
    pub implementation: Vec<u8>,
}

pub(super) fn do_generate_bridge(path: &Path, opt: &Opt) -> Vec<u8> {
    let header = false;
    generate_from_path(path, opt, header)
}

pub(super) fn do_generate_header(path: &Path, opt: &Opt) -> Vec<u8> {
    let header = true;
    generate_from_path(path, opt, header)
}

fn generate_from_path(path: &Path, opt: &Opt, header: bool) -> Vec<u8> {
    let source = match fs::read_to_string(path) {
        Ok(source) => source,
        Err(err) => format_err(path, "", Error::Io(err)),
    };
    match generate_from_string(&source, opt, header) {
        Ok(out) => out,
        Err(err) => format_err(path, &source, err),
    }
}

fn generate_from_string(source: &str, opt: &Opt, header: bool) -> Result<Vec<u8>> {
    let mut source = source;
    if source.starts_with("#!") && !source.starts_with("#![") {
        let shebang_end = source.find('\n').unwrap_or(source.len());
        source = &source[shebang_end..];
    }
    let syntax: File = syn::parse_str(source)?;
    let generated = generate(syntax, opt, header, !header)?;
    Ok(if header {
        generated.header
    } else {
        generated.implementation
    })
}

pub(super) fn generate(
    syntax: File,
    opt: &Opt,
    gen_header: bool,
    gen_implementation: bool,
) -> Result<GeneratedCode> {
    proc_macro2::fallback::force();
    let ref mut errors = Errors::new();
    let bridge = syntax
        .modules
        .into_iter()
        .next()
        .ok_or(Error::NoBridgeMod)?;
    let ref namespace = bridge.namespace;
    let trusted = bridge.unsafety.is_some();
    let ref apis = syntax::parse_items(errors, bridge.content, trusted);
    let ref types = Types::collect(errors, apis);
    errors.propagate()?;
    check::typecheck(errors, namespace, apis, types);
    errors.propagate()?;
    // Some callers may wish to generate both header and C++
    // from the same token stream to avoid parsing twice. But others
    // only need to generate one or the other.
    Ok(GeneratedCode {
        header: if gen_header {
            write::gen(namespace, apis, types, opt, true).content()
        } else {
            Vec::new()
        },
        implementation: if gen_implementation {
            write::gen(namespace, apis, types, opt, false).content()
        } else {
            Vec::new()
        },
    })
}
<commit_msg>Handwrite Opt::default to prepare for default-true fields<commit_after>// Functionality that is shared between the cxx_build::bridge entry point and
// the cxxbridge CLI command.

mod error;
mod file;
pub(super) mod include;
pub(super) mod out;
mod write;

#[cfg(test)]
mod tests;

pub(super) use self::error::Error;
use self::error::{format_err, Result};
use self::file::File;
use crate::syntax::report::Errors;
use crate::syntax::{self, check, Types};
use std::fs;
use std::path::Path;

/// Options for C++ code generation.
///
/// We expect options to be added over time, so this is a non-exhaustive struct.
/// To instantiate one you need to crate a default value and mutate those fields
/// that you want to modify.
///
/// ```
/// # use cxx_gen::Opt;
/// #
/// let impl_annotations = r#"__attribute__((visibility("default")))"#.to_owned();
///
/// let mut opt = Opt::default();
/// opt.cxx_impl_annotations = Some(impl_annotations);
/// ```
#[non_exhaustive]
pub struct Opt {
    /// Any additional headers to #include. The cxxbridge tool does not parse or
    /// even require the given paths to exist; they simply go into the generated
    /// C++ code as #include lines.
    pub include: Vec<String>,
    /// Optional annotation for implementations of C++ function wrappers that
    /// may be exposed to Rust. You may for example need to provide
    /// `__declspec(dllexport)` or `__attribute__((visibility("default")))` if
    /// Rust code from one shared object or executable depends on these C++
    /// functions in another.
    pub cxx_impl_annotations: Option<String>,
}

/// Results of code generation.
pub struct GeneratedCode {
    /// The bytes of a C++ header file.
    pub header: Vec<u8>,
    /// The bytes of a C++ implementation file (e.g. .cc, cpp etc.)
    pub implementation: Vec<u8>,
}

impl Default for Opt {
    fn default() -> Self {
        Opt {
            include: Vec::new(),
            cxx_impl_annotations: None,
        }
    }
}

pub(super) fn do_generate_bridge(path: &Path, opt: &Opt) -> Vec<u8> {
    let header = false;
    generate_from_path(path, opt, header)
}

pub(super) fn do_generate_header(path: &Path, opt: &Opt) -> Vec<u8> {
    let header = true;
    generate_from_path(path, opt, header)
}

fn generate_from_path(path: &Path, opt: &Opt, header: bool) -> Vec<u8> {
    let source = match fs::read_to_string(path) {
        Ok(source) => source,
        Err(err) => format_err(path, "", Error::Io(err)),
    };
    match generate_from_string(&source, opt, header) {
        Ok(out) => out,
        Err(err) => format_err(path, &source, err),
    }
}

fn generate_from_string(source: &str, opt: &Opt, header: bool) -> Result<Vec<u8>> {
    let mut source = source;
    if source.starts_with("#!") && !source.starts_with("#![") {
        let shebang_end = source.find('\n').unwrap_or(source.len());
        source = &source[shebang_end..];
    }
    let syntax: File = syn::parse_str(source)?;
    let generated = generate(syntax, opt, header, !header)?;
    Ok(if header {
        generated.header
    } else {
        generated.implementation
    })
}

pub(super) fn generate(
    syntax: File,
    opt: &Opt,
    gen_header: bool,
    gen_implementation: bool,
) -> Result<GeneratedCode> {
    proc_macro2::fallback::force();
    let ref mut errors = Errors::new();
    let bridge = syntax
        .modules
        .into_iter()
        .next()
        .ok_or(Error::NoBridgeMod)?;
    let ref namespace = bridge.namespace;
    let trusted = bridge.unsafety.is_some();
    let ref apis = syntax::parse_items(errors, bridge.content, trusted);
    let ref types = Types::collect(errors, apis);
    errors.propagate()?;
    check::typecheck(errors, namespace, apis, types);
    errors.propagate()?;
    // Some callers may wish to generate both header and C++
    // from the same token stream to avoid parsing twice. But others
    // only need to generate one or the other.
    Ok(GeneratedCode {
        header: if gen_header {
            write::gen(namespace, apis, types, opt, true).content()
        } else {
            Vec::new()
        },
        implementation: if gen_implementation {
            write::gen(namespace, apis, types, opt, false).content()
        } else {
            Vec::new()
        },
    })
}
<|endoftext|>