Skip to content

Commit 97b7783

Browse files
authored
refactor: migrate deps-cargo and deps-pypi from toml_edit to toml-span (#65) (#69)
Replace toml_edit with toml-span in both Cargo.toml and pyproject.toml parsers. toml-span provides reliable span tracking for all values including inline tables, eliminating text-search fallbacks for position tracking. - Rewrite deps-cargo parser to use toml_span::parse() with Value.span - Rewrite deps-pypi parser, removing find_dependency_string_position and find_table_key_position workarounds - Migrate lockfile parsers in both crates - Remove toml_edit from workspace dependencies entirely
1 parent aec1dde commit 97b7783

File tree

13 files changed

+233
-497
lines changed

13 files changed

+233
-497
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2121
- **Google Maven repository support** — Android packages (`androidx.*`, `com.google.firebase.*`, `com.google.android.*`, `com.android.*`) now resolve from Google Maven instead of Maven Central
2222

2323
### Changed
24+
- **Migrate deps-cargo and deps-pypi from toml_edit to toml-span** — Reliable span tracking for all values including inline tables; eliminates text-search fallbacks for position tracking
25+
- Remove `toml_edit` from workspace dependencies (all TOML parsers now use `toml-span`)
2426
- Extract `LineOffsetTable` and `position_in_range` to deps-core for reuse across ecosystems
2527
- Extract `complete_package_names_generic` to deps-core completion module
2628

Cargo.lock

Lines changed: 2 additions & 48 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ tempfile = "3"
4242
thiserror = "2"
4343
tokio = "1.49"
4444
tokio-test = "0.4"
45-
toml_edit = "0.25"
4645
toml-span = "0.7"
4746
tower-lsp-server = "0.23"
4847
tracing = "0.1"

crates/deps-cargo/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ serde = { workspace = true, features = ["derive"] }
2020
serde_json = { workspace = true }
2121
thiserror = { workspace = true }
2222
tokio = { workspace = true, features = ["fs"] }
23-
toml_edit = { workspace = true }
23+
toml-span = { workspace = true }
2424
tower-lsp-server = { workspace = true }
2525
tracing = { workspace = true }
2626
urlencoding = { workspace = true }

crates/deps-cargo/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ This crate provides parsing and registry integration for Rust's Cargo ecosystem.
1111

1212
## Features
1313

14-
- **TOML Parsing** — Parse `Cargo.toml` with position tracking using `toml_edit`
14+
- **TOML Parsing** — Parse `Cargo.toml` with position tracking using `toml-span`
1515
- **Lock File Parsing** — Extract resolved versions from `Cargo.lock`
1616
- **crates.io Registry** — Sparse index client for package metadata
1717
- **Version Resolution** — Semver-aware version matching

crates/deps-cargo/src/error.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,8 @@ use thiserror::Error;
1212
#[derive(Error, Debug)]
1313
pub enum CargoError {
1414
/// Failed to parse Cargo.toml
15-
#[error("Failed to parse Cargo.toml: {source}")]
16-
TomlParseError {
17-
#[source]
18-
source: toml_edit::TomlError,
19-
},
15+
#[error("Failed to parse Cargo.toml: {message}")]
16+
TomlParseError { message: String },
2017

2118
/// Invalid semver version specifier
2219
#[error("Invalid semver version specifier '{specifier}': {message}")]
@@ -157,9 +154,9 @@ impl From<deps_core::DepsError> for CargoError {
157154
impl From<CargoError> for deps_core::DepsError {
158155
fn from(err: CargoError) -> Self {
159156
match err {
160-
CargoError::TomlParseError { source } => Self::ParseError {
157+
CargoError::TomlParseError { message } => Self::ParseError {
161158
file_type: "Cargo.toml".into(),
162-
source: Box::new(source),
159+
source: Box::new(std::io::Error::other(message)),
163160
},
164161
CargoError::InvalidVersionSpecifier { message, .. } => Self::InvalidVersionReq(message),
165162
CargoError::PackageNotFound { package } => {

crates/deps-cargo/src/lockfile.rs

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ use deps_core::lockfile::{
2929
locate_lockfile_for_manifest,
3030
};
3131
use std::path::{Path, PathBuf};
32-
use toml_edit::DocumentMut;
3332
use tower_lsp_server::ls_types::Uri;
3433

3534
/// Cargo.lock file parser.
@@ -84,42 +83,46 @@ impl LockFileProvider for CargoLockParser {
8483
source: Box::new(e),
8584
})?;
8685

87-
let doc: DocumentMut = content.parse().map_err(|e| DepsError::ParseError {
86+
let doc = toml_span::parse(&content).map_err(|e| DepsError::ParseError {
8887
file_type: "Cargo.lock".into(),
89-
source: Box::new(e),
88+
source: Box::new(std::io::Error::other(e.to_string())),
9089
})?;
9190

9291
let mut packages = ResolvedPackages::new();
9392

94-
let Some(package_array) = doc
95-
.get("package")
96-
.and_then(|v: &toml_edit::Item| v.as_array_of_tables())
97-
else {
93+
let Some(root_table) = doc.as_table() else {
94+
tracing::warn!("Cargo.lock root is not a table");
95+
return Ok(packages);
96+
};
97+
98+
let Some(package_array_val) = root_table.get("package") else {
9899
tracing::warn!("Cargo.lock missing [[package]] array of tables");
99100
return Ok(packages);
100101
};
101102

102-
for table in package_array {
103+
let Some(package_array) = package_array_val.as_array() else {
104+
tracing::warn!("Cargo.lock [[package]] is not an array");
105+
return Ok(packages);
106+
};
107+
108+
for entry in package_array {
109+
let Some(table) = entry.as_table() else {
110+
continue;
111+
};
112+
103113
// Extract required fields
104-
let Some(name) = table.get("name").and_then(|v: &toml_edit::Item| v.as_str()) else {
114+
let Some(name) = table.get("name").and_then(|v| v.as_str()) else {
105115
tracing::warn!("Package missing name field");
106116
continue;
107117
};
108118

109-
let Some(version) = table
110-
.get("version")
111-
.and_then(|v: &toml_edit::Item| v.as_str())
112-
else {
119+
let Some(version) = table.get("version").and_then(|v| v.as_str()) else {
113120
tracing::warn!("Package '{}' missing version field", name);
114121
continue;
115122
};
116123

117124
// Parse source (optional for path dependencies)
118-
let source = parse_cargo_source(
119-
table
120-
.get("source")
121-
.and_then(|v: &toml_edit::Item| v.as_str()),
122-
);
125+
let source = parse_cargo_source(table.get("source").and_then(|v| v.as_str()));
123126

124127
// Parse dependencies array (optional)
125128
let dependencies = parse_cargo_dependencies_from_table(table);
@@ -182,7 +185,7 @@ fn parse_cargo_source(source_str: Option<&str>) -> ResolvedSource {
182185
/// ```toml
183186
/// dependencies = ["serde_derive", "syn"]
184187
/// ```
185-
fn parse_cargo_dependencies_from_table(table: &toml_edit::Table) -> Vec<String> {
188+
fn parse_cargo_dependencies_from_table(table: &toml_span::value::Table<'_>) -> Vec<String> {
186189
let Some(deps_value) = table.get("dependencies") else {
187190
return vec![];
188191
};
@@ -199,9 +202,9 @@ fn parse_cargo_dependencies_from_table(table: &toml_edit::Table) -> Vec<String>
199202
return Some(s.to_string());
200203
}
201204

202-
// Inline table format (rare, extract "name" field)
203-
if let Some(table) = item.as_inline_table()
204-
&& let Some(name) = table.get("name").and_then(|v| v.as_str())
205+
// Table format (rare, extract "name" field)
206+
if let Some(t) = item.as_table()
207+
&& let Some(name) = t.get("name").and_then(|v| v.as_str())
205208
{
206209
return Some(name.to_string());
207210
}

0 commit comments

Comments
 (0)