Skip to main content
Glama

cursor-rust-tools

by terhechte
walk.rs4.43 kB
use anyhow::Result; use ignore::WalkBuilder; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::fs::{self}; use std::path::{Path, PathBuf}; use super::extract_md::extract_md; use super::utils::{get_cargo_dependencies, parse_rust_symbol}; #[derive(Serialize, Deserialize, Default, Debug)] pub struct DocsCache { pub hash: String, pub deps: HashMap<String, HashMap<String, String>>, pub crate_versions: HashMap<String, String>, } impl DocsCache { pub fn new(project: &crate::project::Project) -> Result<Self> { let cache_path = project.cache_dir().join("docs_cache.json"); if cache_path.exists() { let content = fs::read_to_string(cache_path)?; Ok(serde_json::from_str(&content)?) } else { Ok(Self::default()) } } pub fn save(&self, project: &crate::project::Project) -> Result<()> { let cache_path = project.cache_dir().join("docs_cache.json"); fs::create_dir_all(project.cache_dir())?; fs::write(cache_path, serde_json::to_string_pretty(self)?)?; Ok(()) } } pub fn walk_docs(project: &crate::project::Project) -> Result<()> { let mut cache = DocsCache::new(project)?; let dependencies = get_cargo_dependencies(project)?; tracing::info!("dependencies: {:?}", dependencies); // Convert dependencies to a HashMap for easier lookup let dep_versions: HashMap<String, String> = dependencies.into_iter().collect(); // Walk the docs directory let walker = WalkBuilder::new(project.docs_dir()).hidden(false).build(); for result in walker { let entry = result?; let path = entry.path(); if path.extension().and_then(|ext| ext.to_str()) == Some("html") { if let Some(relative_path) = path_to_cache_key(path, project.docs_dir()) { if let Some((crate_name, file_path)) = extract_crate_and_path(&relative_path) { // Skip if crate is not in dependencies let Some(version) = dep_versions.get(crate_name) else { tracing::debug!( "Skipping {crate_name}: {file_path} because it's not in dependencies" ); continue; }; // Skip if crate is in ignore list if project.ignore_crates().contains(&crate_name.to_string()) { tracing::debug!("Skipping {crate_name} because it's in ignore list"); continue; } // Skip if version hasn't changed if let Some(cached_version) = cache.crate_versions.get(crate_name) { if cached_version == version { tracing::debug!( "Skipping {crate_name} because the version has not changed" ); continue; } } // Process the file since it's either new or updated let html_content = fs::read_to_string(path)?; let markdown = extract_md(&html_content); tracing::debug!("Indexing {crate_name}: {file_path}"); let symbol = parse_rust_symbol(file_path) .map(|s| s.to_string()) .unwrap_or(file_path.to_string()); cache .deps .entry(crate_name.to_string()) .or_default() .insert(symbol, markdown); // Store the version number cache .crate_versions .insert(crate_name.to_string(), version.clone()); } } } } // Create and save cache cache.save(project)?; Ok(()) } fn path_to_cache_key(path: &Path, docs_dir: PathBuf) -> Option<String> { path.strip_prefix(docs_dir) .ok() .and_then(|p| p.to_str()) .map(|s| s.to_string()) } fn extract_crate_and_path(path: &str) -> Option<(&str, &str)> { let parts: Vec<&str> = path.splitn(2, '/').collect(); match parts.as_slice() { [crate_name, rest] => Some((*crate_name, *rest)), _ => None, } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/terhechte/cursor-rust-tools'

If you have feedback or need assistance with the MCP directory API, please join our Discord server