Skip to main content

thirdpass_core/extension/
common.rs

1use anyhow::Result;
2
3/// Error value used when an extension cannot resolve a dependency version.
4#[derive(
5    Debug, Clone, Hash, Eq, PartialEq, PartialOrd, Ord, serde::Serialize, serde::Deserialize,
6)]
7pub struct VersionError(String);
8
9impl VersionError {
10    /// Build an error for dependency metadata that omits a version.
11    pub fn from_missing_version() -> Self {
12        Self("Missing version number".to_string())
13    }
14
15    /// Build an error for a dependency version string that could not be parsed.
16    pub fn from_parse_error(raw_version_number: &str) -> Self {
17        Self(format!("Version parse error: {}", raw_version_number))
18    }
19}
20
21impl std::fmt::Display for VersionError {
22    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23        formatter.write_str(&self.0)
24    }
25}
26
27impl std::error::Error for VersionError {}
28
29/// Parsed dependency version or a version parsing error.
30pub type VersionParseResult = std::result::Result<String, VersionError>;
31
32/// A dependency as specified within a dependencies definition file.
33#[derive(
34    Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord, serde::Serialize, serde::Deserialize,
35)]
36pub struct Dependency {
37    /// Dependency package name.
38    pub name: String,
39    /// Parsed dependency version or a captured parse failure.
40    pub version: VersionParseResult,
41}
42
43/// Common view over dependency collections returned by extensions.
44pub trait DependenciesCollection: Sized {
45    /// Registry host that owns the dependencies.
46    fn registry_host_name(&self) -> &str;
47    /// Dependencies found for the registry or file.
48    fn dependencies(&self) -> &[Dependency];
49}
50
51/// Package dependencies found by querying a registry.
52#[derive(Clone, Debug, Hash, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
53pub struct PackageDependencies {
54    /// Package version, included when the extension resolves a missing version.
55    pub package_version: VersionParseResult,
56
57    /// Dependencies registry host name.
58    pub registry_host_name: String,
59
60    /// Dependencies specified within the dependencies specification file.
61    pub dependencies: Vec<Dependency>,
62}
63
64impl DependenciesCollection for PackageDependencies {
65    fn registry_host_name(&self) -> &str {
66        &self.registry_host_name
67    }
68
69    fn dependencies(&self) -> &[Dependency] {
70        &self.dependencies
71    }
72}
73
74/// A dependencies specification file found from inspecting the local filesystem.
75#[derive(Clone, Debug, Hash, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
76pub struct FileDefinedDependencies {
77    /// Absolute file path for dependencies specification file.
78    pub path: std::path::PathBuf,
79
80    /// Dependencies registry host name.
81    pub registry_host_name: String,
82
83    /// Dependencies specified within the dependencies specification file.
84    pub dependencies: Vec<Dependency>,
85}
86
87impl DependenciesCollection for FileDefinedDependencies {
88    fn registry_host_name(&self) -> &str {
89        &self.registry_host_name
90    }
91
92    fn dependencies(&self) -> &[Dependency] {
93        &self.dependencies
94    }
95}
96
97/// Metadata returned by an extension for a package in a registry.
98#[derive(Debug, Clone, Hash, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
99pub struct RegistryPackageMetadata {
100    /// Registry host name such as `crates.io` or `npmjs.com`.
101    pub registry_host_name: String,
102    /// Human-readable package URL.
103    pub human_url: String,
104    /// Download URL for the package source artifact.
105    pub artifact_url: String,
106    /// True when this metadata describes the primary registry hit.
107    pub is_primary: bool,
108    /// Package version, included when the extension resolves a missing version.
109    pub package_version: String,
110}
111
112/// Policy for selecting automatic review target files.
113#[derive(Debug, Clone, Default, Hash, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
114pub struct ReviewTargetPolicy {
115    /// Exact package-relative paths to exclude from automatic target selection.
116    pub excluded_exact_paths: Vec<String>,
117}
118
119impl ReviewTargetPolicy {
120    /// Return true when this policy excludes the exact package-relative path.
121    pub fn excludes_exact_path(&self, package_relative_path: &str) -> bool {
122        self.excluded_exact_paths
123            .iter()
124            .any(|excluded_path| excluded_path == package_relative_path)
125    }
126
127    /// Return true when this policy excludes the package-relative path.
128    pub fn excludes_path(&self, package_relative_path: &std::path::Path) -> bool {
129        self.excludes_exact_path(&package_relative_path_string(package_relative_path))
130    }
131}
132
133fn package_relative_path_string(package_relative_path: &std::path::Path) -> String {
134    if package_relative_path.as_os_str().is_empty() {
135        return ".".to_string();
136    }
137
138    package_relative_path
139        .iter()
140        .map(|component| component.to_string_lossy().into_owned())
141        .collect::<Vec<_>>()
142        .join("/")
143}
144
145/// Extension implementation that is compiled directly into the current process.
146pub trait FromLib: Extension + Send + Sync {
147    /// Initialize extension from a library.
148    fn new() -> Self
149    where
150        Self: Sized;
151}
152
153/// Extension implementation that is loaded by invoking a process.
154pub trait FromProcess: Extension + Send + Sync {
155    /// Initialize extension from a process.
156    fn from_process(
157        process_path: &std::path::Path,
158        extension_config_path: &std::path::Path,
159    ) -> Result<Self>
160    where
161        Self: Sized;
162}
163
164/// Registry and dependency behavior implemented by every Thirdpass extension.
165pub trait Extension: Send + Sync {
166    /// Return the extension short name.
167    fn name(&self) -> String;
168
169    /// Return registry host names supported by this extension.
170    fn registries(&self) -> Vec<String>;
171
172    /// Return automatic review-target selection policy for this extension.
173    fn review_target_policy(&self) -> ReviewTargetPolicy {
174        ReviewTargetPolicy::default()
175    }
176
177    /// Identify specific package dependencies.
178    fn identify_package_dependencies(
179        &self,
180        package_name: &str,
181        package_version: &Option<&str>,
182        extension_args: &[String],
183    ) -> Result<Vec<PackageDependencies>>;
184
185    /// Identify file defined dependencies.
186    fn identify_file_defined_dependencies(
187        &self,
188        working_directory: &std::path::Path,
189        extension_args: &[String],
190    ) -> Result<Vec<FileDefinedDependencies>>;
191
192    /// Query package registries for package metadata.
193    fn registries_package_metadata(
194        &self,
195        package_name: &str,
196        package_version: &Option<&str>,
197    ) -> Result<Vec<RegistryPackageMetadata>>;
198}