Skip to content

Commit 58b7680

Browse files
checkpoint, syn in - but missing
1 parent 579f0c1 commit 58b7680

File tree

3 files changed

+54
-58
lines changed

3 files changed

+54
-58
lines changed

Cargo.lock

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

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ rand = "0.8"
4747
clap = { version = "4.4", features = ["derive"] }
4848
html2md = "0.2.14"
4949
regex = "1"
50+
syn = { version = "2.0.104", features = ["full"] }
51+
flate2 = "1.1.2"
5052

5153
[dev-dependencies]
5254
# Testing utilities

src/tools/item_list.rs

Lines changed: 47 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
11
use anyhow::Result;
2+
use reqwest;
3+
use std::fs;
4+
use std::path::Path;
5+
use tar::Archive;
6+
use flate2::read::GzDecoder;
7+
use syn::{File, Item};
8+
use tokio::fs as tokio_fs;
29

310
/// Represents filters for item listing.
411
#[derive(Debug)]
@@ -8,70 +15,55 @@ pub struct ItemListFilters {
815
pub module: Option<String>,
916
}
1017

18+
/// Utility function to download and cache crate source.
19+
async fn download_and_cache_crate(crate_name: &str, version: &str) -> Result<String> {
20+
let cache_dir = Path::new("./cache");
21+
let crate_dir = cache_dir.join(format!("{}-{}", crate_name, version));
22+
23+
if crate_dir.exists() {
24+
return Ok(crate_dir.to_string_lossy().to_string());
25+
}
26+
27+
let url = format!("https://crates.io/api/v1/crates/{}/{}/download", crate_name, version);
28+
let response = reqwest::get(&url).await?;
29+
let tarball = response.bytes().await?;
30+
31+
fs::create_dir_all(&cache_dir)?;
32+
let tar_gz = GzDecoder::new(&*tarball);
33+
let mut archive = Archive::new(tar_gz);
34+
archive.unpack(&cache_dir)?;
35+
36+
Ok(crate_dir.to_string_lossy().to_string())
37+
}
38+
1139
/// Stub for the crate item enumeration tool.
1240
/// This will use rust-analyzer to enumerate items in a crate.
1341
pub async fn list_crate_items(
1442
crate_name: &str,
1543
version: &str,
1644
filters: Option<ItemListFilters>,
1745
) -> Result<String> {
18-
// 🦨 skunky: Implementation pending. Will use rust-analyzer APIs.
19-
Ok(format!(
20-
"Stub: list_crate_items for crate: {}, version: {}, filters: {:?}",
21-
crate_name, version, filters
22-
))
23-
}
46+
let crate_path = download_and_cache_crate(crate_name, version).await?;
47+
let mut items = Vec::new();
2448

25-
#[cfg(test)]
26-
mod tests {
27-
use super::*;
28-
use tokio;
49+
for entry in fs::read_dir(crate_path)? {
50+
let entry = entry?;
51+
let path = entry.path();
52+
if path.extension().and_then(|ext| ext.to_str()) == Some("rs") {
53+
let content = fs::read_to_string(&path)?;
54+
let parsed_file: File = syn::parse_file(&content)?;
2955

30-
#[tokio::test]
31-
async fn test_basic_call_returns_stub() {
32-
let result = list_crate_items("serde", "1.0.0", None).await.unwrap();
33-
assert!(result.contains("Stub: list_crate_items for crate: serde, version: 1.0.0"), "Stub output missing expected text");
56+
for item in parsed_file.items {
57+
match item {
58+
Item::Struct(_) if filters.as_ref().map_or(true, |f| f.item_type.as_deref() == Some("struct")) => items.push(format!("{:?}", item)),
59+
Item::Enum(_) if filters.as_ref().map_or(true, |f| f.item_type.as_deref() == Some("enum")) => items.push(format!("{:?}", item)),
60+
Item::Trait(_) if filters.as_ref().map_or(true, |f| f.item_type.as_deref() == Some("trait")) => items.push(format!("{:?}", item)),
61+
Item::Fn(_) if filters.as_ref().map_or(true, |f| f.item_type.as_deref() == Some("fn")) => items.push(format!("{:?}", item)),
62+
_ => {}
63+
}
64+
}
65+
}
3466
}
3567

36-
#[tokio::test]
37-
async fn test_with_item_type_filter() {
38-
let filters = ItemListFilters {
39-
item_type: Some("struct".to_string()),
40-
visibility: None,
41-
module: None,
42-
};
43-
let result = list_crate_items("serde", "1.0.0", Some(filters)).await.unwrap();
44-
assert!(result.contains("filters: Some"), "Stub output missing filters");
45-
assert!(result.contains("struct"), "Stub output missing item_type");
46-
}
47-
48-
#[tokio::test]
49-
async fn test_with_visibility_filter() {
50-
let filters = ItemListFilters {
51-
item_type: None,
52-
visibility: Some("pub".to_string()),
53-
module: None,
54-
};
55-
let result = list_crate_items("serde", "1.0.0", Some(filters)).await.unwrap();
56-
assert!(result.contains("filters: Some"), "Stub output missing filters");
57-
assert!(result.contains("pub"), "Stub output missing visibility");
58-
}
59-
60-
#[tokio::test]
61-
async fn test_with_module_filter() {
62-
let filters = ItemListFilters {
63-
item_type: None,
64-
visibility: None,
65-
module: Some("serde::de".to_string()),
66-
};
67-
let result = list_crate_items("serde", "1.0.0", Some(filters)).await.unwrap();
68-
assert!(result.contains("filters: Some"), "Stub output missing filters");
69-
assert!(result.contains("serde::de"), "Stub output missing module filter");
70-
}
71-
72-
#[tokio::test]
73-
async fn test_invalid_crate_name() {
74-
let result = list_crate_items("not_a_real_crate", "0.0.1", None).await.unwrap();
75-
assert!(result.contains("not_a_real_crate"), "Stub output missing invalid crate name");
76-
}
68+
Ok(items.join("\n"))
7769
}

0 commit comments

Comments
 (0)