Inline Code Documentation

This chapter lays out the standards of practice for inline Rust documentation for generating Rust docs. It also provides templates that should be followed when creating documentation for these items. You can review the Templates and Quick Reference, however if this is your first time seeing this document, please read it in its entirety.

The most important items to document are those marked with the pub keyword, as they will have automatic documentation generated for them. When adding new code, the developer should always run cargo doc --open and review the documentation for their code.

Common Sections

All sections are described as ## <section_name> inside inline doc comments. These are a common set of sections used below, however do not hesitate to create a custom section if it is appropriate.

Examples

The examples section is used to provide example usage to a user using the inline code markdown functionality e.g. ```. The great thing about writing examples is that cargo test will run these examples and fail if they are incorrect. This ensures your examples are always up to date!

There are situations where you may expect the example to not compile, fail, panic, etc. To support this, you can pass attributes to the inline code examples, to tell Rust what to expect. Some supported attributes are should_panic, no_run, compile_fail, and ignore.

Including #[doc(html_playground_url = "https://playground.example.com/")] will allow examples to be runnable in the documentation.

#![allow(unused)]
fn main() {
/// ## Examples
///
/// optional description
///
/// ``` <attribute>
/// <code></code>
/// ```
}

Errors

The errors section documents the expected error values when the output of a function is a Result. This section should be an exhaustive list of expected errors, but not an exhaustive list of the error enum values (unless all are possible). You should always contain the error type as a linked reference and the reason why the error would be returned.

#![allow(unused)]
fn main() {
/// ## Errors
///
/// Returns [ErrorName1](crate::module::ErrorEnum::Error1) when <this> happens
/// Returns [ErrorName2](crate::module::ErrorEnum::Error2) when <this> happens
///
}

Safety

The safety section must be provided for any function that is marked as unsafe and is used to document (1) what makes this function unsafe and (2) the expected scenario in which this function will operate safely and as expected. A safety section should also be bubbled up to the struct (if applicable) and the module if any function is unsafe.

It is common (but not required) to see pre-condition checks in the function that validates these conditions, and panic if they fail. One common example is slice::from_raw_parts which will panic with the statement:

unsafe precondition(s) violated: slice::from_raw_parts requires the pointer
to be aligned and non-null, and the total size of the slice not to exceed `isize::MAX`
#![allow(unused)]
fn main() {
/// ## Safety
///
/// <comments>
}

Panics

Provide general description and comments on any functions that use .unwrap(), debug_assert!, etc. that would result in a panic. Typically only used when describing functions.

Lifetimes

Provide a general description and comments on any types that have lifetimes more complex than a single lifetime (explicit or implicit). Assume that the developer understands lifetimes; focus on why the lifetime was modeled a certain way rather than describing why it was needed to make the compiler happy! Typically only used when describing types.

---
config:
  layout: elk
  look: handDrawn
---
graph TD
    A[Complex Lifetimes] --> B[Multiple Parameters]
    A --> C[Unusual Relationships]
    A --> D[Design Decisions]
    B --> E[**Document Why**]
    C --> E
    D --> E

Style Guides

The goal is to create documentation that provides developers with a clear and concise description on how to use a crate, module, type, or function while keeping it clean when auto-generating documentation with cargo doc. As alluded to, it is the responsibility of the developer to ensure that each library crate, public module, public type, and public function is well documented. Below are the expectations for each. If a common section is not applicable to the documented item, do not include it.

Crate Style Guide

Crate documentation should be located at the top of the lib.rs or main.rs file. The intent is to describe the purpose of the crate, providing any setup instructions and examples. This is also the place to describe any common misconceptions or "gotchas". Doc comments here use //! specifying we are documenting the parent item (the crate).

#![allow(unused)]
fn main() {
//! PE32 Management
//!
//! This library provides high-level functionality for operating on and representing PE32 images.
//!
//! ## Examples and Usage
//!
//! ```
//! let file: File = File::open(test_collateral!("test_image.pe32"))
//!   .expect("failed to open test file.");
//!
//! let mut buffer: Vec<u8> = Vec::new();
//! file.read_to_end(&mut buffer).expect("Failed to read test file");
//!
//! let image_info: Pe32ImageInfo = pe32_get_image_info(buffer).unwrap();
//!
//! let mut loaded_image: Vec<u8> = vec![0; image_info.size_of_image as usize];
//! pe32_load_image(&image, &mut loaded_image).unwrap();
//! ```
//!
//! ## License
//!
//! Copyright (C) Microsoft Corporation. All rights reserved.
//!
//! SPDX-License-Identifier: BSD-2-Clause-Patent
//!
}

Module Style Guide

Module documentation should be placed at the top of a module, whether that be a mod.rs file or the module itself if contained to a single file. If a crate only consists of a single module, the crate style guide should be used. Submodules should be avoided if possible, as they cause confusion. The goal is to describe the types found in this module and their interactions with the rest of the crate. Doc comments here use //! specifying we are documenting the parent item (the module).

#![allow(unused)]
fn main() {
//! PE32 Management
//!
//! This module provides high-level functionality for operating on and representing PE32 images.
//!
//! ## License
//!
//! Copyright (C) Microsoft Corporation. All rights reserved.
//!
//! SPDX-License-Identifier: BSD-2-Clause-Patent
//!
}

Type Style Guide

Type documentation should be available for all public types such as enums, structs, etc. The focus should be on the construction of the type (when / how), destruction of the type if a custom Drop trait is implemented, and any performance concerns. Doc comments here use /// specifying we are documenting the item directly below it (the type or member of the type).

Document traits, not trait implementations!

#![allow(unused)]
fn main() {
/// Type for describing errors that result from working with PE32 images.
#[derive(Debug)]
pub enum Pe32Error {
    /// Goblin failed to parse the PE32 image.
    ///
    /// See the enclosed goblin error for a reason why the parsing failed.
    ParseError(goblin::error::Error),
    /// The parsed PE32 image does not contain an Optional Header.
    NoOptionalHeader,
    /// Failed to load the PE32 image into the provided memory buffer.
    LoadError,
    /// Failed to relocate the loaded image to the destination.
    RelocationError,
}

/// Type containing information about a PE32 image.
#[derive(PartialEq, Debug)]
pub struct Pe32ImageInfo {
    /// The offset of the entry point relative to the start address of the PE32 image.
    pub entry_point_offset: usize,
    /// The subsystem type (IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER [0xB], etc.).
    pub image_type: u16,
    /// The total length of the image.
    pub size_of_image: u32,
    /// The size of an individual section in a power of 2 (4K [0x1000], etc.).
    pub section_alignment: u32,
    /// The ascii string representation of a file (<filename>.efi).
    pub filename: Option<String>,
}
}

Function Style Guide

Function documentation should be available for functions of a public type (associated functions), and any public functions. At least one example is required for each function in addition to the other sections mentioned below.

Do not provide an arguments section, the name and type of the argument should make it self-evident.

Do not provide a Returns section, this should be captured in the longer description and the return type makes the possible return value self-evident.

#![allow(unused)]

fn main() {
/// Attempts to parse a PE32 image and return information about the image.
///
/// Parses the bytes buffer containing a PE32 image and generates a [Pe32ImageInfo] struct
/// containing general information about the image otherwise an error.
///
/// ## Errors
///
/// Returns [`ParseError`](Pe32Error::ParseError) if parsing the PE32 image failed. Contains the
/// exact parsing [`Error`](goblin::error::Error).
///
/// Returns [`NoOptionalHeader`](Pe32Error::NoOptionalHeader) if the parsed PE32 image does not
/// contain the OptionalHeader necessary to provide information about the image.
///
/// ## Examples
///
/// ```
/// extern crate std;
///
/// use std::{fs::File, io::Read};
/// use uefi_pe32_lib::pe32_get_image_info;
///
/// let mut file: File = File::open(concat!(env!("CARGO_MANIFEST_DIR"), "/resources/test/","test_image.pe32"))
///   .expect("failed to open test file.");
///
/// let mut buffer: Vec<u8> = Vec::new();
/// file.read_to_end(&mut buffer).expect("Failed to read test file");
///
/// let image_info = pe32_get_image_info(&buffer).unwrap();
/// ```
///
pub fn pe32_get_image_info(image: &[u8]) -> Result<Pe32ImageInfo, Pe32Error> {
  ...
}
}