use std::path::PathBuf;
use crate::loader::LeanLibraryDependency;
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct LeanBuiltCapability {
dylib_path: Option<PathBuf>,
env_var: Option<String>,
manifest_path: Option<PathBuf>,
manifest_env_var: Option<String>,
package: Option<String>,
module: Option<String>,
dependencies: Vec<LeanLibraryDependency>,
}
impl LeanBuiltCapability {
#[must_use]
pub fn path(path: impl Into<PathBuf>) -> Self {
Self {
dylib_path: Some(path.into()),
env_var: None,
manifest_path: None,
manifest_env_var: None,
package: None,
module: None,
dependencies: Vec::new(),
}
}
#[must_use]
pub fn env(env_var: impl Into<String>) -> Self {
Self {
dylib_path: None,
env_var: Some(env_var.into()),
manifest_path: None,
manifest_env_var: None,
package: None,
module: None,
dependencies: Vec::new(),
}
}
#[must_use]
pub fn manifest_path(path: impl Into<PathBuf>) -> Self {
Self {
dylib_path: None,
env_var: None,
manifest_path: Some(path.into()),
manifest_env_var: None,
package: None,
module: None,
dependencies: Vec::new(),
}
}
#[must_use]
pub fn manifest_env(env_var: impl Into<String>) -> Self {
Self {
dylib_path: None,
env_var: None,
manifest_path: None,
manifest_env_var: Some(env_var.into()),
package: None,
module: None,
dependencies: Vec::new(),
}
}
#[must_use]
pub fn env_var(mut self, env_var: impl Into<String>) -> Self {
self.env_var = Some(env_var.into());
self
}
#[must_use]
pub fn manifest_env_var(mut self, env_var: impl Into<String>) -> Self {
self.manifest_env_var = Some(env_var.into());
self
}
#[must_use]
pub fn package(mut self, package: impl Into<String>) -> Self {
self.package = Some(package.into());
self
}
#[must_use]
pub fn module(mut self, module: impl Into<String>) -> Self {
self.module = Some(module.into());
self
}
#[must_use]
pub fn dependency(mut self, dependency: LeanLibraryDependency) -> Self {
self.dependencies.push(dependency);
self
}
#[must_use]
pub fn dependencies(mut self, dependencies: impl IntoIterator<Item = LeanLibraryDependency>) -> Self {
self.dependencies.extend(dependencies);
self
}
#[must_use]
pub fn package_name(&self) -> Option<&str> {
self.package.as_deref()
}
#[must_use]
pub fn module_name(&self) -> Option<&str> {
self.module.as_deref()
}
pub fn take_package_name(&mut self) -> Option<String> {
self.package.take()
}
pub fn take_module_name(&mut self) -> Option<String> {
self.module.take()
}
pub fn take_dependencies(&mut self) -> Vec<LeanLibraryDependency> {
std::mem::take(&mut self.dependencies)
}
#[must_use]
pub fn dependency_descriptors(&self) -> &[LeanLibraryDependency] {
&self.dependencies
}
pub fn dylib_path(&self) -> Result<PathBuf, LeanBuiltCapabilityError> {
if let Some(path) = &self.dylib_path {
return Ok(path.clone());
}
let env_var = self
.env_var
.as_deref()
.ok_or(LeanBuiltCapabilityError::MissingDylibSource)?;
std::env::var_os(env_var)
.map(PathBuf::from)
.ok_or_else(|| LeanBuiltCapabilityError::EnvVarNotSet {
env_var: env_var.to_owned(),
kind: BuiltCapabilityArtifact::Dylib,
})
}
pub fn resolved_manifest_path(&self) -> Result<PathBuf, LeanBuiltCapabilityError> {
if let Some(path) = &self.manifest_path {
return Ok(path.clone());
}
let env_var = self
.manifest_env_var
.as_deref()
.ok_or(LeanBuiltCapabilityError::MissingManifestSource)?;
std::env::var_os(env_var)
.map(PathBuf::from)
.ok_or_else(|| LeanBuiltCapabilityError::EnvVarNotSet {
env_var: env_var.to_owned(),
kind: BuiltCapabilityArtifact::Manifest,
})
}
}
impl From<&crate::build_helpers::BuiltLeanCapability> for LeanBuiltCapability {
fn from(value: &crate::build_helpers::BuiltLeanCapability) -> Self {
Self {
dylib_path: Some(value.dylib_path().to_path_buf()),
env_var: Some(value.env_var().to_owned()),
manifest_path: Some(value.manifest_path().to_path_buf()),
manifest_env_var: Some(value.manifest_env_var().to_owned()),
package: Some(value.package().to_owned()),
module: Some(value.module().to_owned()),
dependencies: Vec::new(),
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum BuiltCapabilityArtifact {
Dylib,
Manifest,
}
impl BuiltCapabilityArtifact {
fn as_str(self) -> &'static str {
match self {
Self::Dylib => "Lean capability dylib",
Self::Manifest => "Lean capability manifest",
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum LeanBuiltCapabilityError {
MissingDylibSource,
MissingManifestSource,
EnvVarNotSet {
env_var: String,
kind: BuiltCapabilityArtifact,
},
}
impl std::fmt::Display for LeanBuiltCapabilityError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::MissingDylibSource => {
f.write_str("LeanBuiltCapability needs either a dylib path or an environment variable")
}
Self::MissingManifestSource => {
f.write_str("LeanBuiltCapability needs either a manifest path or manifest environment variable")
}
Self::EnvVarNotSet { env_var, kind } => {
write!(f, "environment variable {env_var} is not set for {}", kind.as_str())
}
}
}
}
impl std::error::Error for LeanBuiltCapabilityError {}
#[cfg(test)]
#[allow(clippy::expect_used, clippy::panic)]
mod tests {
use super::{BuiltCapabilityArtifact, LeanBuiltCapability, LeanBuiltCapabilityError};
use std::path::PathBuf;
#[test]
fn path_descriptor_resolves_without_runtime_env() {
let spec = LeanBuiltCapability::path("/tmp/libcap.so")
.env_var("LEAN_RS_CAPABILITY_CAP_DYLIB")
.package("pkg")
.module("Cap");
let path = match spec.dylib_path() {
Ok(path) => path,
Err(err) => panic!("expected path, got {err}"),
};
assert_eq!(path, PathBuf::from("/tmp/libcap.so"));
assert_eq!(spec.package_name(), Some("pkg"));
assert_eq!(spec.module_name(), Some("Cap"));
}
#[test]
fn missing_runtime_env_is_typed() {
let spec = LeanBuiltCapability::env("LEAN_TC_TEST_MISSING_CAPABILITY_DYLIB")
.package("pkg")
.module("Cap");
let err = match spec.dylib_path() {
Ok(path) => panic!("expected missing env error, got {}", path.display()),
Err(err) => err,
};
assert!(matches!(
err,
LeanBuiltCapabilityError::EnvVarNotSet {
kind: BuiltCapabilityArtifact::Dylib,
..
}
));
}
#[test]
fn missing_runtime_manifest_env_is_typed() {
let spec = LeanBuiltCapability::manifest_env("LEAN_TC_TEST_MISSING_CAPABILITY_MANIFEST");
let err = match spec.resolved_manifest_path() {
Ok(path) => panic!("expected missing env error, got {}", path.display()),
Err(err) => err,
};
assert!(matches!(
err,
LeanBuiltCapabilityError::EnvVarNotSet {
kind: BuiltCapabilityArtifact::Manifest,
..
}
));
}
#[test]
fn missing_dylib_source_is_typed() {
let spec = LeanBuiltCapability::manifest_path("/tmp/manifest.json");
let err = match spec.dylib_path() {
Ok(path) => panic!("expected missing dylib source error, got {}", path.display()),
Err(err) => err,
};
assert_eq!(err, LeanBuiltCapabilityError::MissingDylibSource);
}
#[test]
fn missing_manifest_source_is_typed() {
let spec = LeanBuiltCapability::path("/tmp/libcap.so").package("pkg").module("Cap");
let err = match spec.resolved_manifest_path() {
Ok(path) => panic!("expected missing manifest source error, got {}", path.display()),
Err(err) => err,
};
assert_eq!(err, LeanBuiltCapabilityError::MissingManifestSource);
}
}