1use std::path::PathBuf;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
4pub enum Edition {
5 E2015,
6 E2018,
7 E2021,
8 E2024,
9}
10
11impl Edition {
12 pub fn as_str(self) -> &'static str {
13 match self {
14 Edition::E2015 => "2015",
15 Edition::E2018 => "2018",
16 Edition::E2021 => "2021",
17 Edition::E2024 => "2024",
18 }
19 }
20}
21
22impl std::str::FromStr for Edition {
23 type Err = UnknownEdition;
24 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
25 Ok(match s {
26 "2015" => Edition::E2015,
27 "2018" => Edition::E2018,
28 "2021" => Edition::E2021,
29 "2024" => Edition::E2024,
30 other => return Err(UnknownEdition(other.to_owned())),
31 })
32 }
33}
34
35impl TryFrom<cargo_metadata::Edition> for Edition {
36 type Error = UnknownEdition;
37 fn try_from(e: cargo_metadata::Edition) -> std::result::Result<Self, Self::Error> {
38 e.as_str().parse()
39 }
40}
41
42#[derive(Debug, Clone, thiserror::Error)]
45#[error("unknown edition: {0}")]
46pub struct UnknownEdition(pub String);
47
48#[derive(Debug, Clone)]
49pub struct CrateUnit {
50 pub edition: Edition,
51 pub manifest_dir: PathBuf,
52 pub files: Vec<PathBuf>,
53 pub size_bytes: u64,
59}
60
61#[derive(Debug, Clone)]
65pub struct Batch {
66 pub edition: Edition,
67 pub units: Vec<CrateUnit>,
68}
69
70impl Batch {
71 pub fn size_bytes(&self) -> u64 {
72 self.units.iter().map(|u| u.size_bytes).sum()
73 }
74
75 pub fn file_count(&self) -> usize {
76 self.units.iter().map(|u| u.files.len()).sum()
77 }
78
79 pub fn sort_key(&self) -> PathBuf {
81 self.units
82 .iter()
83 .map(|u| u.manifest_dir.clone())
84 .min()
85 .unwrap_or_default()
86 }
87}
88
89#[derive(Debug)]
90pub struct BatchResult {
91 pub sort_key: PathBuf,
92 pub stdout: Vec<u8>,
93 pub stderr: Vec<u8>,
94 pub exit_code: i32,
95 pub files: Vec<(PathBuf, PathBuf)>,
99}
100
101#[derive(Debug, Clone, Default)]
102pub struct Config {
103 pub manifest_path: Option<PathBuf>,
104 pub packages: Vec<String>,
105 pub all: bool,
108 pub check: bool,
109 pub workers: Option<usize>,
110 pub channel_capacity: Option<usize>,
111 pub rustfmt_args: Vec<String>,
112 pub batch_size: Option<usize>,
118 pub experimental_cache: bool,
124 pub warnings: bool,
128}
129
130#[derive(Debug)]
131pub struct Report {
132 pub failures: Vec<FileFailure>,
133 pub exit_code: i32,
134}
135
136#[derive(Debug)]
137pub struct FileFailure {
138 pub file: PathBuf,
139 pub manifest_dir: PathBuf,
140}
141
142#[derive(Debug, thiserror::Error)]
143pub enum Error {
144 #[error("`workers` must be >= 1 (got {0})")]
145 InvalidWorkers(usize),
146 #[error("cargo metadata failed: {0}")]
147 Metadata(#[from] cargo_metadata::Error),
148 #[error("io: {0}")]
149 Io(#[from] std::io::Error),
150 #[error(
151 "unsupported edition `{edition}` for package `{package}`; cargo-ff knows 2015/2018/2021/2024 — bump the dep or pin a known edition"
152 )]
153 UnsupportedEdition { edition: String, package: String },
154 #[error("package(s) not found in the workspace: {}", .0.join(", "))]
155 UnknownPackages(Vec<String>),
156 #[error("{0} thread panicked")]
157 ThreadPanicked(&'static str),
158 #[error("send failed (channel closed)")]
159 SendClosed,
160}
161
162pub type Result<T> = std::result::Result<T, Error>;