deps_npm/
types.rs

1use tower_lsp_server::ls_types::Range;
2
3/// Parsed dependency from package.json with position tracking.
4///
5/// Stores all information about a dependency declaration, including its name,
6/// version requirement, and source positions for LSP operations.
7/// Positions are critical for features like hover, completion, and inlay hints.
8///
9/// # Examples
10///
11/// ```
12/// use deps_npm::types::{NpmDependency, NpmDependencySection};
13/// use tower_lsp_server::ls_types::{Position, Range};
14///
15/// let dep = NpmDependency {
16///     name: "express".into(),
17///     name_range: Range::new(Position::new(5, 4), Position::new(5, 13)),
18///     version_req: Some("^4.18.2".into()),
19///     version_range: Some(Range::new(Position::new(5, 16), Position::new(5, 25))),
20///     section: NpmDependencySection::Dependencies,
21/// };
22///
23/// assert_eq!(dep.name, "express");
24/// assert!(matches!(dep.section, NpmDependencySection::Dependencies));
25/// ```
26#[derive(Debug, Clone, PartialEq, Eq)]
27pub struct NpmDependency {
28    pub name: String,
29    pub name_range: Range,
30    pub version_req: Option<String>,
31    pub version_range: Option<Range>,
32    pub section: NpmDependencySection,
33}
34
35// Use macro to implement DependencyInfo and Dependency traits
36deps_core::impl_dependency!(NpmDependency {
37    name: name,
38    name_range: name_range,
39    version: version_req,
40    version_range: version_range,
41});
42
43/// Section in package.json where a dependency is declared.
44///
45/// npm supports multiple dependency sections:
46/// - `dependencies`: Production dependencies
47/// - `devDependencies`: Development-only dependencies
48/// - `peerDependencies`: Peer dependency requirements
49/// - `optionalDependencies`: Optional dependencies (install failures ignored)
50///
51/// # Examples
52///
53/// ```
54/// use deps_npm::types::NpmDependencySection;
55///
56/// let section = NpmDependencySection::Dependencies;
57/// assert!(matches!(section, NpmDependencySection::Dependencies));
58/// ```
59#[derive(Debug, Clone, Copy, PartialEq, Eq)]
60pub enum NpmDependencySection {
61    /// Production dependencies (`dependencies`)
62    Dependencies,
63    /// Development dependencies (`devDependencies`)
64    DevDependencies,
65    /// Peer dependencies (`peerDependencies`)
66    PeerDependencies,
67    /// Optional dependencies (`optionalDependencies`)
68    OptionalDependencies,
69}
70
71/// Version information for an npm package.
72///
73/// Retrieved from the npm registry API at `https://registry.npmjs.org/{package}`.
74/// Contains version number and deprecation status.
75///
76/// # Examples
77///
78/// ```
79/// use deps_npm::types::NpmVersion;
80///
81/// let version = NpmVersion {
82///     version: "4.18.2".into(),
83///     deprecated: false,
84/// };
85///
86/// assert!(!version.deprecated);
87/// ```
88#[derive(Debug, Clone)]
89pub struct NpmVersion {
90    pub version: String,
91    pub deprecated: bool,
92}
93
94// Use macro to implement VersionInfo and Version traits
95deps_core::impl_version!(NpmVersion {
96    version: version,
97    yanked: deprecated,
98});
99
100/// Package metadata from npm registry.
101///
102/// Contains basic information about an npm package for display in completion
103/// suggestions. Retrieved from `https://registry.npmjs.org/-/v1/search?text={query}`.
104///
105/// # Examples
106///
107/// ```
108/// use deps_npm::types::NpmPackage;
109///
110/// let pkg = NpmPackage {
111///     name: "express".into(),
112///     description: Some("Fast, unopinionated, minimalist web framework".into()),
113///     homepage: Some("http://expressjs.com/".into()),
114///     repository: Some("expressjs/express".into()),
115///     latest_version: "4.18.2".into(),
116/// };
117///
118/// assert_eq!(pkg.name, "express");
119/// ```
120#[derive(Debug, Clone)]
121pub struct NpmPackage {
122    pub name: String,
123    pub description: Option<String>,
124    pub homepage: Option<String>,
125    pub repository: Option<String>,
126    pub latest_version: String,
127}
128
129// Use macro to implement PackageMetadata and Metadata traits
130deps_core::impl_metadata!(NpmPackage {
131    name: name,
132    description: description,
133    repository: repository,
134    documentation: homepage,
135    latest_version: latest_version,
136});
137
138#[cfg(test)]
139mod tests {
140    use super::*;
141    use deps_core::{PackageMetadata, VersionInfo};
142    use tower_lsp_server::ls_types::Position;
143
144    #[test]
145    fn test_npm_dependency_creation() {
146        let dep = NpmDependency {
147            name: "react".into(),
148            name_range: Range::new(Position::new(0, 0), Position::new(0, 5)),
149            version_req: Some("^18.0.0".into()),
150            version_range: Some(Range::new(Position::new(0, 8), Position::new(0, 16))),
151            section: NpmDependencySection::Dependencies,
152        };
153
154        assert_eq!(dep.name, "react");
155        assert_eq!(dep.version_req, Some("^18.0.0".into()));
156    }
157
158    #[test]
159    fn test_dependency_section_variants() {
160        let deps = NpmDependencySection::Dependencies;
161        let dev_deps = NpmDependencySection::DevDependencies;
162        let peer_deps = NpmDependencySection::PeerDependencies;
163        let opt_deps = NpmDependencySection::OptionalDependencies;
164
165        assert!(matches!(deps, NpmDependencySection::Dependencies));
166        assert!(matches!(dev_deps, NpmDependencySection::DevDependencies));
167        assert!(matches!(peer_deps, NpmDependencySection::PeerDependencies));
168        assert!(matches!(
169            opt_deps,
170            NpmDependencySection::OptionalDependencies
171        ));
172    }
173
174    #[test]
175    fn test_npm_version_creation() {
176        let version = NpmVersion {
177            version: "1.0.0".into(),
178            deprecated: false,
179        };
180
181        assert_eq!(version.version, "1.0.0");
182        assert!(!version.deprecated);
183    }
184
185    #[test]
186    fn test_npm_version_info_trait() {
187        let version = NpmVersion {
188            version: "2.0.0".into(),
189            deprecated: true,
190        };
191
192        assert_eq!(version.version_string(), "2.0.0");
193        assert!(version.is_yanked());
194    }
195
196    #[test]
197    fn test_npm_package_creation() {
198        let pkg = NpmPackage {
199            name: "lodash".into(),
200            description: Some("Lodash utility library".into()),
201            homepage: Some("https://lodash.com/".into()),
202            repository: Some("lodash/lodash".into()),
203            latest_version: "4.17.21".into(),
204        };
205
206        assert_eq!(pkg.name, "lodash");
207        assert_eq!(pkg.latest_version, "4.17.21");
208    }
209
210    #[test]
211    fn test_npm_package_metadata_trait() {
212        let pkg = NpmPackage {
213            name: "axios".into(),
214            description: Some("Promise based HTTP client".into()),
215            homepage: Some("https://axios-http.com".into()),
216            repository: Some("axios/axios".into()),
217            latest_version: "1.6.0".into(),
218        };
219
220        assert_eq!(pkg.name(), "axios");
221        assert_eq!(pkg.description(), Some("Promise based HTTP client"));
222        assert_eq!(pkg.repository(), Some("axios/axios"));
223        assert_eq!(pkg.documentation(), Some("https://axios-http.com"));
224        assert_eq!(pkg.latest_version(), "1.6.0");
225    }
226}