From eb2b6cbaa656627fff72554ba75228856b0a5b5a Mon Sep 17 00:00:00 2001 From: Matthew Podwysocki Date: Tue, 15 Sep 2026 15:06:21 -0400 Subject: [PATCH 1/2] Use American English throughout Mixed spellings across 28 files, mostly mine: `honoured`, `behaviour`, `recognise`, `serialise`, `neighbour`, `licence`, `centred`, `whilst`, `sanitised`, `defence` and friends. 91 lines, comments and doc prose plus a handful of strings the user actually sees: - `auth status` said "unrecognised prefix" - a bad token said "Mapbox does not recognise the token it would be given." - three warnings said "`--output` is not honoured by ..." The `cancelled` error code is deliberately left alone. It is the machine-readable `code` in the JSON error contract, documented in docs/commands.md and asserted in tests/non_interactive.rs, so renaming it is a breaking change rather than a spelling fix. Worth doing before 1.0, but as its own decision with its own changelog entry. No behaviour change otherwise, and nothing generated was touched: openapi/ and internal/ are excluded, docs/commands.md's sample output is untouched, and every edited line sits outside a code fence. --- .github/workflows/ci.yml | 4 ++-- CHANGELOG.md | 4 ++-- CONTRIBUTING.md | 2 +- Cargo.toml | 6 +++--- docs/commands.md | 12 ++++++------ scripts/install.ps1 | 6 +++--- scripts/install.sh | 6 +++--- scripts/test-install.ps1 | 8 ++++---- scripts/test-install.sh | 8 ++++---- src/agent_skills.rs | 6 +++--- src/auth.rs | 10 +++++----- src/completion.rs | 4 ++-- src/confirm.rs | 2 +- src/executor.rs | 2 +- src/generate_skills.rs | 16 ++++++++-------- src/http.rs | 4 ++-- src/main.rs | 6 +++--- src/output.rs | 16 ++++++++-------- src/skill_dest.rs | 4 ++-- src/spec.rs | 6 +++--- src/tilesets_cli.rs | 12 ++++++------ src/update_check.rs | 8 ++++---- tests/non_interactive.rs | 8 ++++---- tests/output_contract.rs | 6 +++--- tests/proxy.rs | 12 ++++++------ tests/schema_contract.rs | 2 +- tests/source_guards.rs | 2 +- 27 files changed, 91 insertions(+), 91 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 41eb5e2..3a7db73 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,7 +37,7 @@ jobs: # six-hour default before anyone hears about it. That is not hypothetical — # a test that blocked on `accept()` with no deadline wedged the Windows leg # for its full six hours, and the only reason it went unnoticed for a day - # is that every run on `main` was cancelled by the next push first. + # is that every run on `main` was canceled by the next push first. # # On all three rather than Windows alone: nobody here runs Windows as a # daily driver, so CI is the only signal it has — but a Unix leg that @@ -307,7 +307,7 @@ jobs: "https://github.com/rustsec/rustsec/releases/download/cargo-audit%2Fv${AUDIT_VERSION}/${name}.tgz" echo "${AUDIT_SHA256} /tmp/cargo-audit.tgz" | sha256sum -c - # The archive has a versioned top-level directory and carries a - # README and two licences beside the binary; name the one file that + # README and two licenses beside the binary; name the one file that # belongs on PATH. tar xzf /tmp/cargo-audit.tgz -C "$HOME/.cargo/bin" \ --strip-components=1 "${name}/cargo-audit" diff --git a/CHANGELOG.md b/CHANGELOG.md index 4bcbbd7..5a73aac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -124,7 +124,7 @@ that may never merge. They are not releases and are not listed here. `unsupported scheme socks5` in the message is the part that distinguishes it from the network being down. -- `rand` moved from 0.8 to 0.10. No behaviour changes: the two places it is +- `rand` moved from 0.8 to 0.10. No behavior changes: the two places it is used — the PKCE verifier and the OAuth `state` in `mapbox auth login` — draw from `ThreadRng` before and after, which `rand` declares a CSPRNG, and `thread_rng().gen()` becoming `random()` is a rename. Recorded because it @@ -132,7 +132,7 @@ that may never merge. They are not releases and are not listed here. this release should be able to find it. Both are now covered by tests against RFC 7636, which they were not before. -- Both installers now honour `MAPBOX_CLI_NO_TELEMETRY`, the name the binary +- Both installers now honor `MAPBOX_CLI_NO_TELEMETRY`, the name the binary reads. They were left on the old `DISABLE_TELEMETRY` when the binary was renamed, so neither name silenced both halves: the documented variable stopped the CLI's markers but not the installer's, and the old one did the diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 46d2166..f2a1a69 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -41,7 +41,7 @@ logged in. Four rules the compiler holds rather than a reviewer, declared in `Cargo.toml` with the reasoning beside each: no `unsafe`, no `println!` -(stdout belongs to `output::emit`, the single place `--output` is honoured), +(stdout belongs to `output::emit`, the single place `--output` is honored), no `dbg!`, no `todo!`/`unimplemented!`. ## Where the commands come from diff --git a/Cargo.toml b/Cargo.toml index 7c54919..8cb4407 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,11 +5,11 @@ edition = "2021" description = "A command-line interface for Mapbox APIs, with commands generated at build time from OpenAPI specs." repository = "https://github.com/mapbox/cli" # The file rather than an SPDX expression: LICENSE is Mapbox's own terms and -# not an OSI licence, so there is no identifier that says what it grants. +# not an OSI license, so there is no identifier that says what it grants. license-file = "LICENSE" # A rail against an accidental `cargo publish`, not a decision that it never # could be reconsidered: crates.io is for crates anyone may use under an open -# licence, and a publish cannot be taken back. +# license, and a publish cannot be taken back. publish = false # The oldest Rust this crate compiles on, measured rather than aspirational: # `CredentialLock` in src/auth.rs calls `File::lock`/`unlock`, stabilised in @@ -46,7 +46,7 @@ unsafe_code = "deny" [lints.clippy] # The output contract, made mechanical. This repo's rule is: don't reintroduce a # `println!` for a result — stdout belongs to `output::emit`, which is the -# single place `--output` is honoured and the single place a result is +# single place `--output` is honored and the single place a result is # written. There are no violations today; this keeps it that way. # `print_stderr` is deliberately *not* denied: warnings and progress go to # stderr from a dozen places on purpose, several of them outside the output diff --git a/docs/commands.md b/docs/commands.md index cd536e2..525ed1b 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -1586,7 +1586,7 @@ mapbox sprites delete ckstyle00000000000000001a zz-clitest-1 --username user The sprite index **after** the delete — the named icon is gone from it, and everything else is still there. A 200 with the full layout, not a 204: the -absence of the icon is the only acknowledgement there is. +absence of the icon is the only acknowledgment there is. @@ -1723,7 +1723,7 @@ one, so a plain map image needs no overlay expression. ### `mapbox static get-image` -A map image centred on a point. +A map image centered on a point. #### Parameters @@ -2083,7 +2083,7 @@ mapbox styles delete ckstyle00000000000000003c --username user A 204 carries no body. Rather than print nothing, the CLI confirms what happened — nothing is asked beforehand, so the line after the fact is the -only acknowledgement there is. +only acknowledgment there is.
Terminal — -o textAgent — -o json
@@ -2524,7 +2524,7 @@ security, style quality, geospatial operations and the mobile and web SDKs. Twenty skills, each a directory of Markdown. **Not the same as [`generate-skills`](#generate-skills)**, which is its -neighbour in `--help` and writes something else entirely: that one renders a +neighbor in `--help` and writes something else entirely: that one renders a skill describing *this CLI's own commands*, from the specs compiled into the binary. These are about using Mapbox; that one is about using `mapbox`. They share the destination flags below and nothing else. @@ -2834,7 +2834,7 @@ mapbox/mapbox-agent-skills@main.` and `"updated": []`. ### `mapbox agent-skills uninstall` Removes installed skill directories. At least one `NAME` is required — an -empty list is not a licence to remove everything. +empty list is not a license to remove everything. **This is the one subcommand that makes no request.** It works from what is on disk, which also means it can remove a skill that has since been unpublished. @@ -3476,7 +3476,7 @@ names the flag rather than offering a login that could not outrank it. | `http_` | The API answered non-2xx. Carries `status` and the response `body`. | | `request_failed` | Transport failure — proxy, DNS, TLS. Never carries the URL, because the access token rides in its query string. | -The CLI honours `HTTPS_PROXY`, `HTTP_PROXY`, `ALL_PROXY` and `NO_PROXY`, and +The CLI honors `HTTPS_PROXY`, `HTTP_PROXY`, `ALL_PROXY` and `NO_PROXY`, and needs no proxy configuration of its own. Two things that look like network faults and are not: diff --git a/scripts/install.ps1 b/scripts/install.ps1 index 1ce4d57..cbd81b8 100644 --- a/scripts/install.ps1 +++ b/scripts/install.ps1 @@ -5,7 +5,7 @@ # irm https://cli.mapbox.com/install.ps1 | iex # # This is the Windows half of scripts/install.sh, which is POSIX sh and stops -# with instructions when it recognises Git Bash, MSYS2 or Cygwin. The two are +# with instructions when it recognizes Git Bash, MSYS2 or Cygwin. The two are # the same script in two languages: same environment variables, same order of # operations, and the same things said about a checksum that does not match, an # install directory that cannot be written, and a `mapbox` that came from @@ -86,7 +86,7 @@ # because this value comes from the environment and ends up in a header. $InstallSource = $env:MAPBOX_CLI_INSTALL_SOURCE - # The switch src/telemetry.rs honours for the CLI's own User-Agent, read + # The switch src/telemetry.rs honors for the CLI's own User-Agent, read # here the same way, because someone who put it in a Dockerfile and then # runs `irm ... | iex` in the same file has already said which way they # want it. The product token above survives it - the equivalent of @@ -95,7 +95,7 @@ # # Two names, and only the binary dropped the old one. # MAPBOX_CLI_NO_TELEMETRY is the documented switch; DISABLE_TELEMETRY is - # what it was called before, and this script still honours it. The rename + # what it was called before, and this script still honors it. The rename # was announced as breaking for the binary; nothing announced it for the # installers, which are fetched and run in one line with no release notes # in front of the reader - and breaking an opt-out is the one change that diff --git a/scripts/install.sh b/scripts/install.sh index 37002da..243d5b0 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -62,7 +62,7 @@ USER_AGENT='mapbox-cli-install/1' # header. INSTALL_SOURCE="${MAPBOX_CLI_INSTALL_SOURCE:-}" -# The switch `src/telemetry.rs` honours for the CLI's own User-Agent, read here +# The switch `src/telemetry.rs` honors for the CLI's own User-Agent, read here # the same way, because someone who put it in a Dockerfile and then pipes this # script into sh in the same file has already said which way they want it. The # product token above is what survives it — the equivalent of @@ -71,7 +71,7 @@ INSTALL_SOURCE="${MAPBOX_CLI_INSTALL_SOURCE:-}" # # **Two names, and only the binary dropped the old one.** # `MAPBOX_CLI_NO_TELEMETRY` is the documented switch; `DISABLE_TELEMETRY` is -# what it was called before, and this script still honours it. The rename was +# what it was called before, and this script still honors it. The rename was # announced as breaking for the binary, so a `DISABLE_TELEMETRY=1` there # genuinely stopped working and the changelog says so. Nothing announced it for # the installers — this file is fetched and run in one line, so a reader has no @@ -625,7 +625,7 @@ install_tilesets() { return 2 } -# Honour MAPBOX_TILESETS_CLI, the override the CLI itself respects: someone +# Honor MAPBOX_TILESETS_CLI, the override the CLI itself respects: someone # who has pointed it at a particular executable has already made this # decision, whether or not that executable is currently there. if [ -n "${MAPBOX_TILESETS_CLI:-}" ]; then diff --git a/scripts/test-install.ps1 b/scripts/test-install.ps1 index 210a254..1a04678 100644 --- a/scripts/test-install.ps1 +++ b/scripts/test-install.ps1 @@ -8,7 +8,7 @@ # # ci.yml runs it under both, because that is the split install.ps1 has to cope # with: 5.1 is what `powershell` opens on a stock Windows 11 and is where the -# TLS default, the progress bar and the stderr-is-an-error behaviour bite. +# TLS default, the progress bar and the stderr-is-an-error behavior bite. # # It also runs on macOS and Linux under pwsh, which is how it is developed - # there is no Windows to hand. The cases that cannot mean anything there (the @@ -486,7 +486,7 @@ try { $tagged = @($logged | Where-Object { $_ -like '*src/onboardingscriptrm-rf' }) Expect-Equal '2' ([string]$logged.Count) 'made the two requests a run makes: the manifest and the archive' Expect-Equal '2' ([string]$marked.Count) 'both name the installer and the triple' - Expect-Equal '2' ([string]$tagged.Count) 'both carry the source tag, sanitised for a header' + Expect-Equal '2' ([string]$tagged.Count) 'both carry the source tag, sanitized for a header' Clear-Env 'MAPBOX_CLI_INSTALL_SOURCE' Start-Case 'DISABLE_TELEMETRY keeps it down to the product token' @@ -514,10 +514,10 @@ try { Clear-Env 'DISABLE_TELEMETRY' Clear-Env 'MAPBOX_CLI_INSTALL_SOURCE' - Start-Case 'MAPBOX_CLI_NO_TELEMETRY is honoured, and outranks the old name' + Start-Case 'MAPBOX_CLI_NO_TELEMETRY is honored, and outranks the old name' New-CaseEnv 'telemetry-new-name' $env:MAPBOX_CLI_INSTALL_SOURCE = 'dockerfile' - # The documented name, which the binary reads and this script honours too. + # The documented name, which the binary reads and this script honors too. $env:MAPBOX_CLI_NO_TELEMETRY = '1' [IO.File]::WriteAllText($RequestLog, '') Invoke-Installer diff --git a/scripts/test-install.sh b/scripts/test-install.sh index ca51105..37a6f84 100755 --- a/scripts/test-install.sh +++ b/scripts/test-install.sh @@ -198,7 +198,7 @@ expect_says() { # command-output want label # and `sha256` has to choose between entries rather than match the only one # there. One of the decoys is the Windows `.zip`, sitting immediately after # the entry being read: extensions are no longer uniform across targets, and -# the neighbour a greedy match would bleed into is the one that proves it does +# the neighbor a greedy match would bleed into is the one that proves it does # not. make_channel() { # dir version [bad-sha|broken] channel_dir="$1" @@ -658,7 +658,7 @@ expect_in_file "$CURL_LOG" "-A ${INSTALLER_UA} (${TARGET}" 'DISABLE_TELEMETRY=0 expect_in_file "$CURL_LOG" ' src/dockerfile' 'and the tag comes back with it' unset MAPBOX_TEST_CURL_LOG DISABLE_TELEMETRY MAPBOX_CLI_INSTALL_SOURCE -start 'MAPBOX_CLI_NO_TELEMETRY is honoured, and outranks the old name' +start 'MAPBOX_CLI_NO_TELEMETRY is honored, and outranks the old name' new_case_env telemetry-new-name export MAPBOX_INSTALL_TILESETS=no export MAPBOX_CLI_INSTALL_SOURCE=dockerfile @@ -666,7 +666,7 @@ shim curl-recording curl CURL_LOG="${CASE_DIR}/curl-args" export MAPBOX_TEST_CURL_LOG="$CURL_LOG" -# The documented name, which the binary reads and this script honours too. +# The documented name, which the binary reads and this script honors too. : >"$CURL_LOG" export MAPBOX_CLI_NO_TELEMETRY=1 run_piped && status=0 || status=$? @@ -1026,7 +1026,7 @@ expect_no_out 'is not installed' 'says nothing else about it' expect_no_out 'Install it as well?' 'does not ask' expect_no_out 'fake pipx' 'installs nothing' -start 'MAPBOX_TILESETS_CLI is honoured, set or broken' +start 'MAPBOX_TILESETS_CLI is honored, set or broken' new_case_env tilesets-override shim pipx mkdir -p "${CASE_DIR}/opt" diff --git a/src/agent_skills.rs b/src/agent_skills.rs index 259edd3..63b45a4 100644 --- a/src/agent_skills.rs +++ b/src/agent_skills.rs @@ -1,6 +1,6 @@ //! `mapbox agent-skills` — install the published Mapbox Agent Skills. //! -//! Not to be confused with its neighbour. [`crate::generate_skills`] writes a +//! Not to be confused with its neighbor. [`crate::generate_skills`] writes a //! skill describing *this CLI's own commands*, rendered from the specs //! compiled into the binary. This command installs the *hand-written Mapbox //! domain skills* — cartography, token security, iOS and Android patterns, @@ -1210,7 +1210,7 @@ mod tests { encoder.finish().expect("finish gzip") } - const SKILL_MD: &[u8] = b"---\nname: mapbox-cartography\ndescription: Map design, colour and type. Use when styling.\n---\n\n# Cartography\n"; + const SKILL_MD: &[u8] = b"---\nname: mapbox-cartography\ndescription: Map design, color and type. Use when styling.\n---\n\n# Cartography\n"; fn one_skill() -> Vec { archive(&[ @@ -1252,7 +1252,7 @@ mod tests { assert_eq!(skill.name, "mapbox-cartography"); assert_eq!( skill.description.as_deref(), - Some("Map design, colour and type. Use when styling.") + Some("Map design, color and type. Use when styling.") ); let paths: Vec = skill diff --git a/src/auth.rs b/src/auth.rs index fd577b7..2695298 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -947,7 +947,7 @@ impl Identity<'_> { Some("pk") => "public (pk)", Some("sk") => "secret (sk)", Some("tk") => "temporary (tk)", - _ => "unrecognised prefix", + _ => "unrecognized prefix", }; match self.expires_at { Some(exp) => format!("{usage}, expires {}", time_until(exp, now)), @@ -1062,7 +1062,7 @@ fn rejected_token( ), "TokenInvalid" => ( "token_invalid", - "Mapbox does not recognise the token it would be given.".to_string(), + "Mapbox does not recognize the token it would be given.".to_string(), ), "TokenExpired" => ( "token_expired", @@ -1190,7 +1190,7 @@ fn profile_name(profile: Option<&str>) -> &str { /// token in order to report that it would spend the refresh token is the /// exact mutation the flag exists to avoid — and it takes no /// [`CredentialLock`] either, since it writes nothing there is anything to -/// serialise against. +/// serialize against. /// /// A precondition the real command would fail on is still a failure here: /// `refresh` with nothing stored reports what `force_refresh` reports rather @@ -2195,7 +2195,7 @@ mod tests { assert_eq!(token_usage("pk.body.signature"), Some("pk")); assert_eq!(token_usage("sk.body.signature"), Some("sk")); assert_eq!(token_usage("tk.body.signature"), Some("tk")); - // Anything else is reported as unrecognised rather than guessed at. + // Anything else is reported as unrecognized rather than guessed at. assert_eq!(token_usage("xx.body.signature"), None); assert_eq!(token_usage("pk."), None); assert_eq!(token_usage("nodotsatall"), None); @@ -2515,7 +2515,7 @@ mod tests { ); assert!( elapsed < Duration::from_secs(5), - "waited {elapsed:?}, so the deadline is not being honoured" + "waited {elapsed:?}, so the deadline is not being honored" ); } diff --git a/src/completion.rs b/src/completion.rs index f70076c..0a33158 100644 --- a/src/completion.rs +++ b/src/completion.rs @@ -114,7 +114,7 @@ pub fn warn_output_ignored(matches: &ArgMatches) { return; } eprintln!( - "Warning: `--{}` is not honoured by `{COMMAND}` — a completion script is a shell \ + "Warning: `--{}` is not honored by `{COMMAND}` — a completion script is a shell \ script, and wrapping it in an envelope would leave it unsourceable.", crate::output::ARG ); @@ -155,7 +155,7 @@ pub fn run(app: &Command, matches: &ArgMatches) -> Result<()> { // the result, and there is no rendering of it that is not itself. let mut out = io::stdout().lock(); match out.write_all(&script).and_then(|()| out.flush()) { - // A reader that stopped reading is `head`'s ordinary behaviour, not a + // A reader that stopped reading is `head`'s ordinary behavior, not a // failure to report: the error envelope would be the only thing left // on a terminal after a command that did exactly what was asked. Err(e) if e.kind() == io::ErrorKind::BrokenPipe => Ok(()), diff --git a/src/confirm.rs b/src/confirm.rs index e0ad812..3edfb43 100644 --- a/src/confirm.rs +++ b/src/confirm.rs @@ -10,7 +10,7 @@ //! out unasked. //! //! `--yes` says the answer is yes before it is asked, which is the only way a -//! caller *at* a terminal can get the non-interactive behaviour on purpose. +//! caller *at* a terminal can get the non-interactive behavior on purpose. //! //! The question is deliberately not `--output`-aware. It goes to stderr as //! prose in both modes, because the only way to see it is to be a person at a diff --git a/src/executor.rs b/src/executor.rs index 34ff195..f9d9192 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -1301,7 +1301,7 @@ fn suggested_extension(content_type: &str) -> &'static str { } /// Writes raw bytes to stdout, refusing to do so when that is a terminal — -/// `curl`'s behaviour, and for the same reason: a few hundred KB of PNG will +/// `curl`'s behavior, and for the same reason: a few hundred KB of PNG will /// otherwise scramble the user's shell. fn write_binary(body: &[u8], content_type: &str) -> Result<()> { use std::io::{IsTerminal, Write}; diff --git a/src/generate_skills.rs b/src/generate_skills.rs index 28eb90f..4936e0c 100644 --- a/src/generate_skills.rs +++ b/src/generate_skills.rs @@ -159,7 +159,7 @@ pub fn run(app: &Command, specs: &[ServiceSpec], matches: &ArgMatches, mode: Mod /// The services to describe, or `None` for all of them. /// -/// An unrecognised name is an error naming the alternatives rather than a +/// An unrecognized name is an error naming the alternatives rather than a /// silent no-op: `--service style` is a typo, and writing a skill that /// describes nothing is a worse answer to it than refusing. fn requested_services(schema: &Schema, matches: &ArgMatches) -> Result>> { @@ -856,7 +856,7 @@ const CELL_BUDGET: usize = 220; /// fit, and `crate::first_sentence` would cut `--output`'s at the `.` in /// "Output format." and throw away everything that says what the formats /// are. So the cut applies by length, to the text that would overflow. -fn summarise(text: &str) -> String { +fn summarize(text: &str) -> String { let whole = cell(text); if whole.len() <= CELL_BUDGET { return whole; @@ -873,7 +873,7 @@ fn describe(arg: &Argument) -> String { let mut parts: Vec = vec![]; if let Some(description) = arg.description.as_deref() { - let summary = summarise(description); + let summary = summarize(description); if !summary.is_empty() { let ends_a_sentence = summary.ends_with('.') || summary.ends_with('!'); parts.push(if ends_a_sentence { @@ -1141,7 +1141,7 @@ fn split_example(line: &str) -> Vec { /// /// So a heading in embedded prose is demoted to sit below the command's. /// Lines inside a fenced block are left exactly as they are — a `#` there is -/// a shell comment or a colour, and rewriting one would corrupt the example +/// a shell comment or a color, and rewriting one would corrupt the example /// it belongs to. fn demote_headings(text: &str) -> String { /// One level below the `###` a command heading uses. @@ -1763,7 +1763,7 @@ mod tests { /// Every service gets a reference page, and every page belongs to a /// service — including the three hand-written ones, which is where the - /// cross-cutting behaviour an agent trips over actually lives. + /// cross-cutting behavior an agent trips over actually lives. #[test] fn every_service_has_a_reference_page() { let (_, _, files) = generated(); @@ -1943,7 +1943,7 @@ mod tests { assert_eq!(demote_headings("#hashtag"), "#hashtag"); assert_eq!(demote_headings("plain prose"), "plain prose"); - // A `#` inside a fence is a comment or a colour, not a heading. + // A `#` inside a fence is a comment or a color, not a heading. let fenced = "```bash\n# curl it\n```\n### After"; assert_eq!( fenced.replace("### After", "#### After"), @@ -2099,8 +2099,8 @@ mod tests { // A cell is escaped exactly once, whichever branch produced it. assert!(!describe(types).contains("\\\\|")); - assert_eq!(summarise("a | b"), "a \\| b"); - assert_eq!(summarise("one\ntwo"), "one two"); + assert_eq!(summarize("a | b"), "a \\| b"); + assert_eq!(summarize("one\ntwo"), "one two"); // And a help string that already says "repeatable" does not say it // twice. diff --git a/src/http.rs b/src/http.rs index ea1a636..26cd087 100644 --- a/src/http.rs +++ b/src/http.rs @@ -164,7 +164,7 @@ fn environment_timeout() -> Option { /// /// `--yes`'s `FalseyValueParser` is no help here either, and it is worth /// saying why the two switches take different routes. That parser works -/// because a boolean can read everything it does not recognise as one of its +/// because a boolean can read everything it does not recognize as one of its /// two answers. A duration has no such reading: there is no number that /// `sideways` obviously meant. So this warns and falls back, the way /// `MAPBOX_OUTPUT` does, and what it falls back to is the default rather than @@ -322,7 +322,7 @@ mod tests { /// The point is the second half. `reqwest` supplies thirty seconds to a /// builder that names none, so before this module named one, every Mapbox /// request ran under a number nothing here had chosen — and a `reqwest` - /// upgrade that moved it would have moved this CLI's behaviour with no + /// upgrade that moved it would have moved this CLI's behavior with no /// line of this repo in the diff. Now the number is ours, and this fails /// if it ever silently becomes theirs again. #[test] diff --git a/src/main.rs b/src/main.rs index fb6f480..cf56b23 100644 --- a/src/main.rs +++ b/src/main.rs @@ -459,7 +459,7 @@ fn build_app(specs: &[ServiceSpec]) -> Command { // — and `MAPBOX_TIMEOUT=30s` would each be a usage error on // every command, including the ones needed to recover. // `--yes`'s `FalseyValueParser` does not rescue this one: a - // boolean can read anything it does not recognise as one of + // boolean can read anything it does not recognize as one of // its two answers, and a duration has no such reading. // `http::read_timeout` reads the variable by hand instead, // warns about a value it cannot use, and falls back to the @@ -576,7 +576,7 @@ fn build_app(specs: &[ServiceSpec]) -> Command { // service arm. app = app.subcommand(generate_skills::command()); - // Beside `generate-skills` because the two are neighbours a reader will + // Beside `generate-skills` because the two are neighbors a reader will // want to tell apart: that one writes a skill describing this CLI, this // one installs the published Mapbox domain skills. Both write into the // same agent directories, which is why they share `skill_dest`. @@ -1015,7 +1015,7 @@ fn run(app: &Command, specs: &[ServiceSpec], matches: &ArgMatches, mode: Mode) - generate_skills::run(app, specs, skills_matches, mode)? } // Ahead of the generic service arm for the same reason as its - // neighbours: it makes no Mapbox request and needs no token. The one + // neighbors: it makes no Mapbox request and needs no token. The one // request it does make is to GitHub for a public tarball. Some((agent_skills::COMMAND, skills_matches)) => agent_skills::run( skills_matches, diff --git a/src/output.rs b/src/output.rs index 07bd234..98d1565 100644 --- a/src/output.rs +++ b/src/output.rs @@ -85,7 +85,7 @@ impl Mode { /// that cannot be arranged in a test, and every interesting case here is /// a combination of the two. /// - /// An unrecognised `requested` is treated as `auto`. Clap rejects those + /// An unrecognized `requested` is treated as `auto`. Clap rejects those /// before they reach us for both the flag and `MAPBOX_OUTPUT`; the one /// caller that can pass one is [`Mode::early`], which reads argv and the /// environment itself, before clap has had a chance to complain. @@ -143,7 +143,7 @@ impl Mode { /// `MAPBOX_OUTPUT`, if it holds anything at all. /// -/// An unrecognised value is passed through to [`Mode::resolve`], which falls +/// An unrecognized value is passed through to [`Mode::resolve`], which falls /// back to `auto` — but it is worth saying so, since the caller plainly meant /// something by it. fn environment_request() -> Option { @@ -718,7 +718,7 @@ fn feature_collection_rows(value: &Value) -> Option> { /// `Null` is what all three row-builders — [`search_result_row`], /// [`feature_row`], [`tilequery_row`] — answer for a feature with no /// `properties` at all. An object with no fields is the same failure one step -/// later: `properties` was there and held nothing that builder recognised. +/// later: `properties` was there and held nothing that builder recognized. /// Both fall back the same way, because a numbered `(unnamed)` with no lines /// under it looks like a result that is genuinely blank rather than like a /// shape this renderer does not understand. @@ -751,7 +751,7 @@ fn render_geocoder_list(value: &Value) -> Option { /// /// Note the asymmetry with [`render_batch_feature_list`], which refuses to /// render at all when a second key sits beside `batch`. There the extra key -/// would be *lost* by rendering only what is recognised, so bailing out to +/// would be *lost* by rendering only what is recognized, so bailing out to /// JSON is how nothing goes missing; here the extra key is the thing being /// carried through, so there is nothing to bail out for. fn attribution(value: &Value) -> Option<&str> { @@ -1508,7 +1508,7 @@ pub fn emit_error(mode: Mode, err: &anyhow::Error) { } // The message is only ever one field of the body; print the rest // when there is a rest, so a person loses nothing that the old - // dump-the-body behaviour showed them. + // dump-the-body behavior showed them. if let Some(body) = e.body.as_ref().filter(|b| adds_detail(b)) { if let Ok(pretty) = serde_json::to_string_pretty(body) { eprintln!("{pretty}"); @@ -1552,7 +1552,7 @@ fn print_tips(tips: &[String]) { } } -/// A labelled group of lines on stderr. Nothing for an empty list, so the +/// A labeled group of lines on stderr. Nothing for an empty list, so the /// caller needs no guard. fn eprint_labelled(label: &str, values: &[String]) { for line in labelled_lines(label, values) { @@ -1862,7 +1862,7 @@ mod tests { } /// An empty row is the same failure as a missing `properties` object one - /// step later — the feature was there and nothing in it was recognised — + /// step later — the feature was there and nothing in it was recognized — /// and has to fall back the same way rather than print a numbered /// `(unnamed)` with nothing under it. `feature_collection_rows` and /// `tilequery_feature_rows` guard this; search was still checking only @@ -2259,7 +2259,7 @@ mod tests { } /// An empty row is the same failure as a missing `properties` object, one - /// step later: the feature was there and nothing in it was recognised. + /// step later: the feature was there and nothing in it was recognized. /// Both have to fall the whole collection back to JSON — a numbered /// `(unnamed)` with no lines under it reads as a result that is genuinely /// blank rather than as a shape this renderer does not understand. diff --git a/src/skill_dest.rs b/src/skill_dest.rs index 03f90da..d51dbc3 100644 --- a/src/skill_dest.rs +++ b/src/skill_dest.rs @@ -630,7 +630,7 @@ pub fn from_matches( ) -> Result> { let dir = matches.get_one::(DIR_ARG).map(PathBuf::as_path); // Clap has already refused anything that is not one of the known - // spellings, so an unrecognised value here is impossible rather than + // spellings, so an unrecognized value here is impossible rather than // ignored. let requested: Vec = matches .get_many::(AGENT_ARG) @@ -1069,7 +1069,7 @@ mod tests { ); } - /// `$XDG_CONFIG_HOME` is honoured on every platform, and falls back to + /// `$XDG_CONFIG_HOME` is honored on every platform, and falls back to /// `~/.config` rather than to the OS config directory. // `env::set_var` is unsafe because another thread may be reading the // environment. This test is the only reader inside itself, it restores diff --git a/src/spec.rs b/src/spec.rs index e5001bb..cc797d4 100644 --- a/src/spec.rs +++ b/src/spec.rs @@ -296,7 +296,7 @@ const COMMAND_ALIASES: &[(&str, &str, &str, bool)] = &[]; /// group, as `mapbox styles draft get`. /// /// Absent from everything in `custom-openapi/`, which the decision record -/// does not cover. Those keep the pre-extension behaviour: their file's own +/// does not cover. Those keep the pre-extension behavior: their file's own /// service name, and one flat generated command name. const CLI_COMMAND_EXTENSION: &str = "x-mapbox-cli-command"; @@ -550,7 +550,7 @@ pub enum Numeric { /// `name` is no longer necessarily a service anyone can type. /// [`CLI_COMMAND_EXTENSION`] decides that per operation, so this is the /// file's own name: the fallback service for an operation that declares no -/// target, the key `WITHHELD_OPERATIONS` and its neighbours match on, and +/// target, the key `WITHHELD_OPERATIONS` and its neighbors match on, and /// what a maintainer-only drift check compares the two tables by. `maps` is the /// clearest case — the entry is still called that, and the service it used /// to produce is gone. @@ -1601,7 +1601,7 @@ paths: } /// The declared type is what the executor puts on the wire, so it has to - /// survive parsing exactly — not be normalised into a guess. + /// survive parsing exactly — not be normalized into a guess. #[test] fn a_raw_body_keeps_the_media_type_the_spec_wrote() { let svc = service(BODIES); diff --git a/src/tilesets_cli.rs b/src/tilesets_cli.rs index 69add8c..a9ffbf1 100644 --- a/src/tilesets_cli.rs +++ b/src/tilesets_cli.rs @@ -130,14 +130,14 @@ where /// /// Only a value typed on this command line is worth a warning. Under `auto` /// nothing was asked for, and the child does its own terminal detection -/// anyway — which is exactly the behaviour we would want. `MAPBOX_OUTPUT` is +/// anyway — which is exactly the behavior we would want. `MAPBOX_OUTPUT` is /// excluded for the same reason: it is exported once and applies to /// everything, so warning on it would nag on every tileset upload forever. pub fn warn_output_ignored(matches: &ArgMatches) { let explicit = matches.value_source(crate::output::ARG) == Some(ValueSource::CommandLine); if explicit { eprintln!( - "Warning: `--{}` is not honoured by `{COMMAND}` — its output comes from \ + "Warning: `--{}` is not honored by `{COMMAND}` — its output comes from \ `{DEFAULT_BINARY}`, which has no equivalent option.", crate::output::ARG ); @@ -170,7 +170,7 @@ pub fn warn_yes_ignored(matches: &ArgMatches) { return; } eprintln!( - "Warning: `--{}` is not honoured by `{COMMAND}` — `{DEFAULT_BINARY}` asks its \ + "Warning: `--{}` is not honored by `{COMMAND}` — `{DEFAULT_BINARY}` asks its \ own questions and has its own `-f`/`--force` on the commands that do.", crate::confirm::ARG ); @@ -597,7 +597,7 @@ mod tests { use super::*; use std::sync::Mutex; - /// Serialises the tests that have to mutate the process environment. + /// Serializes the tests that have to mutate the process environment. static ENV_LOCK: Mutex<()> = Mutex::new(()); /// Names the proxy treats as carrying a token. @@ -609,7 +609,7 @@ mod tests { /// Token resolution reads the environment, so without this these tests /// would pass or fail depending on whether the developer happens to export /// a token — the exact ambient dependency the tests exist to pin down. - /// Serialised because the environment is process-global. Assertions belong + /// Serialized because the environment is process-global. Assertions belong /// *outside* the closure so a failure still restores it. fn with_token_env(vars: &[(&str, &str)], body: impl FnOnce() -> T) -> T { let _guard = ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner()); @@ -860,7 +860,7 @@ mod tests { assert!(!consulted); } - /// Nothing stored to honour. `main` turns this into an error rather than + /// Nothing stored to honor. `main` turns this into an error rather than /// quietly handing over the environment token the flag asked to ignore. #[test] fn use_login_with_nothing_stored_resolves_to_nothing() { diff --git a/src/update_check.rs b/src/update_check.rs index 57bf679..f503787 100644 --- a/src/update_check.rs +++ b/src/update_check.rs @@ -231,7 +231,7 @@ fn should_refresh(cache: Option<&Cache>, now: u64) -> bool { /// meant to be acted on, which is what makes forging it worth more than noise /// on stderr. /// -/// So the shape is restricted rather than the content sanitised: ASCII +/// So the shape is restricted rather than the content sanitized: ASCII /// alphanumerics, `.`, `-` and `+`, bounded. That admits `0.2.0` and /// `0.1.3-dev.abc1234`, which is everything the channel publishes, and admits /// no character that could begin a line or move a cursor. @@ -349,7 +349,7 @@ pub fn is_refresh_child() -> bool { /// could fail would only be a way for this feature to produce a broken pipe /// or a stray line on somebody's terminal. /// -/// The opt-outs are honoured here too, not only in [`notify`] which spawned +/// The opt-outs are honored here too, not only in [`notify`] which spawned /// it. Belt and braces: this is the process that makes the request, and the /// switch that says "make no request" should be read by it. pub fn run_refresh_child() -> ExitCode { @@ -681,12 +681,12 @@ mod tests { /// next, so its field names are a contract with a file already on disk. #[test] fn the_cache_round_trips_and_tolerates_a_partial_one() { - let written = serde_json::to_string(&cache(1, 2, "0.2.0")).expect("serialise"); + let written = serde_json::to_string(&cache(1, 2, "0.2.0")).expect("serialize"); assert_eq!( written, r#"{"checked_at":1,"notified_at":2,"latest":"0.2.0"}"# ); - let read: Cache = serde_json::from_str(&written).expect("deserialise"); + let read: Cache = serde_json::from_str(&written).expect("deserialize"); assert_eq!(read, cache(1, 2, "0.2.0")); // A file written before a field existed, and one written after a diff --git a/tests/non_interactive.rs b/tests/non_interactive.rs index 3d41282..96f058b 100644 --- a/tests/non_interactive.rs +++ b/tests/non_interactive.rs @@ -177,7 +177,7 @@ type YesSpelling<'a> = (&'a str, &'a [&'a str], &'a [(&'a str, &'a str)]); /// `--yes` must NOT start a login that cannot finish. /// /// It used to. The flag's documented purpose is to give a caller "the CI -/// behaviour on purpose", so a job exports `MAPBOX_YES=1` to stop its deletes +/// behavior on purpose", so a job exports `MAPBOX_YES=1` to stop its deletes /// blocking — and that silently opted `auth login` back into the browser flow: /// past `config_dir`, into a real dynamic client registration, then five /// minutes of `CALLBACK_TIMEOUT` waiting for a callback nobody would send. One @@ -201,7 +201,7 @@ fn yes_does_not_buy_a_login_a_terminal() { "interactive_required", "{extra:?} {env:?} let the login start" ); - // The blocked store is a second line of defence that should never be + // The blocked store is a second line of defense that should never be // reached: its message means `config_dir` already ran, and `config_dir` // creates the directory it checks. assert!( @@ -420,11 +420,11 @@ fn a_delete_at_a_terminal_asks_before_it_sends() { "EOF at the prompt must cancel, in the shape the caller asked for: {seen}" ); - // And nothing was sent: a cancelled delete writes no result at all. Read + // And nothing was sent: a canceled delete writes no result at all. Read // from the file rather than `seen`, which has the two streams joined. let written = std::fs::read_to_string(&out_path).unwrap_or_default(); assert!( written.trim().is_empty(), - "a cancelled delete wrote to stdout: {written:?}" + "a canceled delete wrote to stdout: {written:?}" ); } diff --git a/tests/output_contract.rs b/tests/output_contract.rs index 4fc8b3c..805465c 100644 --- a/tests/output_contract.rs +++ b/tests/output_contract.rs @@ -141,7 +141,7 @@ fn a_file_where_the_credential_directory_goes_fails_before_the_browser() { /// the missing path parameter is caught after credentials resolve. /// /// Unix only, and it is the redirection that does not port rather than the -/// behaviour: this case has to leave `MAPBOX_CONFIG_DIR` unset so the default +/// behavior: this case has to leave `MAPBOX_CONFIG_DIR` unset so the default /// `~/.mapbox` is what gets probed, and on Windows that default comes from /// `FOLDERID_Profile` — `HOME` is not read, so there is no way to point the /// default at a directory where a test may plant a file. Unlike the case @@ -338,7 +338,7 @@ fn text_mode_still_prints_claps_own_tip() { ); } -/// `-o` written on a line clap rejects still has to be honoured — that is +/// `-o` written on a line clap rejects still has to be honored — that is /// the case `requested_in_argv` exists for. #[test] fn an_explicit_mode_survives_a_line_clap_could_not_parse() { @@ -719,7 +719,7 @@ fn a_failure_carries_a_fix_a_command_and_a_page() { ["https://docs.mapbox.com/api/accounts/tokens/"] ); - // The same three, labelled, for the person at a terminal. + // The same three, labeled, for the person at a terminal. let text = command() .args(UNUSED_PROFILE) .args(["-o", "text", "auth", "whoami"]) diff --git a/tests/proxy.rs b/tests/proxy.rs index dc08670..f8755d6 100644 --- a/tests/proxy.rs +++ b/tests/proxy.rs @@ -2,13 +2,13 @@ //! //! Nothing in `src/` mentions a proxy: `http::build` never calls //! `.no_proxy()`, so `reqwest`'s own system-proxy detection applies and -//! `HTTP_PROXY` / `HTTPS_PROXY` / `ALL_PROXY` / `NO_PROXY` are honoured. That -//! is an important behaviour for anyone on a corporate network and it rests +//! `HTTP_PROXY` / `HTTPS_PROXY` / `ALL_PROXY` / `NO_PROXY` are honored. That +//! is an important behavior for anyone on a corporate network and it rests //! entirely on a library default nobody here chose — a single `.no_proxy()` //! added to fix something else would remove it, and no existing test would //! notice. //! -//! So these tests assert the behaviour rather than the absence of a call. A +//! So these tests assert the behavior rather than the absence of a call. A //! grep for `no_proxy` would pass just as well if the client were rebuilt //! somewhere else, or if a future `reqwest` changed its default. //! @@ -18,7 +18,7 @@ //! asked for. Proving a *negative* — that `HTTP_PROXY` alone does not carry an //! https request, or that `NO_PROXY` exempts a host — means the request goes //! to the real API instead, so those tests would put the internet in the suite -//! to assert behaviour that belongs to `reqwest` rather than to this crate. +//! to assert behavior that belongs to `reqwest` rather than to this crate. //! They are in `docs/commands.md` as prose instead. use std::io::{Read, Write}; @@ -124,7 +124,7 @@ fn accept_within( /// /// `env_remove` rather than `env_clear`, matching `tests/update_check.rs`. /// Clearing takes `SystemRoot` with it on Windows, and without that the -/// socket and TLS stacks cannot initialise — so the CLI failed before it +/// socket and TLS stacks cannot initialize — so the CLI failed before it /// could reach any proxy, which is what left the earlier version of this /// test waiting for a connection that was never going to come. fn mapbox() -> Command { @@ -157,7 +157,7 @@ fn http_proxy(stream: &mut TcpStream) -> String { .to_string() } -/// **The behaviour this file exists for.** An `HTTPS_PROXY` in the environment +/// **The behavior this file exists for.** An `HTTPS_PROXY` in the environment /// is used, and the proxy is asked for the host the CLI was going to reach. #[test] fn an_https_proxy_in_the_environment_is_used() { diff --git a/tests/schema_contract.rs b/tests/schema_contract.rs index b7b6924..ab710fc 100644 --- a/tests/schema_contract.rs +++ b/tests/schema_contract.rs @@ -197,7 +197,7 @@ fn every_answer_carries_the_globals() { /// parser's twelve spellings as the argument's possible values, and publishing /// them told an agent `--yes` accepts `1`, `on` or `false` — none of which it /// does. `mapbox --yes=1` is a usage error, which is exactly the gap between -/// promise and behaviour this file exists to close. +/// promise and behavior this file exists to close. #[test] fn a_flag_promises_no_values_because_it_takes_none() { let value = schema(&["styles", "delete", "--schema"]); diff --git a/tests/source_guards.rs b/tests/source_guards.rs index 5dc9ae3..6de0ddd 100644 --- a/tests/source_guards.rs +++ b/tests/source_guards.rs @@ -163,7 +163,7 @@ fn only_output_completion_and_binary_responses_write_to_stdout() { assert!( unexpected.is_empty(), "these modules reach for stdout directly:\n {}\n\n\ - A result goes through `output::emit`, which is where `--output` is honoured; \ + A result goes through `output::emit`, which is where `--output` is honored; \ anything else belongs on stderr through `output::progress`. If a command really \ does own its bytes — as `completion` does — add it to MAY_WRITE_STDOUT with the \ reason.", From 51d4337afc612d7bcc0d5e734015cf31b5514b09 Mon Sep 17 00:00:00 2001 From: Matthew Podwysocki Date: Tue, 15 Sep 2026 15:41:02 -0400 Subject: [PATCH 2/2] Make the spelling a test, not a convention MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CONTRIBUTING.md now says to write American English, and `prose_is_american_english` in tests/source_guards.rs holds it. The argument is the one that file's neighbours already make: the section this lands in lists four rules the compiler holds "rather than a reviewer", and a spelling nobody is checking is exactly the kind of thing that drifts across 27 files before anyone notices. It earned its place immediately. The sweep in the previous commit used a word-boundary regex, and this guard found sixteen lines it had missed, all of them real: - `eprint_labelled` and `labelled_lines` in output.rs, plus five test names around them — identifiers, where `\b` does not match inside `labelled_lines` - `mislabelled` and `Serialising`, where the British word is a prefix or suffix of a longer one - `"unserialisable error"`, which is a message the CLI prints - `Honouring` at the start of a sentence Two things are skipped deliberately. Fenced code blocks, because sample output and captured API responses are quoted rather than written and rewriting a word inside one would make the document misquote its source — docs/commands.md is full of both, and a British place name in a geocoding result is not a misspelling. And `cancelled`, which is a compatibility promise rather than a spelling, with the reason on the constant. The table of spellings to avoid is itself a list of spellings to avoid, so the check skips a line that is nothing but two quoted strings and a comma. Narrow enough to excuse a data row and not a sentence. Verified both ways rather than trusting a green run: a British spelling in prose fails, naming `src/output.rs:88`, and the same word inside a fenced block passes. No changelog entry. Error *message* text is not one of the promises CONTRIBUTING.md lists — command names, flags, the output modes and the exit codes are. 597 tests, fmt and clippy clean. --- CONTRIBUTING.md | 10 +++ src/executor.rs | 2 +- src/output.rs | 28 ++++---- src/tilesets_cli.rs | 2 +- tests/source_guards.rs | 146 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 172 insertions(+), 16 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f2a1a69..49d7b78 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -44,6 +44,16 @@ Four rules the compiler holds rather than a reviewer, declared in (stdout belongs to `output::emit`, the single place `--output` is honored), no `dbg!`, no `todo!`/`unimplemented!`. +Write American English: comments, documentation, commit messages, and every +string the CLI prints. `prose_is_american_english` in +`tests/source_guards.rs` reads the files and fails on the British spelling, +so this is a test rather than something a reviewer has to notice, and the +failure names the file and the line. It leaves two things alone on purpose. +Fenced code blocks, because sample output and captured API responses are +quoted rather than written, and rewriting a word inside one would make the +document misquote its source. And the `cancelled` error code, which is a +compatibility promise scripts may match on rather than a spelling. + ## Where the commands come from Commands are generated at build time from the OpenAPI specs in `openapi/`, diff --git a/src/executor.rs b/src/executor.rs index f9d9192..309a6d2 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -1268,7 +1268,7 @@ fn is_binary_content_type(content_type: &str) -> bool { /// /// Guessing from the content type rather than always saying `.png`: telling /// someone to redirect a glyph range into `out.png` is advice that produces -/// a mislabelled file, and it reads as though the command misunderstood what +/// a mislabeled file, and it reads as though the command misunderstood what /// it fetched. fn suggested_extension(content_type: &str) -> &'static str { let essence = content_type diff --git a/src/output.rs b/src/output.rs index 98d1565..b06a953 100644 --- a/src/output.rs +++ b/src/output.rs @@ -1491,11 +1491,11 @@ pub fn emit_error(mode: Mode, err: &anyhow::Error) { // wrapped error keeps the context that explains it. None => json!({ "code": GENERIC_CODE, "message": format!("{err:#}") }), }; - // Serialising a `json!` object cannot fail; fall back rather than + // Serializing a `json!` object cannot fail; fall back rather than // panic while already on the error path. let pretty = matches!(mode, Mode::Json { pretty: true }); let line = encode(&payload, pretty) - .unwrap_or_else(|_| r#"{"code":"error","message":"unserialisable error"}"#.to_string()); + .unwrap_or_else(|_| r#"{"code":"error","message":"unserializable error"}"#.to_string()); eprintln!("{line}"); return; } @@ -1524,8 +1524,8 @@ pub fn emit_error(mode: Mode, err: &anyhow::Error) { if let Some(request_id) = e.support_request_id() { eprintln!("Request ID: {request_id} (quote this to Mapbox support)"); } - eprint_labelled("Next", &e.next_actions); - eprint_labelled("Docs", &e.docs); + eprint_labeled("Next", &e.next_actions); + eprint_labeled("Docs", &e.docs); } None => eprintln!("Error: {err:#}"), } @@ -1554,8 +1554,8 @@ fn print_tips(tips: &[String]) { /// A labeled group of lines on stderr. Nothing for an empty list, so the /// caller needs no guard. -fn eprint_labelled(label: &str, values: &[String]) { - for line in labelled_lines(label, values) { +fn eprint_labeled(label: &str, values: &[String]) { + for line in labeled_lines(label, values) { eprintln!("{line}"); } } @@ -1565,7 +1565,7 @@ fn eprint_labelled(label: &str, values: &[String]) { /// `Next: mapbox styles list` reads as one thing; a second `Next:` on /// the line below reads as two unrelated ones. Continuation lines are /// indented to the label's width instead. -fn labelled_lines(label: &str, values: &[String]) -> Vec { +fn labeled_lines(label: &str, values: &[String]) -> Vec { values .iter() .enumerate() @@ -1605,8 +1605,8 @@ mod tests { /// Two commands under one `Next:` have to read as two commands, not as /// one wrapped line and not as two unrelated labels. #[test] - fn a_second_labelled_line_is_aligned_under_the_first() { - let lines = labelled_lines( + fn a_second_labeled_line_is_aligned_under_the_first() { + let lines = labeled_lines( "Next", &[ "mapbox styles list-styles".to_string(), @@ -1621,7 +1621,7 @@ mod tests { " mapbox auth whoami" ] ); - assert!(labelled_lines("Next", &[]).is_empty()); + assert!(labeled_lines("Next", &[]).is_empty()); } #[test] @@ -1868,7 +1868,7 @@ mod tests { /// `tilequery_feature_rows` guard this; search was still checking only /// for `Null`. #[test] - fn a_search_feature_with_nothing_recognisable_falls_back_to_json() { + fn a_search_feature_with_nothing_recognizable_falls_back_to_json() { for properties in ["{}", r#"{"mapbox_id":"opaque-id-nobody-reads"}"#] { let fc = rows(&format!( r#"{{"type":"FeatureCollection","features":[{{"properties":{properties}}}]}}"# @@ -2264,7 +2264,7 @@ mod tests { /// `(unnamed)` with no lines under it reads as a result that is genuinely /// blank rather than as a shape this renderer does not understand. #[test] - fn a_feature_with_no_recognised_properties_falls_back_to_json() { + fn a_feature_with_no_recognized_properties_falls_back_to_json() { for properties in ["{}", r#"{"mapbox_id":"opaque-id-nobody-reads"}"#] { let fc = rows(&format!( r#"{{"type":"FeatureCollection","features":[{{"properties":{properties}}}]}}"# @@ -2852,7 +2852,7 @@ mod tests { } #[test] - fn an_unrecognised_value_falls_back_to_auto() { + fn an_unrecognized_value_falls_back_to_auto() { assert_eq!(Mode::resolve("", true), Mode::Text); assert_eq!(Mode::resolve("yaml", false), Mode::Json { pretty: false }); } @@ -2862,7 +2862,7 @@ mod tests { } #[test] - fn every_spelling_clap_accepts_is_recognised_on_argv() { + fn every_spelling_clap_accepts_is_recognized_on_argv() { for line in [ &["mapbox", "--output", "json", "styles", "list"][..], &["mapbox", "--output=json", "styles", "list"][..], diff --git a/src/tilesets_cli.rs b/src/tilesets_cli.rs index a9ffbf1..d4e9058 100644 --- a/src/tilesets_cli.rs +++ b/src/tilesets_cli.rs @@ -121,7 +121,7 @@ where /// Warns that `--output` stops at this process. /// /// The proxy hands argv to a third-party binary that implements none of this -/// CLI's output contract. Honouring the flag would mean either mirroring the +/// CLI's output contract. Honoring the flag would mean either mirroring the /// child's command surface to translate it per subcommand — the coupling this /// proxy exists to avoid — or capturing its stdio, which turns the child's /// stderr into a pipe and silently costs `upload-source` the progress bar diff --git a/tests/source_guards.rs b/tests/source_guards.rs index 6de0ddd..e12e961 100644 --- a/tests/source_guards.rs +++ b/tests/source_guards.rs @@ -314,3 +314,149 @@ fn every_telemetry_marker_is_disclosed() { sentence in README.md's Privacy section, then update this count." ); } + +/// British spellings, and the American spelling to use instead. +/// +/// Not a dictionary. This is the set that actually drifted into this +/// repository — 91 lines across 27 files before anyone noticed — so it is the +/// set most likely to come back. A word missing from here is not permission +/// for it; it is a word nobody has written yet. +/// +/// Each entry is a prefix, so one row covers a word's whole family: its +/// past tense, its plural and its adverb need no rows of their own. +/// +/// `cancelled` is deliberately absent. It is the machine-readable `code` in +/// the JSON error contract, documented in `docs/commands.md` and asserted in +/// `tests/non_interactive.rs`, which makes it a compatibility promise rather +/// than a spelling. Renaming it is a breaking change for anyone matching on +/// it, and that is somebody's decision rather than this test's. +const BRITISH: &[(&str, &str)] = &[ + ("acknowledgement", "acknowledgment"), + ("amongst", "among"), + ("analyse", "analyze"), + ("apologis", "apologiz"), + ("artefact", "artifact"), + ("behaviour", "behavior"), + ("catalogue", "catalog"), + ("centre", "center"), + ("colour", "color"), + ("criticis", "criticiz"), + ("defence", "defense"), + ("favour", "favor"), + ("honour", "honor"), + ("initialis", "initializ"), + ("judgement", "judgment"), + ("labelled", "labeled"), + ("labelling", "labeling"), + ("licence", "license"), + ("modelled", "modeled"), + ("modelling", "modeling"), + ("neighbour", "neighbor"), + ("normalis", "normaliz"), + ("organis", "organiz"), + ("prioritis", "prioritiz"), + ("programme", "program"), + ("recognis", "recogniz"), + ("sanitis", "sanitiz"), + ("serialis", "serializ"), + ("summaris", "summariz"), + ("utilis", "utiliz"), + ("whilst", "while"), +]; + +/// Every file whose prose is ours to write, as (path, contents). +/// +/// `openapi/` and `custom-openapi/` are left out. Their descriptions do reach +/// the user as help text, but the words are the API teams', mirrored from +/// upstream specs, and an edit here would not survive the next regenerate. +/// Both happen to be clean today; neither is ours to hold to this rule. +fn prose_files() -> Vec<(String, String)> { + let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + let mut out = vec![]; + + for name in [ + "README.md", + "CONTRIBUTING.md", + "CHANGELOG.md", + "SECURITY.md", + ] { + out.push(( + name.to_owned(), + std::fs::read_to_string(root.join(name)).expect("read a root document"), + )); + } + + for dir in ["src", "tests", "docs", "scripts"] { + for entry in std::fs::read_dir(root.join(dir)).expect("read a source directory") { + let path = entry.expect("a directory entry").path(); + let ours = path + .extension() + .is_some_and(|ext| ["rs", "md", "sh", "ps1"].iter().any(|k| ext == *k)); + if ours { + let name = format!( + "{dir}/{}", + path.file_name().expect("a file name").to_string_lossy() + ); + out.push((name, std::fs::read_to_string(&path).expect("read a file"))); + } + } + } + + out.sort(); + // The trap this guard could fall into is reading nothing and passing for + // it, so the count is asserted rather than assumed. + assert!(out.len() > 30, "prose_files found only {}", out.len()); + out +} + +/// A row of a two-column table of string literals, like `BRITISH`'s own. +/// +/// Without this the guard fails on itself: the table of spellings to avoid is +/// a list of spellings to avoid. Narrow on purpose — a line has to be nothing +/// but two quoted strings and a comma — so it excuses a data row and not a +/// sentence. +fn is_a_table_row(line: &str) -> bool { + let line = line.trim(); + line.starts_with("(\"") && line.ends_with("\"),") +} + +/// One spelling, everywhere, and American because Mapbox is. +/// +/// Fenced code blocks in Markdown are skipped: sample output and captured API +/// responses are quoted from somewhere else, and `docs/commands.md` is full of +/// both. A British place name in a captured geocoding result is not a +/// spelling mistake, and rewriting it would make the document misquote the +/// API. +#[test] +fn prose_is_american_english() { + let mut found = vec![]; + for (name, text) in prose_files() { + let markdown = name.ends_with(".md"); + let mut fenced = false; + for (index, line) in text.lines().enumerate() { + if markdown && line.trim_start().starts_with("```") { + fenced = !fenced; + continue; + } + if fenced || is_a_table_row(line) { + continue; + } + let lower = line.to_lowercase(); + for (british, american) in BRITISH { + if lower.contains(british) { + found.push(format!("{name}:{}: {british} -> {american}", index + 1)); + } + } + } + } + + assert!( + found.is_empty(), + "British spellings:\n {}\n\n\ + This repository writes American English, in comments and documentation \ + as well as in anything the CLI prints. If one of these is a proper noun \ + rather than a spelling — a place name in a fixture, say — move it inside \ + a fenced block or drop the word from BRITISH with the reason.", + found.join("\n ") + ); +}
Terminal — -o textAgent — -o json