Skip to content

Commit 9c29a42

Browse files
Split ProjectMetadata::discover() into focused helpers (#535)
## Summary `ProjectMetadata::discover()` was a 96-line method with 4-level nesting that mixed file system traversal, TOML parsing, precedence logic, and fallback state tracking all in one place. This PR extracts three focused helper functions to flatten the loop body and make the algorithm easier to follow: - **`try_load_karva_toml(dir)`** — checks for `karva.toml` in the given directory and parses it if present. Returns `Ok(None)` if the file doesn't exist. - **`try_load_pyproject(dir)`** — checks for `pyproject.toml` in the given directory and parses it if present. Returns `Ok(None)` if the file doesn't exist. - **`has_karva_section(pyproject)`** — returns whether a parsed pyproject contains a `[tool.karva]` section. The main `discover()` loop now reads as a clear sequence: load pyproject, check for karva.toml (highest precedence), check for pyproject with `[tool.karva]`, track closest plain pyproject as fallback. The precedence rules are preserved exactly: 1. `karva.toml` takes precedence over `pyproject.toml` (with a warning if both have karva config) 2. `pyproject.toml` with `[tool.karva]` section 3. Closest `pyproject.toml` without `[tool.karva]` 4. Fallback to default settings at the given path ## Test plan - [x] All 685 tests pass (`just test`) - [x] All pre-commit checks pass (`uvx prek run -a`) - [x] Pure refactor — no behavioral changes 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 2e528db commit 9c29a42

File tree

1 file changed

+58
-52
lines changed
  • crates/karva_metadata/src

1 file changed

+58
-52
lines changed

crates/karva_metadata/src/lib.rs

Lines changed: 58 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -106,88 +106,53 @@ impl ProjectMetadata {
106106
let mut closest_project: Option<Self> = None;
107107

108108
for project_root in path.ancestors() {
109-
let pyproject_path = project_root.join("pyproject.toml");
110-
111-
let pyproject = if let Ok(pyproject_str) = std::fs::read_to_string(&pyproject_path) {
112-
match PyProject::from_toml_str(&pyproject_str) {
113-
Ok(pyproject) => Some(pyproject),
114-
Err(error) => {
115-
return Err(ProjectMetadataError::InvalidPyProject {
116-
path: pyproject_path,
117-
source: Box::new(error),
118-
});
119-
}
120-
}
121-
} else {
122-
None
123-
};
124-
125-
// A `karva.toml` takes precedence over a `pyproject.toml`.
126-
let karva_toml_path = project_root.join("karva.toml");
127-
if let Ok(karva_str) = std::fs::read_to_string(&karva_toml_path) {
128-
let options = match Options::from_toml_str(&karva_str) {
129-
Ok(options) => options,
130-
Err(error) => {
131-
return Err(ProjectMetadataError::InvalidKarvaToml {
132-
path: karva_toml_path,
133-
source: Box::new(error),
134-
});
135-
}
136-
};
137-
138-
if pyproject
139-
.as_ref()
140-
.is_some_and(|project| project.karva().is_some())
141-
{
109+
let pyproject = try_load_pyproject(project_root)?;
110+
111+
if let Some(options) = try_load_karva_toml(project_root)? {
112+
if has_karva_section(pyproject.as_ref()) {
113+
let pyproject_path = project_root.join("pyproject.toml");
114+
let karva_toml_path = project_root.join("karva.toml");
142115
tracing::warn!(
143116
"Ignoring the `tool.karva` section in `{pyproject_path}` because `{karva_toml_path}` takes precedence."
144117
);
145118
}
146119

147120
tracing::debug!("Found project at '{}'", project_root);
148-
149-
let metadata =
150-
Self::from_options(options, project_root.to_path_buf(), python_version);
151-
152-
return Ok(metadata);
121+
return Ok(Self::from_options(
122+
options,
123+
project_root.to_path_buf(),
124+
python_version,
125+
));
153126
}
154127

155128
if let Some(pyproject) = pyproject {
156-
let has_karva_section = pyproject.karva().is_some();
129+
let has_karva = pyproject.karva().is_some();
157130
let metadata =
158131
Self::from_pyproject(pyproject, project_root.to_path_buf(), python_version);
159132

160-
if has_karva_section {
133+
if has_karva {
161134
tracing::debug!("Found project at '{}'", project_root);
162-
163135
return Ok(metadata);
164136
}
165137

166-
// Not a project itself, keep looking for an enclosing project.
167138
if closest_project.is_none() {
168139
closest_project = Some(metadata);
169140
}
170141
}
171142
}
172143

173-
// No project found, but maybe a pyproject.toml was found.
174-
let metadata = if let Some(closest_project) = closest_project {
144+
if let Some(closest_project) = closest_project {
175145
tracing::debug!(
176146
"Project without `tool.karva` section: '{}'",
177147
closest_project.root()
178148
);
179-
180-
closest_project
149+
Ok(closest_project)
181150
} else {
182151
tracing::debug!(
183152
"The ancestor directories contain no `pyproject.toml`. Falling back to a virtual project."
184153
);
185-
186-
// Create a project with a default configuration
187-
Self::new(path.to_path_buf(), python_version)
188-
};
189-
190-
Ok(metadata)
154+
Ok(Self::new(path.to_path_buf(), python_version))
155+
}
191156
}
192157

193158
pub fn python_version(&self) -> PythonVersion {
@@ -214,6 +179,47 @@ impl ProjectMetadata {
214179
}
215180
}
216181

182+
/// Checks for a `karva.toml` in `dir` and parses it if present.
183+
fn try_load_karva_toml(dir: &Utf8Path) -> Result<Option<Options>, ProjectMetadataError> {
184+
let path = dir.join("karva.toml");
185+
186+
let Ok(content) = std::fs::read_to_string(&path) else {
187+
return Ok(None);
188+
};
189+
190+
let options = Options::from_toml_str(&content).map_err(|error| {
191+
ProjectMetadataError::InvalidKarvaToml {
192+
source: Box::new(error),
193+
path,
194+
}
195+
})?;
196+
197+
Ok(Some(options))
198+
}
199+
200+
/// Checks for a `pyproject.toml` in `dir` and parses it if present.
201+
fn try_load_pyproject(dir: &Utf8Path) -> Result<Option<PyProject>, ProjectMetadataError> {
202+
let path = dir.join("pyproject.toml");
203+
204+
let Ok(content) = std::fs::read_to_string(&path) else {
205+
return Ok(None);
206+
};
207+
208+
let pyproject = PyProject::from_toml_str(&content).map_err(|error| {
209+
ProjectMetadataError::InvalidPyProject {
210+
path,
211+
source: Box::new(error),
212+
}
213+
})?;
214+
215+
Ok(Some(pyproject))
216+
}
217+
218+
/// Returns `true` if the pyproject contains a `[tool.karva]` section.
219+
fn has_karva_section(pyproject: Option<&PyProject>) -> bool {
220+
pyproject.is_some_and(|project| project.karva().is_some())
221+
}
222+
217223
#[derive(Debug, Error)]
218224
pub enum ProjectMetadataError {
219225
#[error("project path '{0}' is not a directory")]

0 commit comments

Comments
 (0)