Skip to main content

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}