Skip to main content

deps_composer/
types.rs

1use tower_lsp_server::ls_types::Range;
2
3/// Parsed dependency from composer.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///
8/// # Examples
9///
10/// ```
11/// use deps_composer::types::{ComposerDependency, ComposerSection};
12/// use tower_lsp_server::ls_types::{Position, Range};
13///
14/// let dep = ComposerDependency {
15///     name: "symfony/console".into(),
16///     name_range: Range::new(Position::new(3, 4), Position::new(3, 20)),
17///     version_req: Some("^6.0".into()),
18///     version_range: Some(Range::new(Position::new(3, 23), Position::new(3, 28))),
19///     section: ComposerSection::Require,
20/// };
21///
22/// assert_eq!(dep.name, "symfony/console");
23/// assert!(matches!(dep.section, ComposerSection::Require));
24/// ```
25#[derive(Debug, Clone, PartialEq, Eq)]
26pub struct ComposerDependency {
27    pub name: String,
28    pub name_range: Range,
29    pub version_req: Option<String>,
30    pub version_range: Option<Range>,
31    pub section: ComposerSection,
32}
33
34deps_core::impl_dependency!(ComposerDependency {
35    name: name,
36    name_range: name_range,
37    version: version_req,
38    version_range: version_range,
39});
40
41/// Section in composer.json where a dependency is declared.
42///
43/// # Examples
44///
45/// ```
46/// use deps_composer::types::ComposerSection;
47///
48/// let section = ComposerSection::Require;
49/// assert!(matches!(section, ComposerSection::Require));
50/// ```
51#[derive(Debug, Clone, Copy, PartialEq, Eq)]
52pub enum ComposerSection {
53    /// Production dependencies (`require`)
54    Require,
55    /// Development dependencies (`require-dev`)
56    RequireDev,
57}
58
59/// Version information for a Packagist package.
60///
61/// Retrieved from the Packagist v2 API.
62/// Contains version number and abandonment status.
63///
64/// # Examples
65///
66/// ```
67/// use deps_composer::types::ComposerVersion;
68///
69/// let version = ComposerVersion {
70///     version: "6.0.0".into(),
71///     version_normalized: "6.0.0.0".into(),
72///     abandoned: false,
73/// };
74///
75/// assert!(!version.abandoned);
76/// ```
77#[derive(Debug, Clone)]
78pub struct ComposerVersion {
79    pub version: String,
80    pub version_normalized: String,
81    pub abandoned: bool,
82}
83
84deps_core::impl_version!(ComposerVersion {
85    version: version,
86    yanked: abandoned,
87});
88
89/// Package metadata from Packagist search.
90///
91/// Contains basic information about a Packagist package for display in
92/// completion suggestions.
93///
94/// # Examples
95///
96/// ```
97/// use deps_composer::types::ComposerPackage;
98///
99/// let pkg = ComposerPackage {
100///     name: "symfony/console".into(),
101///     description: Some("Symfony Console Component".into()),
102///     repository: Some("https://github.com/symfony/console".into()),
103///     homepage: Some("https://packagist.org/packages/symfony/console".into()),
104///     latest_version: "6.0.0".into(),
105/// };
106///
107/// assert_eq!(pkg.name, "symfony/console");
108/// ```
109#[derive(Debug, Clone)]
110pub struct ComposerPackage {
111    pub name: String,
112    pub description: Option<String>,
113    pub repository: Option<String>,
114    pub homepage: Option<String>,
115    pub latest_version: String,
116}
117
118deps_core::impl_metadata!(ComposerPackage {
119    name: name,
120    description: description,
121    repository: repository,
122    documentation: homepage,
123    latest_version: latest_version,
124});
125
126#[cfg(test)]
127mod tests {
128    use super::*;
129    use deps_core::{Metadata, Version};
130    use tower_lsp_server::ls_types::Position;
131
132    #[test]
133    fn test_composer_dependency_creation() {
134        let dep = ComposerDependency {
135            name: "symfony/console".into(),
136            name_range: Range::new(Position::new(0, 0), Position::new(0, 15)),
137            version_req: Some("^6.0".into()),
138            version_range: Some(Range::new(Position::new(0, 18), Position::new(0, 22))),
139            section: ComposerSection::Require,
140        };
141
142        assert_eq!(dep.name, "symfony/console");
143        assert_eq!(dep.version_req, Some("^6.0".into()));
144        assert!(matches!(dep.section, ComposerSection::Require));
145    }
146
147    #[test]
148    fn test_composer_section_variants() {
149        assert!(matches!(ComposerSection::Require, ComposerSection::Require));
150        assert!(matches!(
151            ComposerSection::RequireDev,
152            ComposerSection::RequireDev
153        ));
154    }
155
156    #[test]
157    fn test_composer_version_trait() {
158        let version = ComposerVersion {
159            version: "2.0.0".into(),
160            version_normalized: "2.0.0.0".into(),
161            abandoned: true,
162        };
163
164        assert_eq!(version.version_string(), "2.0.0");
165        assert!(version.is_yanked());
166    }
167
168    #[test]
169    fn test_composer_package_metadata_trait() {
170        let pkg = ComposerPackage {
171            name: "monolog/monolog".into(),
172            description: Some(
173                "Sends your logs to files, sockets, inboxes, databases and various web services"
174                    .into(),
175            ),
176            repository: Some("https://github.com/Seldaek/monolog".into()),
177            homepage: Some("https://packagist.org/packages/monolog/monolog".into()),
178            latest_version: "3.0.0".into(),
179        };
180
181        assert_eq!(pkg.name(), "monolog/monolog");
182        assert_eq!(pkg.latest_version(), "3.0.0");
183        assert_eq!(pkg.repository(), Some("https://github.com/Seldaek/monolog"));
184    }
185}