-
-
Notifications
You must be signed in to change notification settings - Fork 89
Coding
The Effects
type is used to manage printing output and displaying progress to the user. It can be used to show the progress of nested tasks, including with spinners or progress meters.
It's not safe to call println!
or eprintln!
directly, because the output may clobber or be clobbered by progress reporting mechanisms. Instead, call one of the following, as appropriate:
writeln!(effects.get_output_stream(), "message {}", param1, ...);
writeln!(effects.get_error_stream(), "message {}", param1, ...);
as appropriate.
For logging, the project uses the tracing
library. You can generate logs using macros like tracing::warn!
. These macros support structured logging, so you can directly include variables which you want to log, without having to convert them to strings first:
// Includes the contents of `buffer` in the log message.
warn!(?buffer, "WriteProgress dropped while buffer was not empty");
To display logs at runtime, set the RUST_LOG
environment variable. For example:
$ RUST_LOG=info cargo run smartlog
Most of the project uses the eyre
library for error-handling. The eyre::Result
type allows you to use the ?
operator with nearly any error type, rather than having to wrap every third-party library Result
into a project- or function-specific Result
type.
Stack traces are displayed with the help of the color-eyre
and tracing-error
libraries. Technically, span traces are recorded, not stack traces. These correspond to functions which have been explicitly annotated using the tracing
library, such as with #[tracing::instrument]
. Other function calls are not included in the span trace.
When the program exits with error, a span trace will appear:
$ cargo run smartlog
Compiling git-branchless v0.3.4 (/Users/wkhan/workspace/git-branchless)
Finished dev [unoptimized + debuginfo] target(s) in 5.74s
Running `target/debug/git-branchless smartlog`
Error:
0: Could not find repository main branch
Location:
src/git/repo.rs:302
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ SPANTRACE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
0: branchless::commands::smartlog::smartlog with effects=<Output fancy=true>
at src/commands/smartlog.rs:269
The span trace includes branchless::commands::smartlog::smartlog
because it is wrapped with #[tracing::instrument]
. Also notice that it includes the parameter value effects
, along with its Debug
representation.
When you want a function to appear in the span trace, you must do one of two things:
- Wrap the function definition with
#[tracing::instrument]
. This is best for first-party code:- Only the definition needs to be wrapped with
#[instrument]
. Callers don't have to remember to callwrap_err_with
every time they call the function. - Parameter values are included in the span trace as well.
- Only the definition needs to be wrapped with
- Call
wrap_err_with
on aResult
to turn it into aneyre::Result
. This is best for third-party libraries which don't useeyre::Result
, or when additional information needs to be added to an operation.
By default, #[tracing::instrument]
-annotated functions will include all of their parameters in the span trace by getting their Debug
representations, which is helpful for debugging.
However, parameters should not be included when the Debug
representation is very large. A parameter whose type doesn't implement Debug
can't be included at all.
To skip printing a certain parameter in the span trace: add a skip
to tracing::instrument
:
#[instrument(skip(value))]
fn set_config(
effects: &Effects,
config: &mut Config,
name: &str,
value: impl Into<ConfigValue>,
) -> eyre::Result<()> {
The project also uses the similar anyhow
library in a few cases:
- When an error needs to be sent between threads. The
eyre::ErrReport
type is notSend
, so we use theanyhow::Error
type instead. - When interfacing with another library that uses
anyhow::Error
.
When specific error values need to be consumed by the rest of the program, rather than reported to the user, the project uses the standard library Result
instead (or in addition). If necessary, the thiserror
library could also be used to compose these kinds of errors.
To keep compile times down, heed the advice in Fast Rust Builds. In particular, you'll see some examples of reducing monomorphization in the codebase:
-
eyre::ErrContext::wrap_err
is called in preference toeyre::ErrContext::wrap_err_with
. The latter takes a function as its argument, which is slower to process by the compiler, and slightly increases code size.- If the containing function is marked with
#[instrument]
, then its parameter values will be printed in the span-trace, so they don't need to be duplicated in thewrap_err
/wrap_err_with
message. Thus,wrap_err_with
is only useful when there are other values which should be included in the error message, such as local variables.
- If the containing function is marked with
- Functions which take
Into<Foo>
orAsRef<Foo>
immediately defer to a function which is specialized to takeFoo
/&Foo
.- This also makes it easier to include the parameter values with
#[instrument]
, asInto<Foo>
/AsRef<Foo>
won't beDebug
, butFoo
often is.
- This also makes it easier to include the parameter values with
The project wraps Git operations into its own git
module, rather than use the types from e.g. git2
directly. This is for a few reasons:
- To be able to swap out the Git implementation.
- To improve API correctness.
- The
Oid
type is split intoMaybeZeroOid
andNonZeroOid
. - The
git2
library returns agit2::Error
for cases such as an object not being found. Our wrapper detects those cases and instead returns anOption
.
- The
- To improve API performance.
- The
git2
library works withIndex
es, which scale with the size of the repository, rather than the size of the change to the repository. This makes some operations very slow, such as cherry-picking changes. The project offers alternative implementations which don't useIndex
es.
- The
Types specific to git2
, etc., should not escape the git
module. Instead, our own wrapper types should be exposed.
- Search the Wiki 🔎
- User guide
- Welcome
- Installation
- Tutorial
- Command overview
- General:
- Navigation:
- Committing:
- Rebasing:
- Verification:
- Collaboration:
- Workflows
- Advanced topics
- Reference
- Developer guide
- Onboarding offer
- Development
- Reference