diff --git a/.codespellrc b/.codespellrc new file mode 100644 index 0000000..604a0d7 --- /dev/null +++ b/.codespellrc @@ -0,0 +1,5 @@ +[codespell] +skip = .git,target,node_modules,dist,*.yaml,*.lock,./book/book,*.min.js +ignore-words-list = implementor,implementors,ser,crate +count = +quiet-level = 3 diff --git a/.github/workflows/book.yml b/.github/workflows/book.yml new file mode 100644 index 0000000..e881858 --- /dev/null +++ b/.github/workflows/book.yml @@ -0,0 +1,29 @@ +name: publish-tutorial + +on: + push: + branches: + - main + +jobs: + tutorial: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - uses: DeterminateSystems/nix-installer-action@main + - uses: DeterminateSystems/magic-nix-cache-action@main + - uses: DeterminateSystems/flake-checker-action@main + + - name: Build Tutorial + run: | + nix develop --command mdbook-admonish install book + nix develop --command mdbook-mermaid install book + nix develop --command mdbook build book + + - uses: JamesIves/github-pages-deploy-action@v4.3.0 + with: + token: ${{ secrets.GITHUB_TOKEN }} + branch: gh-pages + folder: ./book/book + clean: true diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bb914e1..891417a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,12 +14,11 @@ jobs: test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - - name: Setup - uses: ./.github/workflows/setup + - uses: DeterminateSystems/nix-installer-action@main + - uses: DeterminateSystems/magic-nix-cache-action@main + - uses: DeterminateSystems/flake-checker-action@main - - name: Argus tests - uses: actions-rs/cargo@v1 - with: - command: test + - name: Argus Test + run: nix develop --command ci-check diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e440ffd..ad4199e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,37 +10,31 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - uses: actions-rs/install@v0.1 - with: - crate: cargo-workspaces - version: latest - use-tool-cache: true - - name: Setup - uses: ./.github/workflows/setup - - run: cargo ws publish skip --no-remove-dev-deps --from-git --yes --token ${{ secrets.CRATES_IO }} + - uses: DeterminateSystems/nix-installer-action@main + - uses: DeterminateSystems/magic-nix-cache-action@main + - uses: DeterminateSystems/flake-checker-action@main + - run: nix develop --command \ + cargo ws publish skip --no-remove-dev-deps --from-git --yes --token ${{ secrets.CRATES_IO }} publish-ide: runs-on: ubuntu-latest needs: publish-crates steps: - uses: actions/checkout@v3 - - uses: actions/setup-node@v4 - with: - node-version: 20 - - name: Setup - uses: ./.github/workflows/setup + - uses: DeterminateSystems/nix-installer-action@main + - uses: DeterminateSystems/magic-nix-cache-action@main + - uses: DeterminateSystems/flake-checker-action@main - - name: Publish to Open VSX Registry - uses: HaaLeo/publish-vscode-extension@v1 - id: publishToOpenVSX - with: - pat: ${{ secrets.OVSX_MRKT }} - packagePath: "./ide/packages/extension" + - name: Build TS bindings + run: nix develop --command cargo make init-bindings - - name: Publish to Visual Studio Marketplace - uses: HaaLeo/publish-vscode-extension@v1 - with: - pat: ${{ secrets.VSCODE_MRKT }} - registryUrl: https://marketplace.visualstudio.com - packagePath: "./ide/packages/extension" + - name: Depot Setup + run: nix develop --command depot setup + + - name: Publish Extension + run: | + cd ide/packages/extension + nix develop --command vsce package + nix develop --command vsce publish -p ${{ secrets.VSCODE_MRKT }} --packagePath argus-*.vsix + nix develop --command pnpx ovsx publish argus-*.vsix -p ${{ secrets.OVSX_MRKT }} diff --git a/.github/workflows/setup/action.yml b/.github/workflows/setup/action.yml deleted file mode 100644 index f1d953b..0000000 --- a/.github/workflows/setup/action.yml +++ /dev/null @@ -1,33 +0,0 @@ -name: setup -runs: - using: composite - steps: - - uses: actions/checkout@v3 - - - uses: davidB/rust-cargo-make@v1 - with: - version: '0.36.4' - - - name: Install Guile - run: sudo apt-get install -y guile-3.0 - shell: bash - - - name: Install Depot - run: curl https://raw.githubusercontent.com/cognitive-engineering-lab/depot/main/scripts/install.sh | sh - shell: bash - - - name: Install Test Libraries - run: sudo apt-get install -y libasound2-dev libudev-dev - shell: bash - - - name: Gen bindings - run: cargo make init-bindings - shell: bash - - - name: Prepare IDE - run: cd ide && depot build - shell: bash - - - name: Install Argus - run: cargo install --path crates/argus-cli --debug --locked - shell: bash diff --git a/.gitignore b/.gitignore index dced3e1..2edd08e 100644 --- a/.gitignore +++ b/.gitignore @@ -17,7 +17,6 @@ gha-creds-*.json /org/meeting* /org/*.png /org/media/ -sample-book/ # Rust things target/ diff --git a/book/.gitignore b/book/.gitignore index 7585238..f2c4c50 100644 --- a/book/.gitignore +++ b/book/.gitignore @@ -1 +1,4 @@ book +mdbook-admonish.css +mermaid-init.js +mermaid.min.js diff --git a/book/book.toml b/book/book.toml index 8f8b435..e765ce2 100644 --- a/book/book.toml +++ b/book/book.toml @@ -6,4 +6,20 @@ src = "src" title = "Argus: a trait debugger for Rust" [output.html] +default-theme = "light" + curly-quotes = true +additional-js = ["mermaid.min.js", "mermaid-init.js"] +additional-css = ["./mdbook-admonish.css"] + +[preprocessor] + +[preprocessor.image-size] +command = "mdbook-image-size" + +[preprocessor.mermaid] +command = "mdbook-mermaid" + +[preprocessor.admonish] +command = "mdbook-admonish" +assets_version = "3.0.2" # do not edit: managed by `mdbook-admonish install` diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index 7d4bdb8..a3162f2 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -2,14 +2,9 @@ [Introduction](README.md) -- [Debugging trait errors](debug-media.md) + -- [How to think about Argus]() +- [Trait Debugging 101](trait-debugging-101.md) -- [Debug a Bevy game]() +- [Trait Methods and Typestate](typestate.md) -- [Debug an Axum server]() - -- [Debug async Rust]() - -- [Debug stack overflows]() diff --git a/book/src/assets/axum-hello-server/async-fix-response.mp4 b/book/src/assets/axum-hello-server/async-fix-response.mp4 new file mode 100644 index 0000000..8b4fc69 Binary files /dev/null and b/book/src/assets/axum-hello-server/async-fix-response.mp4 differ diff --git a/book/src/assets/axum-hello-server/axum-type-checks.mp4 b/book/src/assets/axum-hello-server/axum-type-checks.mp4 new file mode 100644 index 0000000..ba44c9d Binary files /dev/null and b/book/src/assets/axum-hello-server/axum-type-checks.mp4 differ diff --git a/book/src/assets/axum-hello-server/bottom-up-start.mp4 b/book/src/assets/axum-hello-server/bottom-up-start.mp4 new file mode 100644 index 0000000..c574de8 Binary files /dev/null and b/book/src/assets/axum-hello-server/bottom-up-start.mp4 differ diff --git a/book/src/assets/axum-hello-server/from-rqst-prts-annotated.png b/book/src/assets/axum-hello-server/from-rqst-prts-annotated.png new file mode 100644 index 0000000..0f1408e Binary files /dev/null and b/book/src/assets/axum-hello-server/from-rqst-prts-annotated.png differ diff --git a/book/src/assets/axum-hello-server/top-down-error-highlighted.png b/book/src/assets/axum-hello-server/top-down-error-highlighted.png new file mode 100644 index 0000000..6720951 Binary files /dev/null and b/book/src/assets/axum-hello-server/top-down-error-highlighted.png differ diff --git a/book/src/assets/axum-hello-server/top-down-impl-highlighted.png b/book/src/assets/axum-hello-server/top-down-impl-highlighted.png new file mode 100644 index 0000000..ea4ffcc Binary files /dev/null and b/book/src/assets/axum-hello-server/top-down-impl-highlighted.png differ diff --git a/book/src/assets/axum-hello-server/top-down-root-highlighted.png b/book/src/assets/axum-hello-server/top-down-root-highlighted.png new file mode 100644 index 0000000..1e7b9c6 Binary files /dev/null and b/book/src/assets/axum-hello-server/top-down-root-highlighted.png differ diff --git a/book/src/assets/diesel-bad-select/find-bug.mp4 b/book/src/assets/diesel-bad-select/find-bug.mp4 new file mode 100644 index 0000000..d543691 Binary files /dev/null and b/book/src/assets/diesel-bad-select/find-bug.mp4 differ diff --git a/book/src/assets/diesel-bad-select/fixed-error.mp4 b/book/src/assets/diesel-bad-select/fixed-error.mp4 new file mode 100644 index 0000000..ed8a6b6 Binary files /dev/null and b/book/src/assets/diesel-bad-select/fixed-error.mp4 differ diff --git a/book/src/assets/simple-workspace/argus-toolbar.png b/book/src/assets/simple-workspace/argus-toolbar.png deleted file mode 100644 index 252f828..0000000 Binary files a/book/src/assets/simple-workspace/argus-toolbar.png and /dev/null differ diff --git a/book/src/assets/simple-workspace/expressions-in-body.mp4 b/book/src/assets/simple-workspace/expressions-in-body.mp4 deleted file mode 100644 index fde2956..0000000 Binary files a/book/src/assets/simple-workspace/expressions-in-body.mp4 and /dev/null differ diff --git a/book/src/assets/simple-workspace/fix-error.mp4 b/book/src/assets/simple-workspace/fix-error.mp4 deleted file mode 100644 index 1445a41..0000000 Binary files a/book/src/assets/simple-workspace/fix-error.mp4 and /dev/null differ diff --git a/book/src/assets/simple-workspace/impl-block.mp4 b/book/src/assets/simple-workspace/impl-block.mp4 deleted file mode 100644 index a8d39fc..0000000 Binary files a/book/src/assets/simple-workspace/impl-block.mp4 and /dev/null differ diff --git a/book/src/assets/simple-workspace/jump-to-error.mp4 b/book/src/assets/simple-workspace/jump-to-error.mp4 deleted file mode 100644 index ae48747..0000000 Binary files a/book/src/assets/simple-workspace/jump-to-error.mp4 and /dev/null differ diff --git a/book/src/assets/simple-workspace/open-to-tree.mp4 b/book/src/assets/simple-workspace/open-to-tree.mp4 deleted file mode 100644 index 6652c5a..0000000 Binary files a/book/src/assets/simple-workspace/open-to-tree.mp4 and /dev/null differ diff --git a/book/src/assets/simple-workspace/top-down.mp4 b/book/src/assets/simple-workspace/top-down.mp4 deleted file mode 100644 index aeec0f0..0000000 Binary files a/book/src/assets/simple-workspace/top-down.mp4 and /dev/null differ diff --git a/book/src/assets/simple-workspace/tree-ico.png b/book/src/assets/simple-workspace/tree-ico.png deleted file mode 100644 index 5e56294..0000000 Binary files a/book/src/assets/simple-workspace/tree-ico.png and /dev/null differ diff --git a/book/src/trait-debugging-101.md b/book/src/trait-debugging-101.md new file mode 100644 index 0000000..df1f36f --- /dev/null +++ b/book/src/trait-debugging-101.md @@ -0,0 +1,216 @@ +# Trait Debugging 101 + +Traits are a pervasive language feature in Rust: Copying, printing, indexing, multiplying, and more common operations use the Rust trait system. As you use more of the language, and utilize the numerous published crates, you will inevitably encounter more traits. Popular crates in the Rust ecosystem use traits to achieve strong type safety, such as the Diesel crate that relies on traits to turn invalid SQL queries into type errors. Impressive! + +Unfortunately, traits also obfuscate type errors. Compiler diagnostics become increasingly complex alongside the types and traits used. This guide demonstrates *trait debugging* in Rust using a new tool, Argus, developed by the [Cognitive Engineering Lab](https://cel.cs.brown.edu/) at Brown University. + +## Your First Web Server + +[Axum](https://docs.rs/axum/latest/axum/) is a popular Rust web application framework, a great example of how traits can obfuscate type errors. We will use Axum to build a web server, and Argus to debug the trait errors; here's our starting code. + +```rust,ignore,compile_fail +{{#include ../../examples/hello-server/src/main.rs:4:}} +``` + +Oh no, our server doesn't type check. Surely, the error diagnostic will tell us why--- + +```text +error[E0277]: the trait bound `fn(LoginAttempt) -> bool {login}: Handler<_, _>` is not satisfied + --> src/main.rs:14:49 + | +14 | let app = Router::new().route("/login", get(login)); + | --- ^^^^^ the trait `Handler<_, _>` is not implemented for fn item `fn(LoginAttempt) -> bool {login}` + | | + | required by a bound introduced by this call + | + = help: the following other types implement trait `Handler`: + as Handler> + as Handler<(), S>> +note: required by a bound in `axum::routing::get` +``` + +in a long-winded way the diagnostic has said "`login` does not implement `Handler`." But as the authors we *intended* for `login` to be a handler. The diagnostic hasn't provided much specific information as to why the code doesn't type check. + +```admonish note +Going forward we will write `{login}` to abbreviate the type of `login`, `fn(LoginAttempt) -> bool`, which is far too verbose to repeat over and over. +``` + +When the error diagnostic says "trait bound `Bleh` is not satisfied", it's a great opportunity to use Argus. + +## Down the Search Tree + +In Rust we write type definitions and trait implementations separately---we refer to trait implementations as "impl blocks." The inner compiler component called the *trait solver* is responsible for answering queries like "Does `{login}` implement `Handler`?" such queries appear as trait bounds in the source code. The trait solver searches through the impl blocks trying to find whether or not the trait bound holds. + +In this post we will be using the *search tree* a data structure produced by the trait solver that describes how it searched impl blocks, and why---or why not---a particular trait bound holds. + +````admonish example +Here's an illustrative diagram of the Axum-error search tree. Argus provides the search tree in a different format, similar to a directory tree, as you shall see further on. + +```mermaid +--- +title: Search tree produced by the Axum trait error +--- +graph TD + root["{login}: Handler"] + implRespH["impl Handler for T\nwhere\n    T: IntoResponse"] + intoResp["{login}: IntoResponse"] + implH["impl Handler for F\nwhere\n    F: FnOnce(T1) -> Fut,\n    Fut: Future + Send,\n    T1: FromRequest"] + isFunc["{login}: FnOnce(LoginAttempt) -> bool"] + boolFut["bool: Future"] + loginARqst["LoginAttempt: FromRequest"] + + root -.-> implRespH + implRespH --T = {login}--> intoResp + root -.-> implH + implH --F = {login}, T1 = LoginAttempt, Fut = bool--> isFunc + implH --Fut = bool--> boolFut + implH --T1 = LoginAttempt--> loginARqst + + class root,implRespH,intoResp,implH,boolFut,loginARqst cssFailure + class isFunc cssSuccess + + classDef default fill:#fafafa, stroke-width:3px, text-align:left + classDef cssSuccess stroke:green + classDef cssFailure stroke:red + linkStyle 0,1,2,4,5 stroke:red + linkStyle 3 stroke:green, stroke-width:4px +``` +> We elide trivial bounds to declutter the diagram. Don't panic if you open the Argus panel and see some bounds not shown here. + +Dotted lines represent an **Or** relationship between parent and child. That is, exactly one of the child blocks needs to hold---outlined in green. We see dotted lines coming from the root bound and extending to the impl blocks. Impl blocks always form an Or relationship with their parent. + +Solid lines represent **And** relationships between parent and child. That is, every one of the child blocks needs to hold. We see solid lines coming from impl blocks and extending to the nested constraints. Constraints always form an And relationship with their parent impl block. + +Traversing the tree from root to leaf is what's referred to as "Top-Down" in the Argus extension. This view represents the full search tree, in other words, *how* the trait solver responded to the query. +```` + +Notice that the Rust compiler diagnostic mentions the *root bound*, `{login}: Handler<_, _>`, instead of the more specific failing bounds at the tree leaves. The compiler is conservative, when presented with multiple failures in different impls, it defaults to reporting their parent. In the diagram there are two potentially matching impl blocks. There's one for a function with a single argument, and there's one for things that implement `IntoResponse` directly---i.e., static responses that don't need input. Because there's more than one potentially matching impl block Rust can't decide which is the actual error, so it reports the parent node of all errors. + +We developed Argus so you can identify the *specific failures* that led to a particular trait error; Argus can provide more specific details on a trait error than Rust is willing to summarize in a single text-based diagnostic message. Let's walk through the search tree as presented in Argus' Top-Down view. + +![Search tree initial bound](assets/axum-hello-server/top-down-root-highlighted.png =600x center) + +Highlighted at the top in orange is the search tree root. Argus represents the search tree in a directory view sort of way. The nodes are on each line, you expand the node's children by clicking. Notice how we still use solid and dotted lines to represent the parent child relationship, they're just to the left of the node instead an arrow connecting two bubbles. + +The trait solver finds an appropriate impl block that matches the initial trait bound ([RustDoc link](https://docs.rs/axum/latest/axum/handler/trait.Handler.html#impl-Handler%3C(M,+T1),+S%3E-for-F))---highlighted below in green. + +![Search tree found impl](assets/axum-hello-server/top-down-impl-highlighted.png =600x center) + +In order to use this impl block to satisfy the root trait bound, `{login}: Handler<_, _>`, the trait solver needs to satisfy all of the conditions in the where clause. Here are the steps it performs. + +1. `F` unifies with `fn(LoginAttempt) -> bool`. + +2. Does `F` implement the trait `FnOnce(T1) -> Fut`? Yes, it does, we said + + ```rust,ignore + F = fn(LoginAttempt) -> bool + ``` + + The type parameters `T1` and `Fut` unify with `LoginAttempt` and `bool`. The bounds `Clone`, `Send`, and `'static` are also checked successfully. + +3. Does `Fut` implement `Future`? Hmmmm, no it doesn't. In step 2 we said that `Fut = bool`, but booleans aren't futures. + +This failing bound tells us that the output of the function needs to be a future. Argh, we forgot to make the handler function asynchronous! What a silly mistake. However, before we jump back to our code and fix the issue, let's reflect on the Argus interface and see how we could have reached this same conclusion faster. + +![Search tree found impl](assets/axum-hello-server/top-down-error-highlighted.png =600x center) + +The screenshots included so far of the trait search tree are from the Top-Down view in Argus. This means we view the search just as Rust performed it: We started at the root question `{login}: Handler<_, _>`, descended into the impl blocks, and found the failing where-clause in a tree leaf. This failing leaf is highlighted in the above image in red. There's a second failing bound, but we'll come back to that in the next section. The insight is that errors are *leaves* in the search tree, so the Top-Down view doesn't show you the errors first but rather the full trait solving process. + +## Up the Search Tree + +What if you want to see the errors first? Argus provides a second view of the tree called the Bottom-Up view. The Bottom-Up view starts at the error leaves and expanding node children traverses up the tree towards the root. This view prioritizes showing you errors first. + +````admonish example +The Bottom-Up view is the *inverted* search tree. You start at the leaves and traverse to the root. Here's the bottom-up version of the Axum error search tree. + +```mermaid +--- +title: Bottom-Up view of the search tree. +--- +graph TD + root["{login}: Handler"] + implRespH["impl Handler for T\nwhere\n    T: IntoResponse"] + intoResp["{login}: IntoResponse"] + implH["impl Handler for F\nwhere\n    F: FnOnce(T1) -> Fut,\n    Fut: Future + Send,\n    T1: FromRequest"] + isFunc["{login}: FnOnce(LoginAttempt) -> bool"] + boolFut["bool: Future"] + loginARqst["LoginAttempt: FromRequest"] + + + implRespH -.-> root + intoResp --> implRespH + implH -.-> root + isFunc --> implH + boolFut --> implH + loginARqst --> implH + + class root,implRespH,intoResp,implH,boolFut,loginARqst cssFailure + class isFunc cssSuccess + + classDef default fill:#fafafa, stroke-width:3px, text-align:left + classDef cssSuccess stroke:green + classDef cssFailure stroke:red + linkStyle 0,1,2,4,5 stroke:red,color:red + linkStyle 3 stroke:green, stroke-width:4px, color:green +``` + +Argus sorts the failing leaves in the Bottom-Up view by which are "most-likely" the root cause of the error. No tool is perfect, and Argus can be wrong! If you click on "Other failures," which appears below the first shown failure, Argus provides you a full list. +```` + + + + + +The above demonstrates that Argus identifies `bool: Future` as a root cause of the overall failure in addition to the second failure: `LoginAttempt: FromRequestParts<_, _>`. The note icon in the Bottom-Up view indicates that the two failures must be resolved together if you want to us the function as a handler. + + + +It turns out there are a lot of problems with our handler. Above shows that after marking the `login` function asynchronous, the bound `bool: IntoResponse` remains unsatisfied. This happened because the associated type `Output` of the `Future`, in this case our boolean, needs to implement `IntoResponse`. Some Argus features demonstrated that you may have missed. + +1. We expand the Bottom Up view to see the lineage of impl blocks traversed to arrive at the failure. In this case there was only one between the root bound and the failing leaf, but the constraints in the where clause show the bound's origin. + +2. To fix the `IntoResponse` bound we looked at all implementors of the trait. The list icon next to a tree node shows all impl blocks for the trait. Going through the list we saw the type `Json` that satisfies our needs while preserving code intent. + +```admonish note +The list of trait implementors is equivalent to what you'd find in Rust documentation. See for yourself in the [`IntoResponse` documentation](https://docs.rs/axum/latest/axum/response/trait.IntoResponse.html#foreign-impls). Rust documentation makes a distinction between "implementations on foreign types" and "implementors," Argus lists both kinds of impl block together. +``` + +Moving forward let's finally fix the last failing bound and get the code to type check. + + + +The above video contains a lot of information. Let's break down what happened. + +1. We look through the implementors of `FromRequestParts`; this being the Argus-identified. However no impl block seemed to preserve my intent of extracting a `LoginAttempt` from request headers. It is a vague to say "nothing seemed right," and of course fixing a type error may require some familiarity with the crate you're using or the domain in which you're working. + +2. Implementing `FromRequestParts` doesn't seem right, but we haven't checked what introduced the bound. Expanding the Bottom-Up view reveals that the bound `FromRequest` was first a constraint to implement `Handler`, and that `FromRequestParts` is an attempt to satisfy the `FromRequest` bound. Here's how that relationship looks in the Argus Top-Down view. + ![FromRequestParts provenance](assets/axum-hello-server/from-rqst-prts-annotated.png =600x center) + What happened is the impl block + + ``` + impl FromRequest for T ... + ``` + + unified `T` and `LoginAttempt` leading Argus to believe that `FromRequestParts` is the failing bound, but if we can satisfy `LoginAttempt: FromRequest<_, _>` this also fixes the trait error. + +3. We look through the list of implementors for `FromRequest`. It's here we find that `Json` types implement `FromRequest`. + +4. We fix the last failing bound, `LoginAttempt: Deserialize`, by deriving `Deserialize` for `LoginAttempt`. + +Finally, after all of these errors, we have a type correct program. All of this work to get a type-correct program, you can use your free time to implement the actual functionality if you wish. + +# Wrapping up + +Rust uses a mechanism called traits to define units of shared behavior. We implement traits for types with impl blocks. + +The *trait solver* searches through available impl blocks to determine if a given type implements a specified trait. Tracing the steps made by the trait solver is what we call the *search tree,* the core data structure exposed by the Argus IDE extension. + +The Argus interface shows the search tree either Top-Down or Bottom-Up. The Top-Down view is the search tree as generated by the trait solver. The Bottom-Up view inverts the search tree and traverses the tree from leaves to root. The list icon next to a node shows all impl blocks for the trait in that node. + +In the next chapter we'll show off more features of Argus and debug a Diesel trait error. diff --git a/book/src/typestate.md b/book/src/typestate.md new file mode 100644 index 0000000..9528186 --- /dev/null +++ b/book/src/typestate.md @@ -0,0 +1,102 @@ +# Trait Methods and Typestate + +Every programming language cultivates its own set of patterns. One pattern common in Rust is the *builder pattern.* Some data structures are complicated to construct, they may require a large number of inputs, or have complex configuration; the builder pattern helps construct complex values. + +A great example of working with builders is the Diesel [`QueryDsl`](https://docs.rs/diesel/latest/diesel/prelude/trait.QueryDsl.html). The `QueryDsl` trait exposes a number of methods to construct a valid SQL query. Each method consumes the caller, and returns a type that itself implements `QueryDsl`. As an example here's the method signature for `select` + +```rust,ignore +fn select(self, selection: Selection) -> Select + where + Selection: Expression, + Self: SelectDsl { /* ... */ } +``` + +The `QueryDsl` demonstrates the complexity allowed by the builder pattern, it ensures valid SQL queries by encoding query semantics in Rust traits. One drawback of this pattern is that error diagnostics become difficult to understand as your types get larger and the traits involved more complex. In this chapter we will walk through how to debug and understand a trait error involving the builder pattern, or as some people will call it, *typestate.* We refer to this pattern as typestate because each method returns a type in a particular state, the methods available to the resulting type depend on its state. Calling methods in the wrong order, or forgetting a method, can result in the wrong state for the next method you'd like to call. Let's walk through an example. + +```rust,ignore +{{#include ../../examples/bad-select/src/main.rs:7:30}} +``` + +Running `cargo check` produces the following verbose diagnostic. + +```text +error[E0271]: type mismatch resolving `>::Count == Once` + --> src/main.rs:29:32 + | +29 | ... .load::<(i32, String)>(con... + | ---- ^^^^ expected `Once`, found `Never` + | | + | required by a bound introduced by this call + | +note: required for `posts::columns::id` to implement `AppearsOnTable` + --> src/main.rs:16:9 + | +16 | ... id -> ... + | ^^ + = note: associated types for the current `impl` cannot be restricted in `where` clauses + = note: 2 redundant requirements hidden + = note: required for `Grouped>` to implement `AppearsOnTable` + = note: required for `WhereClause>` to implement `diesel::query_builder::where_clause::ValidWhereClause>` + = note: required for `SelectStatement, ..., ..., ...>` to implement `Query` + = note: required for `SelectStatement, ..., ..., ...>` to implement `LoadQuery<'_, _, (i32, std::string::String)>` +note: required by a bound in `diesel::RunQueryDsl::load` + --> diesel-2.1.6/src/query_dsl/mod.rs:1542:15 + | +1540 | ...fn load<'query, U>(self, conn: &mut Con... + | ---- required by a bound in this associated function +1541 | ...where +1542 | ... Self: LoadQuery<'query, Conn, +... + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `RunQueryDsl::load` + = note: the full name for the type has been written to 'bad_select-fa50bb6fe8eee519.long-type-16986433487391717729.txt' +``` + +As we did in the previous section, we shall demo a short workflow using Argus to gather the same information. + + + +We want to call attention to a some aspects of the above video that are easy to glance over. + +1. When opening the Argus debugger the hover tooltip said "Expression contains unsatisfied trait bounds," but there wasn't a link to jump to the error. This is an unfortunate circumstance, but one that does occur. In these cases you can open the Argus panel by clicking the Argus status in the bottom information bar, or run the command 'Argus: Inspect current file' in the command palette. + +2. In the Argus panel there are two errors we chose *not* to explore further + + ```rust,ignore + id: Iterator + table: Iterator + ``` + + We chose not to explore them for two reasons. (1) they are in expressions that don't contain errors as shown by Rust Analyzer, the method calls `.eq(...)` and `.filter(...)`. (2) the trait bound for `Iterator` seems strange as it isn't related to the error diagnostic at all; we're looking for something Diesel related but these errors are talking about `Iterator`. For these two reasons I chose to ignore the two `Iterator` bound "errors" and explore the other first. + ```admonish important + **Argus may present more errors than the Rust compiler,** it is research software after all. Use your judgement to decide which errors are first worth exploring, if there are multiple, look at all of them before diving down into one specific search tree. We're working hard to reduce noise produced by Argus as much as possible. + ``` + +3. The printed types in Rust can get painfully verbose, the Rust diagnostic even *wrote types to a file* because they were too long. Argus shortens and condenses type information to keep the panel as readable as possible. One example of this is that fully-qualified identifiers, like `users::columns::id` prints shortened as `id`. On hover, the full path is shown at the bottom of the Argus panel in our mini-buffer. Extra information or notes Argus has for you are printed in the mini-buffer, so keep an eye on that if you feel Argus isn't giving you enough information. + +4. Clicking the tree icon next to a node in the Bottom-Up view jumps to that same node in the Top-Down view. This operation is useful if you want to gather contextual information around a node, but don't want to search the Top-Down tree for it. You can get there in one click. + +In the video we expanded the Bottom-Up view to see where the bound `Count == Once` came from. The origin stems from the `T: AppearsOnTable` constraint in the where clause of the `Eq` impl block. In English we can summarize this as "an equality constraint is valid if both expressions appear in the selected table." Looking through the search tree I see that the bound + +```rust,ignore +users::columns::id: AppearsOnTable +``` + +holds true, while the bound + +```rust,ignore +posts::columns::id: AppearsOnTable +``` + +is unsatisfied. Argh, we forgot to join the `users` and `posts` tables! At this point we understand and have identified the error, now it's time to fix the program. Unfortunately Argus provides no aide to *fix* typestate errors. We're in the wrong state, `posts::id` doesn't appear in the table we're selecting from, we need to get it on the selected-from table. This is a great time to reach for the Diesel documentation for [`QueryDsl`](https://docs.rs/diesel/latest/diesel/prelude/trait.QueryDsl.html). + + + +Here we used our domain knowledge of SQL to find the appropriate join methods. We decided to use an `inner_join` to join the tables, and then all was fixed. + +```admonish note +Finding the appropriate method to change the typestate won't always be so straightforward. If you lack domain knowledge or are unfamiliar with the terms used in the library, you may have to read more of the documentation and look through examples to find appropriate fixes. When in doubt, try something! And use Argus to continue debugging. +``` diff --git a/book/src/whirlwind.md b/book/src/whirlwind.md new file mode 100644 index 0000000..1185010 --- /dev/null +++ b/book/src/whirlwind.md @@ -0,0 +1 @@ +# Whirlwind Tour diff --git a/crates/argus-ser/src/lib.rs b/crates/argus-ser/src/lib.rs index 9fb6e80..56f1e61 100644 --- a/crates/argus-ser/src/lib.rs +++ b/crates/argus-ser/src/lib.rs @@ -18,7 +18,7 @@ //! If a type requires expansion into a richer form, this is done inside the `new` function. //! //! If a type needs to be used within a serde `with` attribute, then an associated function -//! `serialize` is defined, and actual serialization will be deffered to the `serialize` +//! `serialize` is defined, and actual serialization will be deferred to the `serialize` //! extension method. //! //! If you need to serialize an optional type then prefix it with `Option__`, and diff --git a/crates/argus-ser/src/path/default.rs b/crates/argus-ser/src/path/default.rs index e197c7b..aa633ad 100644 --- a/crates/argus-ser/src/path/default.rs +++ b/crates/argus-ser/src/path/default.rs @@ -1,4 +1,4 @@ -//! Default implementaitons from `rustc_middle::ty::print` +//! Default implementations from `rustc_middle::ty::print` use rustc_data_structures::sso::SsoHashSet; use rustc_hir::{def_id::DefId, definitions::DefPathData}; diff --git a/crates/argus-ser/src/ty.rs b/crates/argus-ser/src/ty.rs index 1ace998..edaad43 100644 --- a/crates/argus-ser/src/ty.rs +++ b/crates/argus-ser/src/ty.rs @@ -1767,7 +1767,7 @@ pub enum BoundVariable { impl BoundVariable { pub fn new(didx: ty::DebruijnIndex, var: ty::BoundVar) -> Self { - // FIXME: bound varialbes shouldn't be in serialized types, I haven't + // FIXME: bound variables shouldn't be in serialized types, I haven't // encountered one in the raw output, and before release this was a // `panic`, which never fired. Self::Error(format!("{var:?}^{didx:?}").to_string()) diff --git a/crates/argus/src/aadebug/tree.rs b/crates/argus/src/aadebug/tree.rs index 4d46b9b..fb6d1f9 100644 --- a/crates/argus/src/aadebug/tree.rs +++ b/crates/argus/src/aadebug/tree.rs @@ -456,7 +456,7 @@ impl<'a, 'tcx: 'a> T<'a, 'tcx> { /// A local type failing to implement a trait (local/external). /// NOTE that `T: C` where `T` is an external type is considered impossible /// to change, if this is the only option a relaxed rule might suggest - /// creating a wapper for the type. + /// creating a wrapper for the type. /// /// Intrusive changes /// diff --git a/crates/argus/src/analysis/transform.rs b/crates/argus/src/analysis/transform.rs index 4317f4f..e471a3c 100644 --- a/crates/argus/src/analysis/transform.rs +++ b/crates/argus/src/analysis/transform.rs @@ -245,7 +245,7 @@ impl<'a, 'tcx: 'a> ObligationsBuilder<'a, 'tcx> { // Filter down the set of obligations as much as possible. // // 1. Remove obligations that shouldn't have been checked. (I.e., a failed - // precondition dissallows it from succeeding.) Hopefully, in the future these + // precondition disallows it from succeeding.) Hopefully, in the future these // aren't even solved for. retain_error_sources( &mut obligations, @@ -263,7 +263,7 @@ impl<'a, 'tcx: 'a> ObligationsBuilder<'a, 'tcx> { let obligations = obligations .into_iter() - // marge back in indices without data + // merge back in indices without data .chain(obligations_no_data.into_iter()) .map(|idx| *self.obligations[idx]) .collect::>(); @@ -452,7 +452,7 @@ mod tree_search { /// Search for the target obligation along the non-branching tree path. /// - /// This is usefull if a predicate, reported as a trait error, does not + /// This is useful if a predicate, reported as a trait error, does not /// match one of the stored roots. This can happen when the start of /// the "trait tree" is a stick, e.g., /// diff --git a/crates/argus/src/ext.rs b/crates/argus/src/ext.rs index 8552a8b..36d0246 100644 --- a/crates/argus/src/ext.rs +++ b/crates/argus/src/ext.rs @@ -79,7 +79,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { /// Determine what level of "necessity" an obligation has. /// /// For example, obligations with a cause `SizedReturnType`, - /// with a `self_ty` `()` (unit), is *unecessary*. Obligations whose + /// with a `self_ty` `()` (unit), is *unnecessary*. Obligations whose /// kind is not a Trait Clause, are generally deemed `ForProfessionals` /// (that is, you can get them when interested), and others are shown /// `OnError`. Necessary obligations are trait predicates where the diff --git a/crates/argus/src/find_bodies.rs b/crates/argus/src/find_bodies.rs index d8c11a7..90777c7 100644 --- a/crates/argus/src/find_bodies.rs +++ b/crates/argus/src/find_bodies.rs @@ -1,6 +1,6 @@ //! This is a copy of the `BodyFinder` from `rustc_utils` but it //! does *not* skip const/static items. Funny enough, these items -//! often have imporant trait contraints evaluated (think derive macros). +//! often have important trait constraints evaluated (think derive macros). use rustc_hir::{intravisit::Visitor, BodyId}; use rustc_middle::{hir::nested_filter::OnlyBodies, ty::TyCtxt}; use rustc_span::Span; diff --git a/crates/argus/src/proof_tree/serialize.rs b/crates/argus/src/proof_tree/serialize.rs index da61ec2..cd64234 100644 --- a/crates/argus/src/proof_tree/serialize.rs +++ b/crates/argus/src/proof_tree/serialize.rs @@ -161,8 +161,8 @@ impl SerializedTreeVisitor<'_> { // comparing the JSON values is a bad idea in general. (This is what comparing // interned keys does essentially). We should wait until the new trait solver // has some mechanism for detecting cycles and piggy back off that. - // FIXME: this is currently dissabled but we should check for cycles again... - #[allow(dead_code, unused)] + // FIXME: this is currently disabled but we should check for cycles again... + #[allow(dead_code)] fn check_for_cycle_from(&mut self, from: ProofNodeIdx) { if self.cycle.is_some() { return; diff --git a/crates/argus/src/tls.rs b/crates/argus/src/tls.rs index cfb6b39..e2939b9 100644 --- a/crates/argus/src/tls.rs +++ b/crates/argus/src/tls.rs @@ -27,7 +27,7 @@ use crate::{ const DRAIN_WINDOW: usize = 100; // NOTE: we use thread local storage to accumulate obligations -// accross call to the obligation inspector in `typeck_inspect`. +// across call to the obligation inspector in `typeck_inspect`. // DO NOT set this directly, make sure to use the function `push_obligaion`. thread_local! { static BODY_DEF_PATH: RefCell> = RefCell::default(); diff --git a/examples/bad-select/.rustfmt.toml b/examples/bad-select/.rustfmt.toml new file mode 100644 index 0000000..575d9b5 --- /dev/null +++ b/examples/bad-select/.rustfmt.toml @@ -0,0 +1,2 @@ +# Force method chains to a newline +max_width = 40 diff --git a/examples/bad-select/Cargo.lock b/examples/bad-select/Cargo.lock new file mode 100644 index 0000000..8054bfc --- /dev/null +++ b/examples/bad-select/Cargo.lock @@ -0,0 +1,281 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "bad-select" +version = "0.1.0" +dependencies = [ + "diesel", +] + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "diesel" +version = "2.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff236accb9a5069572099f0b350a92e9560e8e63a9b8d546162f4a5e03026bb2" +dependencies = [ + "bitflags", + "byteorder", + "diesel_derives", + "itoa", + "libsqlite3-sys", + "mysqlclient-sys", + "percent-encoding", + "pq-sys", + "time", + "url", +] + +[[package]] +name = "diesel_derives" +version = "2.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14701062d6bed917b5c7103bdffaee1e4609279e240488ad24e7bd979ca6866c" +dependencies = [ + "diesel_table_macro_syntax", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "diesel_table_macro_syntax" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc5557efc453706fed5e4fa85006fe9817c224c3f480a34c7e5959fd700921c5" +dependencies = [ + "syn", +] + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "libsqlite3-sys" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c10584274047cb335c23d3e61bcef8e323adae7c5c8c760540f73610177fc3f" +dependencies = [ + "pkg-config", + "vcpkg", +] + +[[package]] +name = "mysqlclient-sys" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f61b381528ba293005c42a409dd73d034508e273bf90481f17ec2e964a6e969b" +dependencies = [ + "pkg-config", + "vcpkg", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "pq-sys" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31c0052426df997c0cbd30789eb44ca097e3541717a7b8fa36b1c464ee7edebd" +dependencies = [ + "vcpkg", +] + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "serde" +version = "1.0.204" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.204" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "syn" +version = "2.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "201fcda3845c23e8212cd466bfebf0bd20694490fc0356ae8e428e0824a915a6" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "time" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "url" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" diff --git a/examples/bad-select/Cargo.toml b/examples/bad-select/Cargo.toml new file mode 100644 index 0000000..71e1d40 --- /dev/null +++ b/examples/bad-select/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "bad-select" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +diesel = {version = "2.0.0-rc.1", default-features = false, features = ["sqlite", "postgres", "mysql"]} diff --git a/examples/bad-select/src/main.rs b/examples/bad-select/src/main.rs new file mode 100644 index 0000000..7e43c2d --- /dev/null +++ b/examples/bad-select/src/main.rs @@ -0,0 +1,32 @@ +use diesel::prelude::*; + +allow_tables_to_appear_in_same_query!( + users, posts, +); + +table! { + users(id) { + id -> Integer, + name -> Text, + } +} + +table! { + posts(id) { + id -> Integer, + name -> Text, + user_id -> Integer, + } +} + +fn query(conn: &mut PgConnection) { + users::table + .filter(users::id.eq(posts::id)) + .select(( + users::id, + users::name, + )) + .load::<(i32, String)>(conn); +} + +fn main() {} diff --git a/examples/hello-server/Cargo.lock b/examples/hello-server/Cargo.lock new file mode 100644 index 0000000..39ab657 --- /dev/null +++ b/examples/hello-server/Cargo.lock @@ -0,0 +1,663 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "async-trait" +version = "0.1.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "axum" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" +dependencies = [ + "async-trait", + "axum-core", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper 1.0.1", + "tokio", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper 0.1.2", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "backtrace" +version = "0.3.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "bytes" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" + +[[package]] +name = "cc" +version = "1.0.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5208975e568d83b6b05cc0a063c8e7e9acc2b43bee6da15616a5b73e109d7437" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", +] + +[[package]] +name = "gimli" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" + +[[package]] +name = "hello-server" +version = "0.1.0" +dependencies = [ + "axum", + "serde", + "tokio", +] + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +dependencies = [ + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", +] + +[[package]] +name = "hyper-util" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9" +dependencies = [ + "bytes", + "futures-util", + "http", + "http-body", + "hyper", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +dependencies = [ + "hermit-abi", + "libc", + "wasi", + "windows-sys", +] + +[[package]] +name = "object" +version = "0.36.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b64972346851a39438c60b341ebc01bba47464ae329e55cf343eb93964efd9" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustversion" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "serde" +version = "1.0.204" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.204" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.120" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_path_to_error" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" +dependencies = [ + "itoa", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "syn" +version = "2.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "201fcda3845c23e8212cd466bfebf0bd20694490fc0356ae8e428e0824a915a6" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "sync_wrapper" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" + +[[package]] +name = "tokio" +version = "1.39.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1" +dependencies = [ + "backtrace", + "libc", + "mio", + "pin-project-lite", + "socket2", + "tokio-macros", + "windows-sys", +] + +[[package]] +name = "tokio-macros" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "log", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/examples/hello-server/Cargo.toml b/examples/hello-server/Cargo.toml new file mode 100644 index 0000000..1448fee --- /dev/null +++ b/examples/hello-server/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "hello-server" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +axum = "0.7.5" +tokio = { version = "1.39", features = ["rt-multi-thread"] } +serde = { version = "1.0", features = ["derive"] } diff --git a/examples/hello-server/src/main.rs b/examples/hello-server/src/main.rs new file mode 100644 index 0000000..6ed8803 --- /dev/null +++ b/examples/hello-server/src/main.rs @@ -0,0 +1,21 @@ +use axum::{routing::get, Router}; +use tokio::net::TcpListener; + +struct LoginAttempt { + user_id: u64, + password: String, +} + +fn login(attempt: LoginAttempt) -> bool { + todo!() +} + +#[tokio::main] +async fn main() { + let app = Router::new() + .route("/login", get(login)); + + let listener = TcpListener::bind("0.0.0.0:3000") + .await.unwrap(); + axum::serve(listener, app).await.unwrap(); +} diff --git a/flake.nix b/flake.nix index 4204072..af564a8 100644 --- a/flake.nix +++ b/flake.nix @@ -6,58 +6,94 @@ }; outputs = { self, nixpkgs, flake-utils, rust-overlay }: - flake-utils.lib.eachDefaultSystem (system: - let - overlays = [ (import rust-overlay) ]; - pkgs = import nixpkgs { - inherit system overlays; - }; - toolchain = pkgs.rust-bin.fromRustupToolchainFile ./rust-toolchain.toml; - depotjs = pkgs.rustPlatform.buildRustPackage rec { - pname = "depot"; - version = "0.2.17"; - - # Depot tests require lots of external toolchains, node, typedoc, biome, ... - # so we'll just skip all tests for now and figure this out later. - doCheck = false; - - src = pkgs.fetchFromGitHub { - owner = "cognitive-engineering-lab"; - repo = pname; - rev = "v${version}"; - hash = "sha256-kiQXxTVvzfovCn0YmOH/vTUQHyRS39gH7iBGaKyRZFg="; - }; - - cargoHash = "sha256-m9sG//vBUqGLwWHkyq+sJ8rkQOeaif56l394dgPU1uQ="; - buildInputs = with pkgs; lib.optionals stdenv.isDarwin [ - darwin.apple_sdk.frameworks.SystemConfiguration - ]; - }; - in { - devShell = with pkgs; mkShell { - buildInputs = [ - # Deployment only - vsce - cargo-workspaces - - llvmPackages_latest.llvm - llvmPackages_latest.lld - - guile - depotjs - nodejs_22 - nodePackages.pnpm - - cargo-make - cargo-watch - rust-analyzer - - toolchain - ] ++ lib.optionals stdenv.isDarwin [ - darwin.apple_sdk.frameworks.SystemConfiguration - ]; - - RUSTC_LINKER = "${pkgs.llvmPackages.clangUseLLVM}/bin/clang"; - }; - }); + flake-utils.lib.eachDefaultSystem (system: + let + overlays = [ (import rust-overlay) ]; + pkgs = import nixpkgs { + inherit system overlays; + }; + entry-crate = ./crates/argus-cli; + toolchain = pkgs.rust-bin.fromRustupToolchainFile ./rust-toolchain.toml; + meta = (builtins.fromTOML (builtins.readFile (entry-crate/Cargo.toml))).package; + inherit (meta) name version; + + depotjs = pkgs.rustPlatform.buildRustPackage rec { + pname = "depot"; + version = "0.2.17"; + + # Depot tests require lots of external toolchains, node, typedoc, biome, ... + # so we'll just skip all tests for now and figure this out later. + doCheck = false; + + src = pkgs.fetchFromGitHub { + owner = "cognitive-engineering-lab"; + repo = pname; + rev = "v${version}"; + hash = "sha256-kiQXxTVvzfovCn0YmOH/vTUQHyRS39gH7iBGaKyRZFg="; + }; + + cargoHash = "sha256-m9sG//vBUqGLwWHkyq+sJ8rkQOeaif56l394dgPU1uQ="; + buildInputs = with pkgs; lib.optionals stdenv.isDarwin [ + darwin.apple_sdk.frameworks.SystemConfiguration + ]; + }; + + mdbook-image-size = with pkgs; rustPlatform.buildRustPackage rec { + pname = "mdbook-image-size"; + version = "0.2.0"; + + src = fetchFromGitHub { + owner = "lhybdv"; + repo = pname; + rev = version; + hash = "sha256-fySGDx3vbLsc3fL/54nMVjVRHNlQ2ZYSM4LMDHxUUvs="; + }; + cargoHash = "sha256-iOTIjZr7vyduGTzK0xUssCKBKc8O0AYLpSdcozKPF2o="; + doCheck = false; + }; + + checkProject = pkgs.writeScriptBin "ci-check" '' + cargo fmt --check + cargo clippy + codespell . + cargo test + ''; + in { + devShell = with pkgs; mkShell { + nativeBuildInputs = [ pkg-config ]; + buildInputs = [ + checkProject + + llvmPackages_latest.llvm + llvmPackages_latest.lld + + toolchain + + guile + depotjs + nodejs_22 + nodePackages.pnpm + codespell + + cargo-make + cargo-watch + rust-analyzer + + mdbook + mdbook-mermaid + mdbook-admonish + mdbook-image-size + + vsce + cargo-workspaces + ] ++ lib.optionals stdenv.isDarwin [ + darwin.apple_sdk.frameworks.SystemConfiguration + ] ++ lib.optionals stdenv.isLinux [ + alsa-lib.dev + udev.dev + ]; + + RUSTC_LINKER = "${llvmPackages.clangUseLLVM}/bin/clang"; + }; + }); } diff --git a/ide/packages/common/src/TreeInfo.ts b/ide/packages/common/src/TreeInfo.ts index ce6cffe..048c73d 100644 --- a/ide/packages/common/src/TreeInfo.ts +++ b/ide/packages/common/src/TreeInfo.ts @@ -244,7 +244,7 @@ export class TreeInfo { const goalData = tree.goals[node.Goal]; const result = tree.results[goalData.result]; return "keep"; - // FIXME: I belive that this logic is correct, but argus crashes when enabled + // FIXME: I believe that this logic is correct, but argus crashes when enabled // return isHiddenObl({ necessity: goalData.necessity, result }) // ? "remove-tree" // : "remove-node"; @@ -425,7 +425,7 @@ export class TreeInfo { /** * Define the heuristic used for inertia in the system. Previously we were - * using `momentum / velocity` but this proved too sporatic. Some proof trees + * using `momentum / velocity` but this proved too sporadic. Some proof trees * were deep, needlessely, and this threw a wrench in the order. */ public static setInertia = (set: SetHeuristic) => { diff --git a/ide/packages/extension/src/view.ts b/ide/packages/extension/src/view.ts index dc62e31..69f88da 100644 --- a/ide/packages/extension/src/view.ts +++ b/ide/packages/extension/src/view.ts @@ -73,7 +73,7 @@ export class View { ); // Listen for when the panel is disposed - // This happens when the user closes the panel or when the panel is closed programatically + // This happens when the user closes the panel or when the panel is closed programmatically this.disposables.push( panel.onDidDispose(() => { log("Disposing panel"); diff --git a/ide/packages/panoptes/src/TreeView/Panels.tsx b/ide/packages/panoptes/src/TreeView/Panels.tsx index 18a8034..b198ec8 100644 --- a/ide/packages/panoptes/src/TreeView/Panels.tsx +++ b/ide/packages/panoptes/src/TreeView/Panels.tsx @@ -26,7 +26,7 @@ export interface PanelDescription { interface PanelState { activePanel: number; node?: number; - programatic?: boolean; + programmatic?: boolean; } export function usePanelState() { @@ -70,13 +70,13 @@ const Panels = ({ }, []); // NOTE: rerenders should not occur if the user clicks on a tab. We cache the - // elements in state to avoid this. IFF the change is *programatic*, meaning + // elements in state to avoid this. IFF the change is *programmatic*, meaning // some GUI action caused the change, we always want to force a rerender so that // state change animations are shown. useEffect(() => { console.debug(`Panel(${id}) params changed`, active, programaticSwitch); if (programaticSwitch) { - // On a programatic switch only rerender the active tab + // On a programmatic switch only rerender the active tab rerender(active); } }, [active, programaticSwitch]); diff --git a/ide/packages/panoptes/src/TreeView/TreeApp.tsx b/ide/packages/panoptes/src/TreeView/TreeApp.tsx index 66a5721..0006c70 100644 --- a/ide/packages/panoptes/src/TreeView/TreeApp.tsx +++ b/ide/packages/panoptes/src/TreeView/TreeApp.tsx @@ -66,7 +66,7 @@ const TreeApp = ({ // Callback passed to the BottomUp panel to jump to the TopDown panel. - setState({ activePanel: 1, node: n, programatic: true }) + setState({ activePanel: 1, node: n, programmatic: true }) } /> ) @@ -93,7 +93,7 @@ const TreeApp = ({ manager={[ state.activePanel, n => setState({ activePanel: n }), - state.programatic + state.programmatic ]} description={tabs} /> diff --git a/ide/packages/panoptes/src/Workspace.tsx b/ide/packages/panoptes/src/Workspace.tsx index 7a7c471..9a3e7c6 100644 --- a/ide/packages/panoptes/src/Workspace.tsx +++ b/ide/packages/panoptes/src/Workspace.tsx @@ -45,7 +45,7 @@ const Workspace = observer( ({ fn }) => fn === HighlightTargetStore.value?.file ); if (0 <= idx && idx !== state.activePanel) - setState({ activePanel: idx, programatic: true }); + setState({ activePanel: idx, programmatic: true }); }, [HighlightTargetStore.value?.file]); const viewProps = { @@ -79,7 +79,7 @@ const Workspace = observer( manager={[ state.activePanel, n => setState({ activePanel: n }), - state.programatic + state.programmatic ]} /> diff --git a/ide/packages/print/src/lib.tsx b/ide/packages/print/src/lib.tsx index 356c3df..f349d30 100644 --- a/ide/packages/print/src/lib.tsx +++ b/ide/packages/print/src/lib.tsx @@ -30,7 +30,7 @@ import { // `PrintWithFallback`. Pretty printing is still a fragile process and // I don't have full confidence in it yet. // -// Additionally, this component sets the contents to stlye with the editor monospace font. +// Additionally, this component sets the contents to style with the editor monospace font. export const PrintWithFallback = ({ object, Content diff --git a/ide/packages/print/src/private/syntax.tsx b/ide/packages/print/src/private/syntax.tsx index 82dae84..08b93aa 100644 --- a/ide/packages/print/src/private/syntax.tsx +++ b/ide/packages/print/src/private/syntax.tsx @@ -41,7 +41,7 @@ export const Kw = ({ children }: React.PropsWithChildren) => ( /** * Create a wrapper around the children using a `stx-wrapper` class and the - * additional class `c`. This makes a wrapper that breakes around the wrapped + * additional class `c`. This makes a wrapper that breaks around the wrapped * elements. */ const makeCSSWrapper = diff --git a/ide/packages/print/src/private/term.tsx b/ide/packages/print/src/private/term.tsx index 1f531b3..4a31068 100644 --- a/ide/packages/print/src/private/term.tsx +++ b/ide/packages/print/src/private/term.tsx @@ -83,7 +83,7 @@ export const PrintExpr = ({ o }: { o: ExprDef }) => { }; // NOTE: this is the mir BinOp enum so not all operators are "source representable." -// Exluding "Cmp" as it rearranges the operands and doesn't follow the pattern. +// Excluding "Cmp" as it rearranges the operands and doesn't follow the pattern. const PrintBinOp = ({ o }: { o: Exclude }) => { if (o === "Add") { return "+"; diff --git a/ide/packages/print/src/private/ty.tsx b/ide/packages/print/src/private/ty.tsx index e1b1717..f88602c 100644 --- a/ide/packages/print/src/private/ty.tsx +++ b/ide/packages/print/src/private/ty.tsx @@ -520,7 +520,7 @@ export const PrintRegion = ({ case "Anonymous": { // NOTE: by default we don't print anonymous lifetimes. There are times // when it looks better, e.g., when the region is `mut`. One gotcha right now - // is that we don't rename them, which makes reasoning about anonymouse lifetimes + // is that we don't rename them, which makes reasoning about anonymous lifetimes // tricky. if (forceAnonymous) { return "'_"; @@ -568,7 +568,7 @@ export const PrintBoundTyKind = ({ o }: { o: BoundTyKind }) => { export const PrintBoundVariableKind = ({ o }: { o: BoundVariableKind }) => { if ("Const" === o) { - // TODO: not sure what to do with boudn "consts", we don't have data for them. + // TODO: not sure what to do with bound "consts", we don't have data for them. return null; } else if ("Ty" in o) { return ;