constructor-boilerplate

One tree of constructors is built by hand at many sites.

Group
Opportunities
Runs
Runs under ur report: ur report --rule constructor-boilerplate
Level
note
Reads
Every file
Fix
None: the smart constructor's name and where it lives are a design choice

Why

These sites build the same nest of constructors, such as a struct literal or a call of a type's or variant's path around others, and differ only in the arguments they pass, the holes the finding shows. A literal every site writes the same stays in the shape. A smart constructor that takes the holes as arguments would build the tree once. It reads every language whose grammar states its shapes in a Shapes.rsc; an advisory rule that ur report runs.

The fix, as Ur suggests it: write a smart constructor that takes the holes as arguments, and call it at each site.

For agents

Before writing out a nest of constructors again, look for a function that builds it and call that, or write one.

Example

src/errors.rs
use crate::http::{Body, CachePolicy, Response, Status};

pub fn not_found(path: &str) -> Response {
    Response {
        status: Status::NotFound,
        body: Body::Text(format!("no page at {path}")),
        cache: CachePolicy::NoStore,
    }
}

pub fn unauthorized(realm: &str) -> Response {
    Response {
        status: Status::Unauthorized,
        body: Body::Text(format!("sign in to {realm} to continue")),
        cache: CachePolicy::NoStore,
    }
}

pub fn too_large(limit: usize) -> Response {
    Response {
        status: Status::PayloadTooLarge,
        body: Body::Text(format!("uploads are limited to {limit} bytes")),
        cache: CachePolicy::NoStore,
    }
}

pub fn unavailable(seconds: u64) -> Response {
    Response {
        status: Status::ServiceUnavailable,
        body: Body::Text(format!("try again in {seconds} seconds")),
        cache: CachePolicy::NoStore,
    }
}
What Ur prints
$ ur report --rule constructor-boilerplate
opportunity[constructor-boilerplate]: 4 sites build `Response { status: $, body: Body::Text(format!($)), cache: CachePolicy::NoStore }` with only its holes different (saves about 12 lines)
  --> ./src/errors.rs:4:5-8 builds `Response { status: $, body: Body::Text(format!($)), cache: CachePolicy::NoStore }`
  --> ./src/errors.rs:12:5-16 builds `Response { status: $, body: Body::Text(format!($)), cache: CachePolicy::NoStore }`
  --> ./src/errors.rs:20:5-24 builds `Response { status: $, body: Body::Text(format!($)), cache: CachePolicy::NoStore }`
  --> ./src/errors.rs:28:5-32 builds `Response { status: $, body: Body::Text(format!($)), cache: CachePolicy::NoStore }`
  = hint: write a smart constructor that takes the holes as arguments, and call it at each site
1 opportunity saving about 12 lines in all

Try it in the playground →