use crate::error::Result;
use tower_lsp_server::ls_types::{Range, Uri};
pub trait ManifestParser: Send + Sync {
type Dependency: DependencyInfo + Clone + Send + Sync;
type ParseResult: ParseResultInfo<Dependency = Self::Dependency> + Send;
fn parse(&self, content: &str, doc_uri: &Uri) -> Result<Self::ParseResult>;
}
pub trait DependencyInfo {
fn name(&self) -> &str;
fn name_range(&self) -> Range;
fn version_requirement(&self) -> Option<&str>;
fn version_range(&self) -> Option<Range>;
fn source(&self) -> DependencySource;
fn features(&self) -> &[String] {
&[]
}
}
pub trait ParseResultInfo {
type Dependency: DependencyInfo;
fn dependencies(&self) -> &[Self::Dependency];
fn workspace_root(&self) -> Option<&std::path::Path>;
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum DependencySource {
Registry,
Git {
url: String,
rev: Option<String>,
},
Path { path: String },
Url { url: String },
Sdk { sdk: String },
Workspace,
CustomRegistry { url: String },
}
impl DependencySource {
pub fn is_registry(&self) -> bool {
matches!(self, Self::Registry | Self::CustomRegistry { .. })
}
pub fn is_version_resolvable(&self) -> bool {
self.is_registry()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum LoadingState {
#[default]
Idle,
Loading,
Loaded,
Failed,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_dependency_source_registry() {
let source = DependencySource::Registry;
assert_eq!(source, DependencySource::Registry);
assert!(source.is_registry());
assert!(source.is_version_resolvable());
}
#[test]
fn test_dependency_source_git() {
let source = DependencySource::Git {
url: "https://github.com/user/repo".into(),
rev: Some("main".into()),
};
assert!(!source.is_registry());
assert!(!source.is_version_resolvable());
match source {
DependencySource::Git { url, rev } => {
assert_eq!(url, "https://github.com/user/repo");
assert_eq!(rev, Some("main".into()));
}
_ => panic!("Expected Git source"),
}
}
#[test]
fn test_dependency_source_git_no_rev() {
let source = DependencySource::Git {
url: "https://github.com/user/repo".into(),
rev: None,
};
match source {
DependencySource::Git { url, rev } => {
assert_eq!(url, "https://github.com/user/repo");
assert!(rev.is_none());
}
_ => panic!("Expected Git source"),
}
}
#[test]
fn test_dependency_source_path() {
let source = DependencySource::Path {
path: "../local-crate".into(),
};
assert!(!source.is_registry());
match source {
DependencySource::Path { path } => {
assert_eq!(path, "../local-crate");
}
_ => panic!("Expected Path source"),
}
}
#[test]
fn test_dependency_source_url() {
let source = DependencySource::Url {
url: "https://example.com/package.whl".into(),
};
assert!(!source.is_registry());
assert!(!source.is_version_resolvable());
}
#[test]
fn test_dependency_source_sdk() {
let source = DependencySource::Sdk {
sdk: "flutter".into(),
};
assert!(!source.is_registry());
}
#[test]
fn test_dependency_source_workspace() {
let source = DependencySource::Workspace;
assert!(!source.is_registry());
assert!(!source.is_version_resolvable());
}
#[test]
fn test_dependency_source_custom_registry() {
let source = DependencySource::CustomRegistry {
url: "https://gems.example.com".into(),
};
assert!(source.is_registry());
assert!(source.is_version_resolvable());
}
#[test]
fn test_dependency_source_clone() {
let source1 = DependencySource::Git {
url: "https://example.com/repo".into(),
rev: Some("v1.0".into()),
};
let source2 = source1.clone();
assert_eq!(source1, source2);
}
#[test]
fn test_dependency_source_equality() {
let reg1 = DependencySource::Registry;
let reg2 = DependencySource::Registry;
assert_eq!(reg1, reg2);
let git1 = DependencySource::Git {
url: "https://example.com".into(),
rev: None,
};
let git2 = DependencySource::Git {
url: "https://example.com".into(),
rev: None,
};
assert_eq!(git1, git2);
let git3 = DependencySource::Git {
url: "https://different.com".into(),
rev: None,
};
assert_ne!(git1, git3);
}
#[test]
fn test_dependency_source_debug() {
let source = DependencySource::Registry;
let debug = format!("{:?}", source);
assert_eq!(debug, "Registry");
let git = DependencySource::Git {
url: "https://example.com".into(),
rev: Some("main".into()),
};
let git_debug = format!("{:?}", git);
assert!(git_debug.contains("https://example.com"));
assert!(git_debug.contains("main"));
}
#[test]
fn test_loading_state_default() {
assert_eq!(LoadingState::default(), LoadingState::Idle);
}
#[test]
fn test_loading_state_copy() {
let state = LoadingState::Loading;
let copied = state;
assert_eq!(state, copied);
}
#[test]
fn test_loading_state_debug() {
let debug_str = format!("{:?}", LoadingState::Loading);
assert_eq!(debug_str, "Loading");
}
#[test]
fn test_loading_state_all_variants() {
let variants = [
LoadingState::Idle,
LoadingState::Loading,
LoadingState::Loaded,
LoadingState::Failed,
];
for (i, v1) in variants.iter().enumerate() {
for (j, v2) in variants.iter().enumerate() {
if i == j {
assert_eq!(v1, v2);
} else {
assert_ne!(v1, v2);
}
}
}
}
}