Architecture and backend17 min

The structure of a project (3): the function nobody could test

By · founder and CEO of Hábil ·

All your logic in main.rs and your tests/ can't import it, not even with pub. What goes in lib.rs, what stays private, and a template that checks it.

The structure of a project · installment 3 of 7 · Rust

Scenario: the characters and the story are made up. The technical data and the quotes carry their source at the end.

In 1 minute

  • In Rust, a test in tests/ is another crate. It can only use what a library declares pub. If your project only has main.rs, there's nothing to import [1].
  • The way out is to split the program: the logic in src/lib.rs and a thin main.rs that only calls it [2][3].
  • In Rust everything is private until you say pub [3][5]. It's not a nuisance: it's a boundary the compiler guards, and it lets you change what's inside without breaking anyone.
  • You take away a template that clones and runs on the first try, with a script that proves that boundary by turning red when it should.
  • For Monday: look in your project for the main.rs functions that deserve a test. Those go in the library.

In this article

  1. What happened on Tuesday at 19:20?
  2. What does the Rust Book ask for?
  3. What are five frequent mistakes?
  4. Exercise: can you cross the boundary?
  5. Key takeaways
  6. What's next?
  7. The template and the course
  8. What does Hábil do?
  9. References

What happened on Tuesday at 19:20?

Iván wanted to test a function from the outside, and the compiler answered that there was nothing to import.

Tuesday, 19:20. Third floor.

Third assignment, the first in Rust. The checker again, the one from Go and Python, which queries services over HTTP. The request: "Make the report stop showing the tokens in the URLs."

The program had been written by someone who was no longer around. Everything lived in a single file:

src/main.rs      900 líneas
Cargo.toml
Cargo.lock

Iván found the function that built the report's URL, near line 600, and fixed it. He wanted a test that said "the token doesn't come out", the kind that lives in tests/. He created tests/reporte.rs and wrote, just like in the book:

use revisor::url_publica;

cargo test. The answer (trimmed):

error[E0432]: unresolved import `revisor`
  = help: if you wanted to use a crate named `revisor`, use `cargo add revisor` to add it to your `Cargo.toml`

A crate named revisor? His project was called that. He made the function pub and ran it again. The same error, word for word. And the suggestion pointed to an outside package; he wanted his own.

At 19:41 he wrote to Lucía, the tech lead.

"I made it pub and it still can't find it."

"Where's your library?" she answered.

"I don't have one. It's a program."

"That's it. A program doesn't export anything. Your tests in tests/ are another crate, and another crate can only use a library. Tomorrow we split that main.rs in two."

Iván looked at the 900 lines. The compiler wasn't telling him his code was wrong. It was telling him it had no door.

What does the Rust Book ask for?

That the logic live in a library and that main.rs only call it, because only a library can be imported from the outside.

The testing chapter of the Rust Book says it bluntly. If a project only has src/main.rs, you can't create integration tests in tests/ that import its functions, because «Only library crates expose functions that other crates can use; binary crates are meant to be run on their own» [1]: binaries are made to run on their own.

And it gives the way out in the same paragraph: a simple src/main.rs that calls the logic in src/lib.rs [1]. Chapter 12 turns it into a recipe and leaves main little to do: read the arguments, set up the configuration, call the library and handle the error [2].

Measured on a one-function project: in main.rs, private or pub, the test gives E0432; in lib.rs and pub, it passes.

This installment's template, estructura-rust, is built that way:

estructura-rust/
├── Cargo.toml · Cargo.lock
├── servicios.yaml           el ejemplo
├── src/
│   ├── main.rs              EL PROGRAMA: opciones, llamada, código de salida
│   ├── lib.rs               LA BIBLIOTECA: declara los módulos y qué es público
│   ├── servicio.rs          el vocabulario. Sin red ni archivos
│   ├── config.rs            lee y valida servicios.yaml
│   ├── revisar.rs           consulta muchos servicios a la vez
│   └── reporte.rs           tabla y JSON
├── tests/                   cada archivo es un crate aparte
│   ├── integracion.rs
│   ├── binario.rs
│   └── comun/mod.rs
├── herramientas/probar-frontera.sh
├── .github/workflows/verificar.yml
└── README.md · LICENSE · LICENSE-CONTENIDO.md

Let's go piece by piece, with the usual question: what problem does it solve today?

Why are there two roots in src/?

lib.rs is the library and main.rs is the program: two crates of the same package, and Cargo finds both without you asking.

These are Cargo's conventional paths: the library in src/lib.rs, the executable in src/main.rs and the integration tests in tests/ [6]. That's why Cargo.toml doesn't declare any of them.

The interesting part is how they talk to each other. The program is just one more user of the library, like an external crate: «It can only use the public API» [3]. main.rs has the same permissions as tests/ and as any outside project, and the book sees that as an advantage: being both author and client, you design your API better [3].

What does main.rs do, then?

The minimum a program can't delegate: read the options, make the call and decide which code it exits with.

In the template, main.rs reads three options, calls the library, prints the report and exits with 0, 1 or 2. It doesn't decide what a healthy service is. What's left is tested by running it whole: when there are integration tests, Cargo compiles the program first and leaves its path in CARGO_BIN_EXE_<nombre> [7]. That's how tests/binario.rs works. What's in main.rs can't be imported from the outside; it can be run.

What does it mean that everything is private?

That nothing is visible outside the module where it's born, except from its submodules, until you put pub on it; and the compiler enforces it.

The Rust Book says it with a list: in Rust, all items (functions, methods, structs, enums, modules and constants) «are private to parent modules by default» [3]. The language reference sums it up: «By default, everything is private», with two exceptions that don't matter here [5].

Why that way? So you know which parts you can change without breaking outside code, the book says [3]. And it calls your library's public API «your contract with users of your crate» [3]: your contract with whoever uses it.

There's a middle ground, pub(crate): visible throughout the current crate [5]. It's deceptive, because a test in tests/ is another crate (measured: it gives the same error as private).

In the template, reporte.rs offers two public functions, tabla and json. The one that cleans the URL, url_publica, is private: it's a detail of how the report is put together.

So how do you test url_publica?

From the inside: unit tests live in the same file and do see what's private.

The book's convention is a tests module at the end of each file in src/, marked with #[cfg(test)] so it's only compiled with cargo test [1]. It's a child submodule of the file, and «items in child modules can use the items in their ancestor modules» [1]: a child sees its ancestors' private items. That's why «Rust’s privacy rules do allow you to test private functions» [1]. The split in the template:

So how do you test url_publica?
WhereWhat it seesWhat it tests
At the end of servicio.rs, config.rs and reporte.rsEverything, private includedEach piece alone: url_publica, clasificar, the config errors
tests/integracion.rsOnly the library's pub itemsThe pieces together, against a local HTTP server
tests/binario.rsThe program, as a processWhat it prints and which code it exits with
The documentation of servicio.rsThe pub itemsThat the clasificar example stays true

The last one deserves a line. The example in the comment of clasificar is also a test: cargo test extracts the examples from the documentation of the library and runs them [8]. The program's aren't: measured with a deliberately false example, in main.rs nobody runs it and everything comes out green; in lib.rs, the same example turns red. Another reason for the logic to live in lib.rs.

This is what cargo test --locked looks like in the template, only the lines that matter (paths trimmed; times vary):

     Running unittests src/lib.rs (…)
test result: ok. 28 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
     Running unittests src/main.rs (…)
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
     Running tests/binario.rs (…)
test result: ok. 7 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.33s
     Running tests/integracion.rs (…)
test result: ok. 13 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.13s
   Doc-tests revisor
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

That's 49 tests in five blocks. They're counted there, in the test result lines, and not from the exit code: if a file in tests/ doesn't compile, its block doesn't even show up.

Why tests/comun/mod.rs and not tests/comun.rs?

Because each file at the root of tests/ is a suite, and a helpers file would show up as an empty suite on every run.

The two integration tests share a test HTTP server on 127.0.0.1. If it lived in tests/comun.rs, Cargo would compile it as one more suite [1], and it would show up on every run with running 0 tests (measured). The book shows that case and says it «is not what we wanted» [1]. Its way out is the template's: a subfolder, because files in subfolders of tests/ aren't compiled as separate crates [1]. Each test brings it in with mod comun;.

And the modules in src/, without mod.rs?

One file per module, named after the module. It's the path the book calls more idiomatic.

Each pub mod reporte; line in lib.rs tells the compiler to look for src/reporte.rs. The Rust Book teaches that path and acknowledges an older one, src/reporte/mod.rs, which still works [4]. Which one is better, and what happens if you mix them, is in mistake 3.

What does revisar.rs take care of that a template shouldn't teach wrong?

A time limit that covers the whole query, a cap on what's read, and errors that never repeat the URL.

The time limit covers the complete query, from the server name to the body, and at most 64 KiB of the body is read. A measured nuance: if the name server doesn't answer, the report comes out on time, but the program takes as long to finish as the system takes to give up (on Linux, about 10 s). reqwest errors, from the HTTP library, «may include the full URL», its documentation warns [12]; the template never uses their text. Nor that of the YAML errors, which repeat the value (measured): it states the service, the field and, if the YAML can't be read, the line and the column. We removed each protection in a copy, one by one: all ten turned red. And the edges (the 400 code, --paralelo 0, 1 and 60,000 ms) have their test, which turns red if the edge moves.

And where does Iván's fix go?

In the library, in reporte.rs, next to whoever uses it; and its test, at the end of the same file.

On Wednesday, Iván and Lucía split the main.rs. The URL function ended up in reporte.rs, private, with its unit test beside it. The test Iván wanted, "the token doesn't come out", ended up in tests/, but it uses the public function json. From the outside you test what's promised; from the inside, how it's kept.

How do I know the boundary exists?

Because a script crosses it on purpose on every change and demands that the compiler reject it for the exact reason.

herramientas/probar-frontera.sh works on a temporary copy. It plants tests/frontera.rs, a test that calls revisor::reporte::url_publica, and compiles it twice on the same copy. Between the two, only one word of src/reporte.rs changes, pub:

  1. Control, with pub fn url_publica. The test has to compile and pass. If not, the experiment is badly set up (the function changed its name, the planted test has an error) and the script stops in red.
  2. Violation, with src/reporte.rs as it is. The same test has to fail to compile, with the E0603 error, in the test and not in the library.

This is the real output:

1/2 control: con «pub fn url_publica», una prueba de tests/ la llama
   ✅ compiló y pasó (1 de 1): la prueba sembrada está bien escrita
2/2 violación: la misma prueba, con src/reporte.rs tal como está
     error[E0603]: function `url_publica` is private
        --> tests/frontera.rs:6:27
   ✅ el compilador la rechazó: lo que no es pub no se ve desde tests/
✅ frontera: se sostiene

Why the control? Because an experiment that only expects a red also turns "green" when it fails for the wrong reason. We broke it in four ways: with the function renamed or the planted test badly written, it stops at «el control NO pasó»; with a different expected error code, it says it failed «pero NO por la privacidad»; with the function pub, it says ❌ COMPILÓ. With pub(crate), it holds.

The GitHub CI is written to run formatting, clippy, the tests and the boundary with stable Rust and with 1.85.0, and the boundary also on macOS with the system /bin/bash, on every change and every Monday. The template is already published on GitHub. If it goes 60 days without activity, GitHub turns off the Monday run [20].

What are five frequent mistakes?

The first two come from not knowing what tests/ sees; the third, from mixing styles; the last two, from opening up too much: the API or the project.

1. Everything in main.rs, without lib.rs

It works, but from tests/ nothing can be imported, not even with pub.

It's Iván's mistake [1]. Chapter 12 gives the underlying reason: since you can't test the main function directly, taking the logic out of it lets you test all of it, and what's left is checked by reading it [2]. It's given away by E0432, with a cargo add suggestion that points elsewhere (measured). You don't need another package: you need your library.

2. Test helpers in tests/common.rs

Cargo compiles it as one more suite, and it shows up empty on every run.

The book shows it: tests/common.rs shows up with running 0 tests, even though it has no tests [1]. It doesn't break anything, but it clutters the count. The way out is the subfolder, tests/common/mod.rs [1].

3. Mixing mod.rs with files named after the module

For the same module, the compiler stops; for different modules, it compiles, but it's confusing.

The Rust Book distinguishes two cases. If you use both styles for the same module, «you’ll get a compiler error» [4]. Measured in the template, with src/reporte.rs and src/reporte/mod.rs at once:

error[E0761]: file for module `reporte` found at both "src/reporte.rs" and "src/reporte/mod.rs"

In different modules it compiles, but it «might be confusing for people navigating your project» [4], and with mod.rs you end up with many files with the same name open in the editor [4]. Pick one style; the template uses the one the book calls more idiomatic.

4. Making everything pub because the compiler complains

Every pub enlarges what others can use, and the compiler stops warning you if it's unneeded.

Faced with an E0603, first ask who needs that function. If it's a test, it goes at the end of the file, as a unit test [1]. If it's another module of the same crate, pub(crate) is enough [5].

If you publish the library or another package uses it, the cost is written in Cargo's compatibility guide, which deals with new versions of a package: «Major: renaming/moving/removing any public items» [11]. The template isn't published (publish = false): its pub items are only used by main.rs and tests/, which get fixed in the same change. Even so, there's a measured cost: pub turns off the dead code warning. With pub(crate) and unused, the compiler says `warning: function url_publica is never used; with pub, it goes quiet. That's why the template's boundary turns red if url_publica becomes pub`.

5. Not versioning Cargo.lock when your CI needs it, or setting up a workspace too early

The first leaves you without the record of what it was tested with; the second, with a structure that has no purpose yet.

The Cargo FAQ says that Cargo.lock records the state of a build that worked, so it can be repeated the same way at another time and on another machine, and that this way CI only fails because of new changes and not because of external factors [9]. cargo new versions it by default; the FAQ clarifies that it depends on what your package needs [9]. Among its uses it lists one that the template does with its 1.85.0 CI row: «Verifying a minimum-supported Rust version (MSRV) that is less than the latest version of a dependency supports» [9]. And it warns about the limit: that determinism «can give a false sense of security», because Cargo.lock doesn't affect whoever uses your package [9]. The template runs with --locked, which fails if the file is missing or if cargo would want to change it [8]. Measured, without Cargo.lock:

error: cannot create the lock file …/Cargo.lock because --locked was passed to prevent this

The other extreme is a workspace on day one: a set of packages that share Cargo.lock and a build folder [10], which the Rust Book presents for when the project has already grown and you want to split it into several crates [10]. With one package, there's nothing to share.

5. Not versioning Cargo.lock when your CI needs it, or setting up a workspace too early
MistakeWhat gives it awaySource
1. Everything in main.rsE0432: unresolved import from tests/[1][2]
2. Helpers in tests/common.rsA suite with running 0 tests[1]
3. mod.rs and a named fileE0761: file for module … found at both[4]
4. pub on everythingNo never used warning; if you publish, every removal is a major change[3][11]
5. No Cargo.lock with a CI that needs it, or a workspace with a single packagecannot create the lock file with --locked; a structure nothing uses[8][9][10]

Exercise: can you cross the boundary?

You're going to try to use a private function from tests/, see how much you have to open up to manage it, and see how the script warns you that you opened up too much.

You need Rust 1.85 or newer (cargo --version), installed with rustup, plus Git and bash; about 20 minutes, plus a couple of minutes the first time, while the dependencies compile.

  • On Linux Mint 22, don't use apt install cargo: Mint 22 is based on Ubuntu 24.04 [16], which ships Rust 1.75 [15], and the 2024 edition arrived with 1.85 [18]. Measured with that cargo: `feature edition2024 is required, plus a suggestion to add cargo-features to Cargo.toml. Don't follow it: update Rust. If you already installed it with apt, remove it with sudo apt remove cargo rustc; otherwise, the rustup installer warns you that there's already a Rust in /usr/bin (measured). Then run sudo apt install build-essential curl and the rustup` command [13][14]:
  curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

Without build-essential, you get `error: linker cc not found` (measured). Lesson 0 of the Rust course walks you through it step by step. - On macOS, xcode-select --install and then the same rustup command [13]. - If you already have rustup, rustup update stable. On Windows, use WSL. - When you're done, open another terminal and check that cargo --version says 1.85 or more: in the same terminal, cargo still isn't found or is still the old one (measured).

Step 1. Clone and check that everything is green.

git clone https://github.com/HabilMX/estructura-rust
cd estructura-rust
cargo test --locked
bash herramientas/probar-frontera.sh

What you should see: five test result: ok. lines that add up to 49 tests and, at the end of the script, ✅ frontera: se sostiene.

Step 2. Predict before you touch. You're going to write a test in tests/ that calls url_publica, which is private. Write down three things: does it compile as is? And if you make it pub(crate)? And if you make it pub?

Step 3. Try to cross.

cat > tests/mia.rs <<'EOF'
#[test]
fn quita_el_token() {
    assert_eq!(
        revisor::reporte::url_publica("https://pagos.ejemplo.com/salud?token=x"),
        "https://pagos.ejemplo.com/salud"
    );
}
EOF
cargo test --locked --test mia

It doesn't compile. The first line of the error:

error[E0603]: function `url_publica` is private

Step 4. Open it up to the crate. Copy only the sed line for your system: the macOS one needs an empty argument after -i (if the sed on your Mac is GNU's, use the Linux one).

# En Linux:
sed -i 's/^fn url_publica(/pub(crate) fn url_publica(/' src/reporte.rs

# En macOS:
sed -i '' 's/^fn url_publica(/pub(crate) fn url_publica(/' src/reporte.rs

cargo test --locked --test mia

The same E0603. pub(crate) opens the function to the whole library crate, and your test is another crate.

Step 5. Open it all the way.

# En Linux:
sed -i 's/^pub(crate) fn url_publica(/pub fn url_publica(/' src/reporte.rs

# En macOS:
sed -i '' 's/^pub(crate) fn url_publica(/pub fn url_publica(/' src/reporte.rs

cargo test --locked --test mia
bash herramientas/probar-frontera.sh

Your test passes (test result: ok. 1 passed). But the script turns red:

❌ COMPILÓ: una prueba de tests/ pudo llamar a revisor::reporte::url_publica. La frontera no existe.

You managed to use it from the outside, and with that you put it into your contract. That's the control at work.

Step 6. Put everything back.

git checkout -- . && git clean -fd
bash herramientas/probar-frontera.sh

Watch out: git clean -fd also deletes any new files you created there, like your notes.

How you know you did it right: you ran seven times and saw three outcomes. Green, four times: the two in step 1, your test in step 5 and the boundary in step 6. Red E0603, twice: steps 3 and 4. And the script's red «COMPILÓ» in step 5. If in step 4 your test compiles, check that you didn't write pub instead of pub(crate). If in step 5 the E0603 is still there, the sed line didn't find pub(crate) fn url_publica(: check step 4.

To go further (optional): move tests/comun/mod.rs to tests/comun.rs (delete the empty folder) and run cargo test --locked. Before that, predict how many test result blocks you'll see. Answer: six, one more than before, Running tests/comun.rs with running 0 tests (measured). Go back with the same git checkout -- . && git clean -fd.

Key takeaways

Three questions to ask your repository this week.

  1. Does your project have src/lib.rs? If not, which function in main.rs would you like to test from the outside and can't today?
  2. Of what's pub in your library, what does another crate really use? Could what nobody uses from the outside be private or pub(crate)?
  3. Does your CI run with --locked? If not, with which versions of your dependencies was the last green tested?

What's next?

In Go, the boundary is guarded by the tool; in Python, by the installation; in Rust, by the compiler. In TypeScript there's one the editor accepts and Node never sees.

A week later, Iván inherited an API in TypeScript. An import with a pretty alias, @/revisar, compiled without a complaint. The editor autocompleted it.

Node, on startup, didn't find it.

How can a program that doesn't start compile? On Sunday, November 22, in installment 4: what tsconfig.json decides, what it doesn't, and why the tests and the code are compiled separately.

The template and the course

  • Template: github.com/HabilMX/estructura-rust. Use it as a GitHub template or clone it [19]. The README explains each folder, what it doesn't include, when to add each thing and how to remove the example. The code is MIT; the text and explanations are CC BY-SA 4.0.
  • Versions: 2024 edition and rust-version = "1.85", the first with that edition [18]. It was tested with Rust 1.99.1, the stable release on October 22 [17], on macOS arm64 (also with the system /bin/bash) and on Linux arm64 (ubuntu:24.04 container), and with 1.85.0 on macOS arm64. Linux x86-64 hasn't been tested yet. The CI is written for each week's stable and 1.85.0 on Linux, and stable on macOS [21]. The dependencies are pinned in Cargo.lock.
  • Hábil's Rust course: if modules, pub or integration tests are new to you, lesson 6 covers them from scratch: Rust course · Lesson 6, Modules, Tests, and Cargo.

I write from what we see every day at Hábil, where we build systems for banking, insurance and companies in Mexico. If at work or at school you run into a repository nobody knows where to touch, let's talk: habil.mx

What does Hábil do?

We teach programming with the same things we use to build: tested, explained and open code.

This series comes from the same place as our courses. The Rust course is the second one, after Go: it teaches who owns each piece of data and which errors the compiler catches before running, and we publish it openly, under a Creative Commons license; its lesson 6 covers the modules, tests and Cargo that this article's template uses. And the catalog has more programming courses, free and with code tested and explained step by step.

See the Rust course and the Programming courses catalog.

If your team inherits a system nobody knows where to touch, or needs to build one that can keep being maintained, we do that too: see Build your custom system. Request your first conversation, at no cost. In it we review whether it makes sense to configure what you already have, use a product or build, and what you would get in each case. Write to us on WhatsApp.

Sources accessed on October 22, 2026. Quotes in English are reproduced as is.

References

  1. Steve Klabnik, Carol Nichols and Chris Krycho, «Test Organization» (11.3), The Rust Programming Language. https://doc.rust-lang.org/book/ch11-03-test-organization.html
  2. Steve Klabnik, Carol Nichols and Chris Krycho, «Refactoring to Improve Modularity and Error Handling» (12.3): «Separation of Concerns for Binary Projects», The Rust Programming Language. https://doc.rust-lang.org/book/ch12-03-improving-error-handling-and-modularity.html
  3. Steve Klabnik, Carol Nichols and Chris Krycho, «Paths for Referring to an Item in the Module Tree» (7.3), including «Best Practices for Packages with a Binary and a Library», The Rust Programming Language. https://doc.rust-lang.org/book/ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html
  4. Steve Klabnik, Carol Nichols and Chris Krycho, «Separating Modules into Different Files» (7.5): «Alternate File Paths», The Rust Programming Language. https://doc.rust-lang.org/book/ch07-05-separating-modules-into-different-files.html
  5. The Rust Reference, «Visibility and privacy». https://doc.rust-lang.org/reference/visibility-and-privacy.html
  6. The Cargo Book, «Package Layout». https://doc.rust-lang.org/cargo/guide/project-layout.html
  7. The Cargo Book, «Cargo Targets»: «Binaries» and «Integration tests». https://doc.rust-lang.org/cargo/reference/cargo-targets.html
  8. The Cargo Book, «cargo test»: «Documentation tests» and --locked. https://doc.rust-lang.org/cargo/commands/cargo-test.html
  9. The Cargo Book, «Frequently Asked Questions»: «Why have Cargo.lock in version control?». https://doc.rust-lang.org/cargo/faq.html
  10. Steve Klabnik, Carol Nichols and Chris Krycho, «Cargo Workspaces» (14.3), The Rust Programming Language. https://doc.rust-lang.org/book/ch14-03-cargo-workspaces.html
  11. The Cargo Book, «SemVer Compatibility». https://doc.rust-lang.org/cargo/reference/semver.html
  12. reqwest 0.13.5, «Struct Error», docs.rs. https://docs.rs/reqwest/0.13.5/reqwest/struct.Error.html
  13. Steve Klabnik, Carol Nichols and Chris Krycho, «Installation» (1.1), The Rust Programming Language. https://doc.rust-lang.org/book/ch01-01-installation.html
  14. The Rust Project, «Install Rust», rust-lang.org. https://www.rust-lang.org/tools/install
  15. Launchpad (Canonical), rustc package published in Ubuntu 24.04 «noble»: 1.75.0. https://api.launchpad.net/1.0/ubuntu/+archive/primary?ws.op=getPublishedSources&source_name=rustc&exact_match=true&distro_series=https://api.launchpad.net/1.0/ubuntu/noble
  16. Linux Mint, «Linux Mint 22 Release Notes» («Linux Mint 22.x is based on Ubuntu 24.04»), linuxmint.com. https://www.linuxmint.com/rel_wilma.php
  17. The Rust Release Team, «Announcing Rust 1.99.1», Rust Blog, October 15, 2026. https://blog.rust-lang.org/2026/10/15/Rust-1.99.1/
  18. The Rust Release Team, «Announcing Rust 1.85.0 and Rust 2024», Rust Blog, February 20, 2025. https://blog.rust-lang.org/2025/02/20/Rust-1.85.0/
  19. GitHub Docs, «Creating a template repository». https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-template-repository
  20. GitHub Docs, «Disabling and enabling a workflow». https://docs.github.com/en/actions/how-tos/manage-workflow-runs/disable-and-enable-workflows
  21. David Tolnay, «dtolnay/rust-toolchain», README, GitHub. https://github.com/dtolnay/rust-toolchain

Does your operation face these challenges?

Let's talk about your case

Prefer email? Write to us at hola@habil.mx