#[macro_export]
macro_rules! impl_dependency {
($type:ty {
name: $name:ident,
name_range: $name_range:ident,
version: $version:ident,
version_range: $version_range:ident $(,)?
}) => {
$crate::impl_dependency!($type {
name: $name,
name_range: $name_range,
version: $version,
version_range: $version_range,
source: $crate::parser::DependencySource::Registry,
});
};
($type:ty {
name: $name:ident,
name_range: $name_range:ident,
version: $version:ident,
version_range: $version_range:ident,
source: $source:expr $(,)?
}) => {
impl $crate::parser::DependencyInfo for $type {
fn name(&self) -> &str {
&self.$name
}
fn name_range(&self) -> ::tower_lsp_server::ls_types::Range {
self.$name_range
}
fn version_requirement(&self) -> Option<&str> {
self.$version.as_deref()
}
fn version_range(&self) -> Option<::tower_lsp_server::ls_types::Range> {
self.$version_range
}
fn source(&self) -> $crate::parser::DependencySource {
$source
}
}
impl $crate::ecosystem::Dependency for $type {
fn name(&self) -> &str {
&self.$name
}
fn name_range(&self) -> ::tower_lsp_server::ls_types::Range {
self.$name_range
}
fn version_requirement(&self) -> Option<&str> {
self.$version.as_deref()
}
fn version_range(&self) -> Option<::tower_lsp_server::ls_types::Range> {
self.$version_range
}
fn source(&self) -> $crate::parser::DependencySource {
$source
}
fn as_any(&self) -> &dyn ::std::any::Any {
self
}
}
};
}
#[macro_export]
macro_rules! impl_version {
($type:ty {
version: $version:ident,
yanked: $yanked:ident $(,)?
}) => {
impl $crate::registry::Version for $type {
fn version_string(&self) -> &str {
&self.$version
}
fn is_yanked(&self) -> bool {
self.$yanked
}
fn as_any(&self) -> &dyn ::std::any::Any {
self
}
}
};
}
#[macro_export]
macro_rules! impl_metadata {
($type:ty {
name: $name:ident,
description: $description:ident,
repository: $repository:ident,
documentation: $documentation:ident,
latest_version: $latest_version:ident $(,)?
}) => {
impl $crate::registry::Metadata for $type {
fn name(&self) -> &str {
&self.$name
}
fn description(&self) -> Option<&str> {
self.$description.as_deref()
}
fn repository(&self) -> Option<&str> {
self.$repository.as_deref()
}
fn documentation(&self) -> Option<&str> {
self.$documentation.as_deref()
}
fn latest_version(&self) -> &str {
&self.$latest_version
}
fn as_any(&self) -> &dyn ::std::any::Any {
self
}
}
};
}
#[macro_export]
macro_rules! impl_parse_result {
($type:ty, $dep_type:ty {
dependencies: $dependencies:ident,
uri: $uri:ident $(,)?
}) => {
impl $crate::ecosystem::ParseResult for $type {
fn dependencies(&self) -> Vec<&dyn $crate::ecosystem::Dependency> {
self.$dependencies
.iter()
.map(|d| d as &dyn $crate::ecosystem::Dependency)
.collect()
}
fn workspace_root(&self) -> Option<&::std::path::Path> {
None
}
fn uri(&self) -> &::tower_lsp_server::ls_types::Uri {
&self.$uri
}
fn as_any(&self) -> &dyn ::std::any::Any {
self
}
}
};
($type:ty, $dep_type:ty {
dependencies: $dependencies:ident,
uri: $uri:ident,
workspace_root: $workspace_root:ident $(,)?
}) => {
impl $crate::ecosystem::ParseResult for $type {
fn dependencies(&self) -> Vec<&dyn $crate::ecosystem::Dependency> {
self.$dependencies
.iter()
.map(|d| d as &dyn $crate::ecosystem::Dependency)
.collect()
}
fn workspace_root(&self) -> Option<&::std::path::Path> {
self.$workspace_root.as_deref()
}
fn uri(&self) -> &::tower_lsp_server::ls_types::Uri {
&self.$uri
}
fn as_any(&self) -> &dyn ::std::any::Any {
self
}
}
};
}
#[macro_export]
macro_rules! delegate_to_variants {
($self:ident, $method:ident $(, $arg:expr)*) => {
match $self {
Self::Cargo(dep) => dep.$method($($arg),*),
Self::Npm(dep) => dep.$method($($arg),*),
Self::Pypi(dep) => dep.$method($($arg),*),
}
};
}
#[cfg(test)]
mod tests {
use tower_lsp_server::ls_types::{Position, Range, Uri};
#[derive(Debug, Clone)]
struct TestDependency {
name: String,
name_range: Range,
version_req: Option<String>,
version_range: Option<Range>,
}
#[derive(Debug, Clone)]
struct TestVersion {
version: String,
yanked: bool,
}
#[derive(Debug, Clone)]
struct TestPackage {
name: String,
description: Option<String>,
repository: Option<String>,
homepage: Option<String>,
latest_version: String,
}
#[derive(Debug)]
struct TestParseResult {
dependencies: Vec<TestDependency>,
uri: Uri,
}
impl_dependency!(TestDependency {
name: name,
name_range: name_range,
version: version_req,
version_range: version_range,
});
impl_version!(TestVersion {
version: version,
yanked: yanked,
});
impl_metadata!(TestPackage {
name: name,
description: description,
repository: repository,
documentation: homepage,
latest_version: latest_version,
});
impl_parse_result!(
TestParseResult,
TestDependency {
dependencies: dependencies,
uri: uri,
}
);
#[test]
fn test_impl_dependency_macro() {
use crate::ecosystem::Dependency;
let dep = TestDependency {
name: "test-pkg".into(),
name_range: Range::new(Position::new(0, 0), Position::new(0, 8)),
version_req: Some("1.0.0".into()),
version_range: Some(Range::new(Position::new(0, 10), Position::new(0, 15))),
};
assert_eq!(dep.name(), "test-pkg");
assert_eq!(dep.version_requirement(), Some("1.0.0"));
assert!(dep.as_any().is::<TestDependency>());
}
#[test]
fn test_impl_version_macro() {
use crate::registry::Version;
let version = TestVersion {
version: "2.0.0".into(),
yanked: true,
};
assert_eq!(version.version_string(), "2.0.0");
assert!(version.is_yanked());
assert!(version.as_any().is::<TestVersion>());
}
#[test]
fn test_impl_metadata_macro() {
use crate::registry::Metadata;
let pkg = TestPackage {
name: "my-pkg".into(),
description: Some("A test package".into()),
repository: Some("user/repo".into()),
homepage: Some("https://example.com".into()),
latest_version: "3.0.0".into(),
};
assert_eq!(pkg.name(), "my-pkg");
assert_eq!(pkg.description(), Some("A test package"));
assert_eq!(pkg.documentation(), Some("https://example.com"));
assert!(pkg.as_any().is::<TestPackage>());
}
#[test]
fn test_impl_parse_result_macro() {
use crate::ecosystem::ParseResult;
let result = TestParseResult {
dependencies: vec![TestDependency {
name: "dep1".into(),
name_range: Range::default(),
version_req: None,
version_range: None,
}],
uri: Uri::from_file_path("/test").unwrap(),
};
assert_eq!(result.dependencies().len(), 1);
assert!(result.workspace_root().is_none());
assert!(result.as_any().is::<TestParseResult>());
}
}