1use std::path::PathBuf;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
4#[non_exhaustive]
5pub enum Edition {
6 E2015,
7 E2018,
8 E2021,
9 E2024,
10}
11
12impl Edition {
13 #[must_use]
14 pub const fn as_str(self) -> &'static str {
15 match self {
16 Self::E2015 => "2015",
17 Self::E2018 => "2018",
18 Self::E2021 => "2021",
19 Self::E2024 => "2024",
20 }
21 }
22}
23
24impl std::str::FromStr for Edition {
25 type Err = UnknownEdition;
26 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
27 Ok(match s {
28 "2015" => Self::E2015,
29 "2018" => Self::E2018,
30 "2021" => Self::E2021,
31 "2024" => Self::E2024,
32 other => return Err(UnknownEdition(other.to_owned())),
33 })
34 }
35}
36
37impl TryFrom<cargo_metadata::Edition> for Edition {
38 type Error = UnknownEdition;
39 fn try_from(e: cargo_metadata::Edition) -> std::result::Result<Self, Self::Error> {
40 e.as_str().parse()
41 }
42}
43
44#[derive(Debug, Clone, thiserror::Error)]
47#[error("unknown edition: {0}")]
48#[non_exhaustive]
49pub struct UnknownEdition(pub String);
50
51#[derive(Debug, Clone)]
52#[non_exhaustive]
53pub struct CrateUnit {
54 pub edition: Edition,
55 pub manifest_dir: PathBuf,
56 pub files: Vec<PathBuf>,
57 pub size_bytes: u64,
63}
64
65#[derive(Debug, Clone)]
71#[non_exhaustive]
72pub struct Batch {
73 pub edition: Edition,
74 pub units: Vec<CrateUnit>,
75}
76
77impl Batch {
78 #[must_use]
79 pub fn size_bytes(&self) -> u64 {
80 self.units.iter().map(|u| u.size_bytes).sum()
81 }
82
83 #[must_use]
84 pub fn file_count(&self) -> usize {
85 self.units.iter().map(|u| u.files.len()).sum()
86 }
87
88 #[must_use]
90 pub fn sort_key(&self) -> PathBuf {
91 self.units
92 .iter()
93 .map(|u| u.manifest_dir.clone())
94 .min()
95 .unwrap_or_default()
96 }
97}
98
99#[derive(Debug, Clone)]
102#[non_exhaustive]
103pub struct BatchFile {
104 pub file: PathBuf,
105 pub manifest_dir: PathBuf,
106}
107
108#[derive(Debug)]
109#[non_exhaustive]
110pub struct BatchResult {
111 pub sort_key: PathBuf,
112 pub stdout: Vec<u8>,
113 pub stderr: Vec<u8>,
114 pub exit_code: i32,
115 pub files: Vec<BatchFile>,
118}
119
120#[allow(clippy::struct_excessive_bools)]
124#[derive(Debug, Clone, Default)]
125#[non_exhaustive]
126pub struct Config {
127 pub manifest_path: Option<PathBuf>,
128 pub packages: Vec<String>,
129 pub all: bool,
132 pub check: bool,
133 pub workers: Option<usize>,
134 pub channel_capacity: Option<usize>,
135 pub rustfmt_args: Vec<String>,
136 pub batch_size: Option<usize>,
142 pub experimental_cache: bool,
148 pub warnings: bool,
152}
153
154#[derive(Debug)]
155#[non_exhaustive]
156pub struct Report {
157 pub failures: Vec<FileFailure>,
158 pub exit_code: i32,
159}
160
161#[derive(Debug)]
162#[non_exhaustive]
163pub struct FileFailure {
164 pub file: PathBuf,
165 pub manifest_dir: PathBuf,
166}
167
168#[derive(Debug, thiserror::Error)]
169#[non_exhaustive]
170pub enum Error {
171 #[error("`workers` must be >= 1 (got {0})")]
172 InvalidWorkers(usize),
173 #[error("cargo metadata failed: {0}")]
174 Metadata(#[from] cargo_metadata::Error),
175 #[error("io: {0}")]
176 Io(#[from] std::io::Error),
177 #[error(
178 "unsupported edition `{edition}` for package `{package}`; cargo-ff knows 2015/2018/2021/2024 — bump the dep or pin a known edition"
179 )]
180 UnsupportedEdition { edition: String, package: String },
181 #[error("package(s) not found in the workspace: {}", .0.join(", "))]
182 UnknownPackages(Vec<String>),
183 #[error("{0} thread panicked")]
184 ThreadPanicked(&'static str),
185 #[error("send failed (channel closed)")]
186 SendClosed,
187}
188
189pub type Result<T> = std::result::Result<T, Error>;