1use crate::{CratesIoRegistry, ParsedDependency, crate_url};
7use async_trait::async_trait;
8use deps_core::{EcosystemHandler, HttpCache, SemverMatcher, VersionRequirementMatcher};
9use std::sync::Arc;
10
11pub struct CargoHandler {
16 registry: CratesIoRegistry,
17}
18
19#[async_trait]
20impl EcosystemHandler for CargoHandler {
21 type Registry = CratesIoRegistry;
22 type Dependency = ParsedDependency;
23 type UnifiedDep = ParsedDependency; fn new(cache: Arc<HttpCache>) -> Self {
26 Self {
27 registry: CratesIoRegistry::new(cache),
28 }
29 }
30
31 fn registry(&self) -> &Self::Registry {
32 &self.registry
33 }
34
35 fn extract_dependency(dep: &Self::UnifiedDep) -> Option<&Self::Dependency> {
36 Some(dep)
38 }
39
40 fn package_url(name: &str) -> String {
41 crate_url(name)
42 }
43
44 fn ecosystem_display_name() -> &'static str {
45 "crates.io"
46 }
47
48 #[inline]
49 fn is_version_latest(version_req: &str, latest: &str) -> bool {
50 SemverMatcher.is_latest_satisfying(version_req, latest)
51 }
52
53 fn format_version_for_edit(_dep: &Self::Dependency, version: &str) -> String {
54 format!("\"{version}\"")
55 }
56
57 fn is_deprecated(version: &crate::CargoVersion) -> bool {
58 version.yanked
59 }
60
61 fn is_valid_version_syntax(version_req: &str) -> bool {
62 version_req.parse::<semver::VersionReq>().is_ok()
63 }
64
65 fn parse_version_req(version_req: &str) -> Option<semver::VersionReq> {
66 version_req.parse().ok()
67 }
68}
69
70#[cfg(test)]
71mod tests {
72 use super::*;
73
74 #[test]
75 fn test_package_url() {
76 let url = CargoHandler::package_url("serde");
77 assert_eq!(url, "https://crates.io/crates/serde");
78 }
79
80 #[test]
81 fn test_ecosystem_display_name() {
82 assert_eq!(CargoHandler::ecosystem_display_name(), "crates.io");
83 }
84
85 #[test]
86 fn test_is_version_latest_compatible() {
87 assert!(CargoHandler::is_version_latest("1.0.0", "1.0.5"));
88 assert!(CargoHandler::is_version_latest("^1.0.0", "1.5.0"));
89 assert!(CargoHandler::is_version_latest("0.1", "0.1.83"));
90 }
91
92 #[test]
93 fn test_is_version_latest_incompatible() {
94 assert!(!CargoHandler::is_version_latest("1.0.0", "2.0.0"));
95 assert!(!CargoHandler::is_version_latest("0.1", "0.2.0"));
96 }
97
98 #[test]
99 fn test_new_creates_handler() {
100 let cache = Arc::new(HttpCache::new());
101 let handler = CargoHandler::new(cache);
102 let registry = handler.registry();
103 assert!(std::ptr::addr_of!(*registry) == std::ptr::addr_of!(handler.registry));
104 }
105
106 #[test]
107 fn test_extract_dependency_returns_some() {
108 use crate::ParsedDependency;
109 use tower_lsp_server::ls_types::{Position, Range};
110
111 let dep = ParsedDependency {
112 name: "test".into(),
113 name_range: Range::new(Position::new(0, 0), Position::new(0, 4)),
114 version_req: Some("1.0.0".into()),
115 version_range: Some(Range::new(Position::new(0, 8), Position::new(0, 13))),
116 features: vec![],
117 features_range: None,
118 source: crate::DependencySource::Registry,
119 workspace_inherited: false,
120 section: crate::DependencySection::Dependencies,
121 };
122 let result = CargoHandler::extract_dependency(&dep);
123 assert!(result.is_some());
124 assert_eq!(result.unwrap().name, "test");
125 }
126
127 #[test]
128 fn test_is_version_latest_with_tilde() {
129 assert!(CargoHandler::is_version_latest("~1.0.0", "1.0.5"));
130 assert!(!CargoHandler::is_version_latest("~1.0.0", "1.1.0"));
131 }
132
133 #[test]
134 fn test_is_version_latest_with_exact() {
135 assert!(CargoHandler::is_version_latest("=1.0.0", "1.0.0"));
136 assert!(!CargoHandler::is_version_latest("=1.0.0", "1.0.1"));
137 }
138
139 #[test]
140 fn test_is_version_latest_edge_cases() {
141 assert!(CargoHandler::is_version_latest("0.0.1", "0.0.1"));
142 assert!(!CargoHandler::is_version_latest("0.0.1", "0.0.2"));
143 }
144}