use std::any::Any;
use tower_lsp_server::ls_types::Range;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BundlerDependency {
pub name: String,
pub name_range: Range,
pub version_req: Option<String>,
pub version_range: Option<Range>,
pub group: DependencyGroup,
pub source: DependencySource,
pub platforms: Vec<String>,
pub require: Option<String>,
}
pub use deps_core::parser::DependencySource;
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub enum DependencyGroup {
#[default]
Default,
Development,
Test,
Production,
Custom(String),
}
#[derive(Debug, Clone)]
pub struct BundlerVersion {
pub number: String,
pub prerelease: bool,
pub yanked: bool,
pub created_at: Option<String>,
pub platform: String,
}
impl BundlerVersion {
pub fn is_stable(&self) -> bool {
!self.prerelease && !self.yanked
}
}
#[derive(Debug, Clone)]
pub struct GemInfo {
pub name: String,
pub info: Option<String>,
pub homepage_uri: Option<String>,
pub source_code_uri: Option<String>,
pub documentation_uri: Option<String>,
pub version: String,
pub licenses: Vec<String>,
pub authors: Option<String>,
pub downloads: u64,
}
impl deps_core::DependencyInfo for BundlerDependency {
fn name(&self) -> &str {
&self.name
}
fn name_range(&self) -> Range {
self.name_range
}
fn version_requirement(&self) -> Option<&str> {
self.version_req.as_deref()
}
fn version_range(&self) -> Option<Range> {
self.version_range
}
fn source(&self) -> deps_core::parser::DependencySource {
self.source.clone()
}
fn features(&self) -> &[String] {
&[]
}
}
impl deps_core::Dependency for BundlerDependency {
fn name(&self) -> &str {
&self.name
}
fn name_range(&self) -> Range {
self.name_range
}
fn version_requirement(&self) -> Option<&str> {
self.version_req.as_deref()
}
fn version_range(&self) -> Option<Range> {
self.version_range
}
fn source(&self) -> deps_core::parser::DependencySource {
self.source.clone()
}
fn features(&self) -> &[String] {
&[]
}
fn as_any(&self) -> &dyn Any {
self
}
}
#[cfg(test)]
mod tests {
use super::*;
use tower_lsp_server::ls_types::Position;
fn create_test_dependency(source: DependencySource) -> BundlerDependency {
BundlerDependency {
name: "test_gem".into(),
name_range: Range::new(Position::new(1, 5), Position::new(1, 13)),
version_req: Some("~> 1.0".into()),
version_range: Some(Range::new(Position::new(1, 17), Position::new(1, 23))),
group: DependencyGroup::Default,
source,
platforms: vec![],
require: None,
}
}
#[test]
fn test_dependency_source_variants() {
let registry = DependencySource::Registry;
let git = DependencySource::Git {
url: "https://github.com/rails/rails".into(),
rev: Some("main".into()),
};
let path = DependencySource::Path {
path: "../local".into(),
};
let custom = DependencySource::CustomRegistry {
url: "https://custom.gem.source".into(),
};
assert!(registry.is_registry());
assert!(!git.is_registry());
assert!(!path.is_registry());
assert!(custom.is_registry());
}
#[test]
fn test_dependency_group_variants() {
let default = DependencyGroup::Default;
let dev = DependencyGroup::Development;
let test = DependencyGroup::Test;
let prod = DependencyGroup::Production;
let custom = DependencyGroup::Custom("staging".into());
assert!(matches!(default, DependencyGroup::Default));
assert!(matches!(dev, DependencyGroup::Development));
assert!(matches!(test, DependencyGroup::Test));
assert!(matches!(prod, DependencyGroup::Production));
assert!(matches!(custom, DependencyGroup::Custom(_)));
}
#[test]
fn test_dependency_group_default() {
let group = DependencyGroup::default();
assert!(matches!(group, DependencyGroup::Default));
}
#[test]
fn test_bundler_version_creation() {
let version = BundlerVersion {
number: "7.0.8".into(),
prerelease: false,
yanked: false,
created_at: Some("2023-09-09".into()),
platform: "ruby".into(),
};
assert_eq!(version.number, "7.0.8");
assert!(!version.yanked);
assert!(version.is_stable());
}
#[test]
fn test_bundler_version_prerelease() {
let version = BundlerVersion {
number: "7.1.0.beta1".into(),
prerelease: true,
yanked: false,
created_at: None,
platform: "ruby".into(),
};
assert!(version.prerelease);
assert!(!version.is_stable());
}
#[test]
fn test_bundler_version_yanked() {
let version = BundlerVersion {
number: "1.0.0".into(),
prerelease: false,
yanked: true,
created_at: None,
platform: "ruby".into(),
};
assert!(version.yanked);
assert!(!version.is_stable());
}
#[test]
fn test_bundler_dependency_trait() {
use deps_core::Dependency;
let dep = BundlerDependency {
name: "rails".into(),
name_range: Range::new(Position::new(1, 5), Position::new(1, 10)),
version_req: Some("~> 7.0".into()),
version_range: Some(Range::new(Position::new(1, 14), Position::new(1, 20))),
group: DependencyGroup::Default,
source: DependencySource::Registry,
platforms: vec![],
require: None,
};
assert_eq!(dep.name(), "rails");
assert_eq!(dep.version_requirement(), Some("~> 7.0"));
}
#[test]
fn test_dependency_info_trait_registry() {
use deps_core::DependencyInfo;
let dep = create_test_dependency(DependencySource::Registry);
assert_eq!(dep.name(), "test_gem");
assert_eq!(dep.name_range().start.line, 1);
assert_eq!(dep.version_requirement(), Some("~> 1.0"));
assert!(dep.version_range().is_some());
assert!(dep.features().is_empty());
assert!(dep.source().is_registry());
}
#[test]
fn test_dependency_info_trait_git() {
use deps_core::DependencyInfo;
let dep = create_test_dependency(DependencySource::Git {
url: "https://github.com/rails/rails".into(),
rev: Some("abc123".into()),
});
match dep.source() {
deps_core::parser::DependencySource::Git { url, rev } => {
assert_eq!(url, "https://github.com/rails/rails");
assert_eq!(rev, Some("abc123".to_string()));
}
_ => panic!("Expected Git source"),
}
}
#[test]
fn test_dependency_info_trait_path() {
use deps_core::DependencyInfo;
let dep = create_test_dependency(DependencySource::Path {
path: "../local_gem".into(),
});
match dep.source() {
deps_core::parser::DependencySource::Path { path } => {
assert_eq!(path, "../local_gem");
}
_ => panic!("Expected Path source"),
}
}
#[test]
fn test_dependency_info_trait_custom_source() {
use deps_core::DependencyInfo;
let dep = create_test_dependency(DependencySource::CustomRegistry {
url: "https://gems.example.com".into(),
});
assert!(dep.source().is_registry());
}
#[test]
fn test_dependency_trait_as_any() {
use deps_core::Dependency;
let dep = create_test_dependency(DependencySource::Registry);
let any = dep.as_any();
assert!(any.is::<BundlerDependency>());
assert!(any.downcast_ref::<BundlerDependency>().is_some());
}
#[test]
fn test_dependency_trait_source_conversions() {
use deps_core::Dependency;
let sources = vec![
DependencySource::Registry,
DependencySource::Git {
url: "https://github.com/test/repo".into(),
rev: Some("v1.0".into()),
},
DependencySource::Path {
path: "./local".into(),
},
DependencySource::CustomRegistry {
url: "https://custom.example.com".into(),
},
];
for source in sources {
let dep = create_test_dependency(source);
let _ = dep.source();
}
}
#[test]
fn test_dependency_without_version() {
use deps_core::Dependency;
let dep = BundlerDependency {
name: "test".into(),
name_range: Range::default(),
version_req: None,
version_range: None,
group: DependencyGroup::Default,
source: DependencySource::Registry,
platforms: vec![],
require: None,
};
assert_eq!(dep.name(), "test");
assert!(dep.version_requirement().is_none());
assert!(dep.version_range().is_none());
}
#[test]
fn test_version_trait_implementation() {
use deps_core::Version;
let version = BundlerVersion {
number: "1.2.3".into(),
prerelease: false,
yanked: false,
created_at: Some("2024-01-01".into()),
platform: "ruby".into(),
};
assert_eq!(version.version_string(), "1.2.3");
assert!(!version.is_yanked());
assert!(version.features().is_empty());
assert!(version.as_any().is::<BundlerVersion>());
}
#[test]
fn test_version_trait_yanked() {
use deps_core::Version;
let version = BundlerVersion {
number: "1.0.0".into(),
prerelease: false,
yanked: true,
created_at: None,
platform: "ruby".into(),
};
assert!(version.is_yanked());
}
#[test]
fn test_metadata_trait_full() {
use deps_core::Metadata;
let gem = GemInfo {
name: "rails".into(),
info: Some("Full-stack web application framework".into()),
homepage_uri: Some("https://rubyonrails.org".into()),
source_code_uri: Some("https://github.com/rails/rails".into()),
documentation_uri: Some("https://api.rubyonrails.org".into()),
version: "7.0.8".into(),
licenses: vec!["MIT".into()],
authors: Some("DHH".into()),
downloads: 500_000_000,
};
assert_eq!(gem.name(), "rails");
assert_eq!(
gem.description(),
Some("Full-stack web application framework")
);
assert_eq!(gem.repository(), Some("https://github.com/rails/rails"));
assert_eq!(gem.documentation(), Some("https://api.rubyonrails.org"));
assert_eq!(gem.latest_version(), "7.0.8");
assert!(gem.as_any().is::<GemInfo>());
}
#[test]
fn test_metadata_trait_minimal() {
use deps_core::Metadata;
let gem = GemInfo {
name: "minimal".into(),
info: None,
homepage_uri: None,
source_code_uri: None,
documentation_uri: None,
version: "0.1.0".into(),
licenses: vec![],
authors: None,
downloads: 0,
};
assert_eq!(gem.name(), "minimal");
assert!(gem.description().is_none());
assert!(gem.repository().is_none());
assert!(gem.documentation().is_none());
assert_eq!(gem.latest_version(), "0.1.0");
}
#[test]
fn test_gem_info_fields() {
let gem = GemInfo {
name: "test".into(),
info: Some("Test gem".into()),
homepage_uri: Some("https://example.com".into()),
source_code_uri: Some("https://github.com/test/test".into()),
documentation_uri: Some("https://docs.example.com".into()),
version: "1.0.0".into(),
licenses: vec!["MIT".into(), "Apache-2.0".into()],
authors: Some("Test Author".into()),
downloads: 1000,
};
assert_eq!(gem.licenses.len(), 2);
assert_eq!(gem.authors, Some("Test Author".into()));
assert_eq!(gem.downloads, 1000);
}
#[test]
fn test_bundler_dependency_clone() {
let dep = create_test_dependency(DependencySource::Registry);
let cloned = dep.clone();
assert_eq!(dep, cloned);
}
#[test]
fn test_bundler_dependency_debug() {
let dep = create_test_dependency(DependencySource::Registry);
let debug_str = format!("{:?}", dep);
assert!(debug_str.contains("test_gem"));
}
}