Skip to content

Commit 64bfede

Browse files
committed
Fix: Use cargo_metadata for Cargo.toml parsing
The module used old implementation of Cargo.toml parsing. Resolves: eclipse-score/score#1522 Signed-off-by: Nicolae Dicu <[email protected]>
1 parent b4494b6 commit 64bfede

File tree

4 files changed

+63
-223
lines changed

4 files changed

+63
-223
lines changed

README.md

Lines changed: 0 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -10,135 +10,3 @@ See [docs/](docs/) for more information.
1010

1111
[pypi-badge]: https://img.shields.io/pypi/v/sphinx-rust.svg
1212
[pypi-link]: https://pypi.org/project/sphinx-rust
13-
14-
## Fork notes
15-
16-
### Integration, usage and testing
17-
18-
**CURRENTLY FORK UNDER DEVELOPMENT! - PIP PACKAGE UNAVAILABLE**
19-
20-
* In order to integrate in score:
21-
22-
Update score/docs/conf.py :
23-
24-
```python
25-
extensions = [
26-
"sphinx_rust",
27-
...
28-
]
29-
30-
# point to a rust crate in score(create a dummy one for testing if unavailable)
31-
rust_crates = [
32-
"../rust-crates/dummy_crate",
33-
]
34-
```
35-
In index.rst :
36-
```
37-
Rust API <api/crates/dummy_crate/index>
38-
```
39-
40-
* Testing in score repo:
41-
42-
For fast development it is recomended to convert sphinx-rust module to bazel module by creating:
43-
44-
- __sphinx-rust__/BUILD.bazel:
45-
46-
```python
47-
load("@rules_python//python:defs.bzl", "py_library")
48-
49-
py_library(
50-
name = "sphinx_rust",
51-
srcs = glob(["python/sphinx_rust/**/*.py"]),
52-
data = glob([
53-
"python/sphinx_rust/sphinx_rust.cpython-312-*.so",
54-
]),
55-
imports = ["python"],
56-
visibility = ["//visibility:public"],
57-
)
58-
```
59-
60-
- __sphinx-rust__/MODULE.bazel
61-
62-
```python
63-
module(name = "sphinx_rust", version = "0.0.0-dev")
64-
bazel_dep(name = "rules_python", version = "1.4.1")
65-
```
66-
67-
Now we can add the __sphinx_rust__ module directly in __score__:
68-
69-
- __score__/MODULE.bazel:
70-
71-
```python
72-
bazel_dep(name = "sphinx_rust", version = "0.0.0-dev")
73-
local_path_override(module_name = "sphinx_rust", path = "../sphinx-rust")
74-
```
75-
76-
- __docs-as-code__/docs.bzl:
77-
78-
```python
79-
def _incremental ...
80-
81-
py_binary(
82-
name = incremental_name,
83-
srcs = ["@score_docs_as_code//src:incremental.py"],
84-
deps = dependencies + ["@sphinx_rust//:sphinx_rust"],
85-
```
86-
87-
- create the dummy_crate in __score__ with the following structure and content:
88-
89-
```sh
90-
../score/rust-crates/
91-
└── dummy_crate
92-
├── Cargo.toml
93-
└── src
94-
└── lib.rs
95-
```
96-
97-
Cargo.toml:
98-
99-
```yaml
100-
[package]
101-
name = "dummy_crate"
102-
version = "0.1.0"
103-
edition = "2021"
104-
105-
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
106-
107-
[dependencies]
108-
109-
[lib]
110-
name = "dummy_crate"
111-
```
112-
113-
lib.rs:
114-
115-
```rs
116-
pub fn add(left: usize, right: usize) -> usize {
117-
left + right
118-
}
119-
120-
#[cfg(test)]
121-
mod tests {
122-
use super::*;
123-
124-
#[test]
125-
fn it_works() {
126-
let result = add(2, 2);
127-
assert_eq!(result, 4);
128-
}
129-
}
130-
```
131-
132-
It can be tested using requirements.in from docs-as-code as well but this is not that reliable as bazel will cache sphinx-rust:
133-
134-
- in __docs-as-code__/src/requirements.in:
135-
136-
```python
137-
sphinx-rust @ file:///mnt/d/qorix/forks/sphinx-rust
138-
```
139-
140-
- run pip packages update:
141-
142-
```sh
143-
bazel run //src:requirements.update
144-
```

crates/analyzer/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ serde.workspace = true
2020
serde_json.workspace = true
2121
syn.workspace = true
2222
toml.workspace = true
23+
cargo_metadata = "0.18"
2324

2425
[dev-dependencies]
2526
insta.workspace = true

crates/analyzer/src/analyze/crate_.rs

Lines changed: 61 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,76 @@
11
//! Analyze the crate
2-
use anyhow::{Context, Result};
2+
use anyhow::{anyhow, Context, Result};
3+
use cargo_metadata::{MetadataCommand, Target};
34
use serde::{Deserialize, Serialize};
5+
use std::path::PathBuf;
46

57
use crate::data_model::{Crate, Enum, Function, Module, Struct};
68

79
pub fn analyze_crate(path: &str) -> Result<AnalysisResult> {
810
// make the path absolute
911
// TODO we use dunce to canonicalize the path because otherwise there is issues with python's os.path.relpath on windows, but maybe we should fix this on the Python side
10-
let path =
12+
let crate_dir =
1113
dunce::canonicalize(path).context(format!("Error resolving crate path: {}", path))?;
14+
eprintln!("running new analyzer");
1215
// check the path is a directory
13-
if !path.is_dir() {
14-
return Err(anyhow::anyhow!(format!(
16+
if !crate_dir.is_dir() {
17+
return Err(anyhow!(
1518
"Crate path is not a directory: {}",
16-
path.to_string_lossy()
17-
)));
19+
crate_dir.to_string_lossy()
20+
));
1821
}
1922
// check if Cargo.toml exists
20-
let cargo_toml_path = path.join("Cargo.toml");
23+
let cargo_toml_path = crate_dir.join("Cargo.toml");
2124
if !cargo_toml_path.exists() {
22-
return Err(anyhow::anyhow!(format!(
25+
return Err(anyhow!(
2326
"Cargo.toml does not exist in: {}",
24-
path.to_string_lossy()
25-
)));
27+
crate_dir.to_string_lossy()
28+
));
2629
}
2730

28-
// read the Cargo.toml and initialize the Crate struct
29-
let contents = std::fs::read_to_string(&cargo_toml_path)?;
30-
let cargo_toml: CargoToml = toml::from_str(&contents).context(format!(
31-
"Error parsing: {}",
32-
cargo_toml_path.to_string_lossy()
33-
))?;
31+
// use `cargo_metadata` instead of implementing own TOML parser
32+
let metadata = MetadataCommand::new()
33+
.manifest_path(&cargo_toml_path)
34+
.exec()
35+
.context("Failed to run `cargo metadata`")?;
3436

35-
// check whether the crate is a library or binary
36-
let (crate_name, to_root) = if let Some(lib) = cargo_toml.lib {
37-
if cargo_toml.bin.is_some() {
38-
return Err(anyhow::anyhow!(format!(
39-
"Both lib and bin sections in: {}",
40-
path.to_string_lossy()
41-
)));
42-
}
43-
(
44-
lib.name.unwrap_or(cargo_toml.package.name),
45-
lib.path.unwrap_or("src/lib.rs".to_string()),
46-
)
47-
} else if let Some(bin) = cargo_toml.bin {
48-
(
49-
bin.name.unwrap_or(cargo_toml.package.name),
50-
bin.path.unwrap_or("src/main.rs".to_string()),
51-
)
52-
} else {
53-
return Err(anyhow::anyhow!(format!(
54-
"No lib or bin section in: {}",
55-
path.to_string_lossy()
56-
)));
57-
};
37+
let root_pkg = metadata
38+
.root_package()
39+
.ok_or_else(|| anyhow!("`cargo metadata` returned no root package"))?;
40+
41+
// Prefer library target; fall back to the first binary target
42+
let root_target: &Target = root_pkg
43+
.targets
44+
.iter()
45+
.find(|t| t.kind.contains(&"lib".into()))
46+
.or_else(|| root_pkg.targets.iter().find(|t| t.kind.contains(&"bin".into())))
47+
.ok_or_else(|| anyhow!("No lib or bin target defined in manifest"))?;
48+
49+
let crate_name = root_target.name.clone();
50+
let root_module = PathBuf::from(&root_target.src_path);
5851

5952
let mut result = AnalysisResult::new(Crate {
60-
name: crate_name,
61-
version: cargo_toml.package.version.clone(),
53+
name: crate_name.clone(),
54+
version: root_pkg.version.to_string(), // workspace-aware
6255
});
6356

6457
// check existence of the root module
65-
let root_module = path.join(to_root);
6658
if !root_module.exists() {
6759
return Ok(result);
6860
}
6961

7062
// read the top-level module
7163
let content = std::fs::read_to_string(&root_module)?;
72-
let (module, structs, enums, functions) =
73-
Module::parse(Some(&root_module), &[&result.crate_.name], &content).context(format!(
74-
"Error parsing module {}",
75-
root_module.to_string_lossy()
76-
))?;
64+
let (module, structs, enums, functions) = Module::parse(
65+
Some(&root_module),
66+
&[&result.crate_.name],
67+
&content,
68+
)
69+
.context(format!(
70+
"Error parsing module {}",
71+
root_module.to_string_lossy()
72+
))?;
73+
7774
let mut modules_to_read = module
7875
.declarations
7976
.iter()
@@ -91,24 +88,23 @@ pub fn analyze_crate(path: &str) -> Result<AnalysisResult> {
9188
result.enums.extend(enums);
9289
result.functions.extend(functions);
9390

94-
// recursively find/read the public sub-modules
91+
// recursively find/read the public submodules
9592
let mut read_modules = vec![];
9693
while let Some((parent_dir, module_name, parent)) = modules_to_read.pop() {
97-
let (module_path, submodule_dir) =
98-
if parent_dir.join(&module_name).with_extension("rs").exists() {
99-
(
100-
parent_dir.join(&module_name).with_extension("rs"),
101-
parent_dir.join(&module_name),
102-
)
103-
} else if parent_dir.join(&module_name).join("mod.rs").exists() {
104-
(
105-
parent_dir.join(&module_name).join("mod.rs"),
106-
parent_dir.to_path_buf(),
107-
)
108-
} else {
109-
// TODO warn about missing module?
110-
continue;
111-
};
94+
let (module_path, submodule_dir) = if parent_dir.join(&module_name).with_extension("rs").exists() {
95+
(
96+
parent_dir.join(&module_name).with_extension("rs"),
97+
parent_dir.join(&module_name),
98+
)
99+
} else if parent_dir.join(&module_name).join("mod.rs").exists() {
100+
(
101+
parent_dir.join(&module_name).join("mod.rs"),
102+
parent_dir.to_path_buf(),
103+
)
104+
} else {
105+
// TODO warn about missing module?
106+
continue;
107+
};
112108

113109
if read_modules.contains(&module_path) {
114110
continue;
@@ -126,12 +122,12 @@ pub fn analyze_crate(path: &str) -> Result<AnalysisResult> {
126122
"Error parsing module {}",
127123
module_path.to_string_lossy()
128124
))?;
125+
129126
modules_to_read.extend(
130127
module
131128
.declarations
132129
.iter()
133-
.map(|s| (submodule_dir.clone(), s.to_string(), path.clone()))
134-
.collect::<Vec<_>>(),
130+
.map(|s| (submodule_dir.clone(), s.to_string(), path.clone())),
135131
);
136132
result.modules.push(module);
137133
result.structs.extend(structs);
@@ -164,31 +160,6 @@ impl AnalysisResult {
164160
}
165161
}
166162

167-
#[derive(Debug, Deserialize)]
168-
struct CargoToml {
169-
package: Package,
170-
bin: Option<Bin>,
171-
lib: Option<Lib>,
172-
}
173-
174-
#[derive(Debug, Deserialize)]
175-
struct Package {
176-
name: String,
177-
version: String,
178-
}
179-
180-
#[derive(Debug, Deserialize)]
181-
struct Lib {
182-
name: Option<String>,
183-
path: Option<String>,
184-
}
185-
186-
#[derive(Debug, Deserialize)]
187-
struct Bin {
188-
name: Option<String>,
189-
path: Option<String>,
190-
}
191-
192163
#[cfg(test)]
193164
mod tests {
194165
use super::*;

crates/py_binding/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "sphinx_rust"
3-
version = "0.0.2"
3+
version = "0.0.2-dev1"
44
publish = false
55
edition = "2021"
66
authors.workspace = true

0 commit comments

Comments
 (0)