use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use crate::manifest::dependency_spec::DependencySpec;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ResourceDependency {
Simple(String),
Detailed(Box<DetailedDependency>),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DetailedDependency {
#[serde(skip_serializing_if = "Option::is_none")]
pub source: Option<String>,
pub path: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub version: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub branch: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub rev: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub command: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub args: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub target: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub filename: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub dependencies: Option<HashMap<String, Vec<DependencySpec>>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tool: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub flatten: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub install: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub template_vars: Option<serde_json::Value>,
}
impl ResourceDependency {
#[must_use]
pub fn get_source(&self) -> Option<&str> {
match self {
Self::Simple(_) => None,
Self::Detailed(d) => d.source.as_deref(),
}
}
#[must_use]
pub fn get_target(&self) -> Option<&str> {
match self {
Self::Simple(_) => None,
Self::Detailed(d) => d.target.as_deref(),
}
}
#[must_use]
pub fn get_tool(&self) -> Option<&str> {
match self {
Self::Detailed(d) => d.tool.as_deref(),
Self::Simple(_) => None,
}
}
pub fn set_tool(&mut self, tool: Option<String>) {
if let Self::Detailed(d) = self {
d.tool = tool;
}
}
#[must_use]
pub fn get_filename(&self) -> Option<&str> {
match self {
Self::Simple(_) => None,
Self::Detailed(d) => d.filename.as_deref(),
}
}
#[must_use]
pub fn get_flatten(&self) -> Option<bool> {
match self {
Self::Simple(_) => None,
Self::Detailed(d) => d.flatten,
}
}
#[must_use]
pub fn get_install(&self) -> Option<bool> {
match self {
Self::Simple(_) => None,
Self::Detailed(d) => d.install,
}
}
pub fn get_template_vars(&self) -> Option<&serde_json::Value> {
match self {
Self::Simple(_) => None,
Self::Detailed(d) => d.template_vars.as_ref(),
}
}
#[must_use]
pub fn get_path(&self) -> &str {
match self {
Self::Simple(path) => path,
Self::Detailed(d) => &d.path,
}
}
#[must_use]
pub fn is_pattern(&self) -> bool {
let path = self.get_path();
path.contains('*') || path.contains('?') || path.contains('[')
}
#[must_use]
pub fn get_version(&self) -> Option<&str> {
match self.resolution_mode() {
crate::resolver::types::ResolutionMode::Version => {
self.get_version_constraint()
}
crate::resolver::types::ResolutionMode::GitRef => {
self.get_git_ref()
}
}
}
#[must_use]
pub fn is_local(&self) -> bool {
self.get_source().is_none()
}
#[must_use]
pub fn resolution_mode(&self) -> crate::resolver::types::ResolutionMode {
crate::resolver::types::ResolutionMode::from_dependency(self)
}
#[must_use]
pub fn get_version_constraint(&self) -> Option<&str> {
match (self, self.resolution_mode()) {
(Self::Detailed(d), crate::resolver::types::ResolutionMode::Version) => {
d.version.as_deref()
}
_ => None,
}
}
#[must_use]
pub fn get_git_ref(&self) -> Option<&str> {
match self {
Self::Detailed(d) => {
d.rev.as_deref().or(d.branch.as_deref())
}
_ => None,
}
}
#[must_use]
pub fn is_mutable(&self) -> bool {
if self.is_local() {
return true;
}
match self {
Self::Detailed(d) => {
if d.branch.is_some() {
return true;
}
if d.rev.is_some() {
return false;
}
if let Some(version) = &d.version {
return Self::is_branch_like_version(version);
}
true
}
Self::Simple(_) => true,
}
}
#[cfg_attr(test, allow(dead_code))]
pub(crate) fn is_branch_like_version(version: &str) -> bool {
let version = version.trim();
if version.is_empty() {
return true;
}
if version.len() == 40 && version.chars().all(|c| c.is_ascii_hexdigit()) {
return false;
}
let semver_start = |s: &str| {
s.starts_with('^')
|| s.starts_with('~')
|| s.starts_with('>')
|| s.starts_with('<')
|| s.starts_with('=')
|| s.starts_with('v')
|| s.starts_with('V')
|| s.chars().next().is_some_and(|c| c.is_ascii_digit())
};
if let Some(last_hyphen_pos) = version.rfind('-') {
let after_hyphen = &version[last_hyphen_pos + 1..];
if semver_start(after_hyphen) {
return false; }
}
if semver_start(version) {
return false;
}
true
}
}