Skip to content

Commit 46d96be

Browse files
feat(rust-sdk): ergonomic cargo options, nextest, ci(), cargo-hack (Py+TS parity) (#143)
1 parent 8f67126 commit 46d96be

16 files changed

Lines changed: 1539 additions & 98 deletions

File tree

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
"""Shared cargo-argument assembly for the Rust toolchain helper.
2+
3+
Turns structured options (features, packages, target, …) into a
4+
canonically-ordered, shell-safe cargo argument string. Every user-supplied
5+
*value* (package/exclude names, the joined feature list, target triple,
6+
profile name) is shell-quoted; the raw escape-hatch ``flags`` pass through
7+
verbatim because they are meant to be literal cargo arguments.
8+
"""
9+
10+
from __future__ import annotations
11+
12+
import shlex
13+
from dataclasses import dataclass
14+
15+
16+
@dataclass(frozen=True)
17+
class CargoOpts:
18+
"""Structured cargo invocation options. See the SDK design reference for
19+
the canonical token order each field lowers to."""
20+
21+
workspace: bool = False
22+
packages: tuple[str, ...] = ()
23+
exclude: tuple[str, ...] = ()
24+
all_features: bool = False
25+
no_default_features: bool = False
26+
features: tuple[str, ...] = ()
27+
target: str | None = None
28+
all_targets: bool = False
29+
release: bool = False
30+
profile: str | None = None
31+
locked: bool = True
32+
flags: tuple[str, ...] = ()
33+
34+
35+
def _validate(opts: CargoOpts) -> None:
36+
if opts.all_features and (opts.features or opts.no_default_features):
37+
msg = (
38+
"hm.rust: --all-features conflicts with features=/no_default_features=\n"
39+
f" observed: all_features=True, features={list(opts.features)!r}, "
40+
f"no_default_features={opts.no_default_features!r}\n"
41+
" → pass all_features=True alone, or list explicit features= "
42+
"without all_features"
43+
)
44+
raise ValueError(msg)
45+
if opts.release and opts.profile is not None:
46+
msg = (
47+
"hm.rust: release=True conflicts with profile=\n"
48+
f" observed: release=True, profile={opts.profile!r}\n"
49+
' → use profile="release" (identical effect) or drop one'
50+
)
51+
raise ValueError(msg)
52+
if opts.exclude:
53+
if opts.packages:
54+
msg = (
55+
"hm.rust: exclude= cannot combine with packages=\n"
56+
f" observed: packages={list(opts.packages)!r}, "
57+
f"exclude={list(opts.exclude)!r}\n"
58+
" → --exclude pairs with --workspace; packages= already selects "
59+
"explicitly, so drop one"
60+
)
61+
raise ValueError(msg)
62+
if not opts.workspace:
63+
msg = (
64+
"hm.rust: exclude= requires workspace=True\n"
65+
f" observed: exclude={list(opts.exclude)!r} without workspace=True\n"
66+
" → cargo --exclude only applies to --workspace; pass workspace=True"
67+
)
68+
raise ValueError(msg)
69+
70+
71+
def cargo_flags(opts: CargoOpts) -> str:
72+
"""Assemble the cargo argument middle (after the subcommand, before any
73+
``--`` passthrough). Returns a leading-space string, or ``""`` when empty.
74+
"""
75+
_validate(opts)
76+
toks: list[str] = []
77+
78+
# scope
79+
if opts.packages:
80+
toks += [f"-p {shlex.quote(p)}" for p in opts.packages]
81+
elif opts.workspace:
82+
toks.append("--workspace")
83+
toks += [f"--exclude {shlex.quote(e)}" for e in opts.exclude]
84+
85+
# target selection
86+
if opts.all_targets:
87+
toks.append("--all-targets")
88+
89+
# features
90+
if opts.all_features:
91+
toks.append("--all-features")
92+
else:
93+
if opts.no_default_features:
94+
toks.append("--no-default-features")
95+
if opts.features:
96+
toks.append("--features " + shlex.quote(",".join(opts.features)))
97+
98+
# target triple
99+
if opts.target is not None:
100+
toks.append(f"--target {shlex.quote(opts.target)}")
101+
102+
# profile / release
103+
if opts.profile is not None:
104+
toks.append(f"--profile {shlex.quote(opts.profile)}")
105+
elif opts.release:
106+
toks.append("--release")
107+
108+
# lockfile
109+
if opts.locked:
110+
toks.append("--locked")
111+
112+
# escape hatch — verbatim
113+
toks += list(opts.flags)
114+
115+
return (" " + " ".join(toks)) if toks else ""

0 commit comments

Comments
 (0)