deps_cargo/types.rs
1use std::any::Any;
2use std::collections::HashMap;
3use tower_lsp_server::ls_types::Range;
4
5pub use deps_core::parser::DependencySource;
6
7/// Parsed dependency from Cargo.toml with position tracking.
8///
9/// Stores all information about a dependency declaration, including its name,
10/// version requirement, features, and source positions for LSP operations.
11/// Positions are critical for features like hover, completion, and inlay hints.
12///
13/// # Examples
14///
15/// ```
16/// use deps_cargo::types::{ParsedDependency, DependencySection};
17/// use deps_cargo::DependencySource;
18/// use tower_lsp_server::ls_types::{Position, Range};
19///
20/// let dep = ParsedDependency {
21/// name: "serde".into(),
22/// name_range: Range::new(Position::new(5, 0), Position::new(5, 5)),
23/// version_req: Some("1.0".into()),
24/// version_range: Some(Range::new(Position::new(5, 9), Position::new(5, 14))),
25/// features: vec!["derive".into()],
26/// features_range: None,
27/// source: DependencySource::Registry,
28/// section: DependencySection::Dependencies,
29/// };
30///
31/// assert_eq!(dep.name, "serde");
32/// assert!(matches!(dep.source, DependencySource::Registry));
33/// ```
34#[derive(Debug, Clone, PartialEq, Eq)]
35pub struct ParsedDependency {
36 pub name: String,
37 pub name_range: Range,
38 pub version_req: Option<String>,
39 pub version_range: Option<Range>,
40 pub features: Vec<String>,
41 pub features_range: Option<Range>,
42 pub source: DependencySource,
43 pub section: DependencySection,
44}
45
46/// Section in Cargo.toml where a dependency is declared.
47///
48/// Cargo.toml has four dependency sections with different purposes:
49/// - `[dependencies]`: Runtime dependencies
50/// - `[dev-dependencies]`: Test and example dependencies
51/// - `[build-dependencies]`: Build script dependencies
52/// - `[workspace.dependencies]`: Workspace-wide dependency definitions
53///
54/// # Examples
55///
56/// ```
57/// use deps_cargo::types::DependencySection;
58///
59/// let section = DependencySection::Dependencies;
60/// assert!(matches!(section, DependencySection::Dependencies));
61/// ```
62#[derive(Debug, Clone, Copy, PartialEq, Eq)]
63pub enum DependencySection {
64 /// Runtime dependencies (`[dependencies]`)
65 Dependencies,
66 /// Development dependencies (`[dev-dependencies]`)
67 DevDependencies,
68 /// Build script dependencies (`[build-dependencies]`)
69 BuildDependencies,
70 /// Workspace-wide dependency definitions (`[workspace.dependencies]`)
71 WorkspaceDependencies,
72}
73
74/// Version information for a crate from crates.io.
75///
76/// Retrieved from the sparse index at `https://index.crates.io/{cr}/{at}/{crate}`.
77/// Contains version number, yanked status, and available feature flags.
78///
79/// # Examples
80///
81/// ```
82/// use deps_cargo::types::CargoVersion;
83/// use std::collections::HashMap;
84///
85/// let version = CargoVersion {
86/// num: "1.0.214".into(),
87/// yanked: false,
88/// features: {
89/// let mut f = HashMap::new();
90/// f.insert("derive".into(), vec!["serde_derive".into()]);
91/// f
92/// },
93/// };
94///
95/// assert!(!version.yanked);
96/// assert!(version.features.contains_key("derive"));
97/// ```
98#[derive(Debug, Clone)]
99pub struct CargoVersion {
100 pub num: String,
101 pub yanked: bool,
102 pub features: HashMap<String, Vec<String>>,
103}
104
105/// Crate metadata from crates.io search API.
106///
107/// Contains basic information about a crate for display in completion suggestions.
108/// Retrieved from `https://crates.io/api/v1/crates?q={query}`.
109///
110/// # Examples
111///
112/// ```
113/// use deps_cargo::types::CrateInfo;
114///
115/// let info = CrateInfo {
116/// name: "serde".into(),
117/// description: Some("A serialization framework".into()),
118/// repository: Some("https://github.com/serde-rs/serde".into()),
119/// documentation: Some("https://docs.rs/serde".into()),
120/// max_version: "1.0.214".into(),
121/// };
122///
123/// assert_eq!(info.name, "serde");
124/// ```
125#[derive(Debug, Clone)]
126pub struct CrateInfo {
127 pub name: String,
128 pub description: Option<String>,
129 pub repository: Option<String>,
130 pub documentation: Option<String>,
131 pub max_version: String,
132}
133
134// Trait implementations for deps-core integration
135
136impl deps_core::Dependency for ParsedDependency {
137 fn name(&self) -> &str {
138 &self.name
139 }
140
141 fn name_range(&self) -> Range {
142 self.name_range
143 }
144
145 fn version_requirement(&self) -> Option<&str> {
146 self.version_req.as_deref()
147 }
148
149 fn version_range(&self) -> Option<Range> {
150 self.version_range
151 }
152
153 fn source(&self) -> deps_core::parser::DependencySource {
154 self.source.clone()
155 }
156
157 fn features(&self) -> &[String] {
158 &self.features
159 }
160
161 fn features_range(&self) -> Option<Range> {
162 self.features_range
163 }
164
165 fn as_any(&self) -> &dyn Any {
166 self
167 }
168}
169
170impl deps_core::Version for CargoVersion {
171 fn version_string(&self) -> &str {
172 &self.num
173 }
174
175 fn is_yanked(&self) -> bool {
176 self.yanked
177 }
178
179 fn features(&self) -> Vec<String> {
180 self.features.keys().cloned().collect()
181 }
182
183 fn as_any(&self) -> &dyn Any {
184 self
185 }
186}
187
188impl deps_core::Metadata for CrateInfo {
189 fn name(&self) -> &str {
190 &self.name
191 }
192
193 fn description(&self) -> Option<&str> {
194 self.description.as_deref()
195 }
196
197 fn repository(&self) -> Option<&str> {
198 self.repository.as_deref()
199 }
200
201 fn documentation(&self) -> Option<&str> {
202 self.documentation.as_deref()
203 }
204
205 fn latest_version(&self) -> &str {
206 &self.max_version
207 }
208
209 fn as_any(&self) -> &dyn Any {
210 self
211 }
212}
213
214#[cfg(test)]
215mod tests {
216 use super::*;
217
218 #[test]
219 fn test_dependency_source_variants() {
220 assert!(matches!(
221 DependencySource::Registry,
222 DependencySource::Registry
223 ));
224 assert!(matches!(
225 DependencySource::Git {
226 url: "u".into(),
227 rev: None
228 },
229 DependencySource::Git { .. }
230 ));
231 assert!(matches!(
232 DependencySource::Path { path: "p".into() },
233 DependencySource::Path { .. }
234 ));
235 assert!(matches!(
236 DependencySource::Workspace,
237 DependencySource::Workspace
238 ));
239 }
240
241 #[test]
242 fn test_dependency_section_variants() {
243 let deps = DependencySection::Dependencies;
244 let dev_deps = DependencySection::DevDependencies;
245 let build_deps = DependencySection::BuildDependencies;
246 let workspace_deps = DependencySection::WorkspaceDependencies;
247
248 assert!(matches!(deps, DependencySection::Dependencies));
249 assert!(matches!(dev_deps, DependencySection::DevDependencies));
250 assert!(matches!(build_deps, DependencySection::BuildDependencies));
251 assert!(matches!(
252 workspace_deps,
253 DependencySection::WorkspaceDependencies
254 ));
255 }
256
257 #[test]
258 fn test_cargo_version_creation() {
259 let version = CargoVersion {
260 num: "1.0.0".into(),
261 yanked: false,
262 features: HashMap::new(),
263 };
264
265 assert_eq!(version.num, "1.0.0");
266 assert!(!version.yanked);
267 assert!(version.features.is_empty());
268 }
269}