Repository navigation
Expand file tree
/
Copy pathCargo.toml
More file actions
209 lines (201 loc) · 10.9 KB
/
Copy pathCargo.toml
File metadata and controls
209 lines (201 loc) · 10.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
[workspace]
members = [
"core",
#"strapdown_c", <-- Future C bindings
#"strapdown_py", <-- Future Python bindings
"sim",
"geonav"
]
resolver = "3"
# Shared metadata for every crate in the workspace. Individual crates opt in
# with `field.workspace = true`.
[workspace.package]
authors = ["James Brodovsky"]
edition = "2024"
license = "MIT"
homepage = "https://www.strapdown.rs"
repository = "https://github.com/jbrodovsky/strapdown-rs"
# Minimum supported Rust version. Keep this in sync with `channel` in
# rust-toolchain.toml, `msrv` in clippy.toml and the CI toolchain pins; raising
# it is a breaking change.
rust-version = "1.91"
[workspace.dependencies]
# Pinned to a single minor version on purpose. A range spanning 0.33/0.34 lets
# Cargo resolve different, semver-incompatible copies into different workspace
# crates, which fails to compile when nalgebra types cross a crate boundary.
nalgebra = "0.34"
chrono = { version = "0.4.41", features = ["serde"] }
serde = { version = "1.0.219", features = ["derive"] }
rand = "0.9.1"
rand_distr = "0.5.1"
csv = "1.3.1"
log = "0.4"
anyhow = "1.0.99"
# Already resolved transitively at 2.0.18, so declaring it directly adds no build cost.
thiserror = "2.0"
clap = { version = "4.5.4", features = ["derive"] }
netcdf = "0.12.0"
world_magnetic_model = "0.4.0"
assert_approx_eq = "1.1.0"
tempfile = "3.15"
[profile.test]
inherits = "release"
[workspace.metadata.book]
title = "Strapdown-rs Documentation"
authors = ["James Brodovsky"]
description = "A Rust library for strapdown inertial navigation systems"
language = "en"
src = "book/src"
# --- Lints (queue 100, issue #253) -------------------------------------------
#
# These are enforced, not advisory: the blocking CI gate and `cargo lint`
# both run `cargo clippy ... -- -D warnings`, which promotes every warn below
# to an error. The v1.0 queue originally deferred this to position 9 to avoid
# churn colliding with positions 2-8, but enforcing now is the deliberate
# choice -- the point of the strict groups is to catch defects while the
# filter code is still being written, not after.
#
# Levels are `warn` rather than `deny` so that a local `cargo clippy` (no
# `-D warnings`) still reports rather than halts, which keeps the inner
# development loop usable. CI is what makes them binding.
#
# Anything allowed below is allowed *here*, once, with a reason -- never with a
# scattered `#[allow]` at the call site.
# Lint levels are `deny`, not `warn` (#253, queue 9).
#
# CI has run `-D warnings` since queue 100, so the gate itself is not new -- what changes is
# that a plain `cargo clippy` or `cargo build` now fails the same way CI does, instead of
# printing a warning a developer can scroll past and rediscover in CI ten minutes later.
#
# Safe for anyone consuming these crates as dependencies: Cargo builds dependencies with
# `--cap-lints allow`, so these levels apply only when this workspace is built directly.
#
# The real cost is toolchain drift. `pedantic` and `nursery` are lint *groups*, so a newer
# clippy can add a lint this code has never been checked against and fail a build that passed
# yesterday. That was already true of CI's `-D warnings`; `unknown_lints` below is what keeps
# the reverse case -- an allow for a lint an older toolchain does not have -- from breaking.
[workspace.lints.rust]
# Required because the clippy table below names lints that do not exist in every
# toolchain the project builds on. An `allow` for an unknown lint is itself a warning,
# which `-D warnings` would turn into an error -- so without this, adding a forward-looking
# allow breaks the older toolchain and omitting it breaks the newer one.
unknown_lints = "allow"
rust_2018_idioms = { level = "deny", priority = -1 }
unreachable_pub = "deny"
missing_debug_implementations = "deny"
# Named by #253. Deferred past queue 100 with the rest of the documentation lints
# because `StrapdownError` did not exist yet and `missing_errors_doc` could not be
# satisfied; that landed in queue 2, and the remaining 157 undocumented public items
# (56 in `sim`, 40 in `geonav`, 21 in `measurements`, ...) are documented now.
#
# This is the lint with the most direct user-facing payoff of the set: every item it
# names is a `pub` item that renders on docs.rs as a bare signature with no prose.
missing_docs = "deny"
[workspace.lints.clippy]
pedantic = { level = "deny", priority = -1 }
nursery = { level = "deny", priority = -1 }
# #254's zero-panic policy, now enforced. The 49 library call sites these named are
# gone; `StrapdownError` gives every one of them somewhere to return to. Tests are
# exempted in `clippy.toml`, so these point only at library code.
# In direct tension with `#[non_exhaustive]`, which the v1.0 API freeze put on every
# configuration type so that adding a field in 1.x is not a major bump. The lint exists to
# push construction toward a struct literal; `#[non_exhaustive]` forbids that literal from
# any other crate, so the two cannot both be satisfied and the lint is simply wrong here --
# it fires on the only construction form the attribute permits. Relaxed once, workspace-wide
# and with this reason, rather than with a scattered call-site `#[allow]` at each of the ~35
# sites (see AGENTS.md on how lints are relaxed in this project).
field_reassign_with_default = "allow"
unwrap_used = "deny"
expect_used = "deny"
panic = "deny"
# Enabled with them: the error and panic sections describe `StrapdownError`, which
# did not exist when these were deferred.
missing_errors_doc = "deny"
missing_panics_doc = "deny"
# Relaxed on purpose, with reasons.
#
# `similar_names` (60 hits) contradicts the documented naming convention: the
# mechanization equations are transcribed from Groves and use deliberately
# parallel names (`r_n`/`r_e`, `lat_0`/`lat_1`, `cos_lat0`/`cos_lat1`).
# Renaming them to satisfy the lint would make them harder to check against
# the reference, which is the opposite of what CLAUDE.md asks for.
similar_names = "allow"
# `must_use_candidate` (88 hits) is pure signature noise with no defect signal.
#
# `doc_markdown` is named explicitly by #253 and was re-examined for it. The 356
# genuine hits -- `TestDataRecord`, `StrapdownState`, `NavSolution` and friends
# written without backticks -- **have been fixed**; that part of #253 is done. The
# lint itself stays allowed because the remaining 142 hits are all false positives
# of one kind, and there is no way to silence them that is worth the cost:
#
# These docs render maths with KaTeX (`katex-header.html`, wired up through
# `build.rustdocflags` in `.cargo/config.toml` for local `cargo doc` and through
# `package.metadata.docs.rs` for docs.rs), so they are full
# of `$...$` and `$$...$$`. `doc_markdown` cannot tell LaTeX from Rust, and reads
# every subscripted symbol -- `p_n`, `v_e`, `C_b^n`, `\Delta t`, `F_k` -- as an
# un-backticked item. Backticking them breaks the maths: KaTeX renders the literal
# source, so Groves Eq. 5.56 comes out as `\frac{v_n(-)}{R_n` instead of a fraction.
# `cargo clippy --fix` did exactly that before the maths lines were restored by hand.
#
# Nor can the false positives be localised. Over half sit in module-level `//!`
# docs (`lib.rs`, `calibration.rs`, `engine.rs`), where the only allow with the
# right scope is an inner attribute at the crate root -- which disables the lint
# for all of `strapdown-core` anyway, while pretending otherwise. Tracked in #329.
must_use_candidate = "allow"
doc_markdown = "allow"
# The crate is named for its domain; `strapdown::StrapdownState` is intentional.
module_name_repetitions = "allow"
# `suboptimal_flops` (81 hits) rewrites `a - b * c` into `b.mul_add(-c, a)`.
# Rejected on three counts, having applied it and read the result:
# 1. `mul_add` is a *fused* operation -- one rounding where the expression had
# two. That is a real change to floating-point results in the WGS84 radii,
# the Somigliana gravity model, the quaternion update and the UKF sigma
# weights. This crate has already lost days to a vertical channel that
# diverged from a 1e-14 perturbation (#266, #286); trading exactness for an
# unmeasured speculative speedup in that code is a bad bargain.
# 2. It destroys the line-by-line correspondence with the Groves equations
# that CLAUDE.md asks the mechanization to preserve. Compare
# `(1.0 - e2 * sin_lat_sq).sqrt()` with
# `e2.mul_add(-sin_lat_sq, 1.0).sqrt()` against the textbook.
# 3. Applied to haversine it emitted `(dlat/2.0).sin().mul_add((dlat/2.0).sin(), ..)`,
# recomputing the sine rather than reusing it.
# `imprecise_flops` stays on: `ln_1p`/`exp_m1` are genuine accuracy wins.
suboptimal_flops = "allow"
# `manual_midpoint` rewrites `0.5 * (a + b)` into `f64::midpoint(a, b)`. Declined for the
# same reason as `suboptimal_flops` above: `midpoint` is specified to round differently in
# order to avoid overflow, and all three hits are inside `position_update`, i.e. the
# mechanization itself. The values being averaged are velocities and latitude rates in the
# ordinary f64 range, so there is no overflow to avoid -- only a rounding change, in the one
# place this crate can least afford one. It also obscures the trapezoidal-integration form
# that matches Groves Eq. 5.56.
manual_midpoint = "allow"
# Numeric domain. `usize as f64` for particle counts and sample indices, and
# `f64 as usize` for grid lookups, are the arithmetic this crate is made of;
# every one is deliberate and bounds-checked by construction. Denying them
# would mean ~70 call-site `#[allow]`s, which is worse than one decision here.
cast_precision_loss = "allow"
cast_possible_truncation = "allow"
cast_sign_loss = "allow"
# WGS84 and Somigliana constants are written exactly as published
# (6378137.0, 7.292115e-5). Digit grouping them would obscure the match.
unreadable_literal = "allow"
# All 7 hits compare exactly-representable values in tests (sign flips,
# `0.5 * 10.0`), checked individually. `assert_approx_eq` is used where the
# comparison is actually approximate, per CLAUDE.md.
float_cmp = "allow"
# Fires on `option_if_let_else` rewrites that turn a readable `if let` into a
# nested `map_or_else` closure; a readability regression in the filter code.
option_if_let_else = "allow"
# 14 hits, all in the mechanization and CLI dispatch. Real signal, but the
# decomposition belongs with #262's `InsEngine` extraction rather than as
# drive-by splits that would conflict with every spine PR above this one.
too_many_lines = "allow"
# Nursery lint that fires on `pub(crate)` inside an already-private module and
# asks for plain `pub`. In `strapdown-sim` (a binary crate) `pub(crate)` is the
# more informative spelling -- it says "crate-internal by intent", not "public
# by accident" -- so this one is declined on readability grounds.
redundant_pub_crate = "allow"
# Enabled with the rest. The `Rc<GeoMap>` parameters in geonav remain by-value on
# purpose -- cloning an `Rc` is a refcount bump and the callee stores it -- so any
# that resurface there should be judged, not mechanically borrowed.
needless_pass_by_value = "deny"