sps_common/dependency/
definition.rs

1// **File:** sps-core/src/dependency/dependency.rs // Should be in the model module
2use std::fmt;
3
4use bitflags::bitflags;
5use serde::{Deserialize, Serialize}; // For derive macros and attributes
6
7bitflags! {
8    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
9    /// Tags associated with a dependency, mirroring Homebrew's concepts.
10    pub struct DependencyTag: u8 {
11        /// Standard runtime dependency, needed for the formula to function.
12        const RUNTIME     = 0b00000001;
13        /// Needed only at build time.
14        const BUILD       = 0b00000010;
15        /// Needed for running tests (`brew test`).
16        const TEST        = 0b00000100;
17        /// Optional dependency, installable via user flag (e.g., `--with-foo`).
18        const OPTIONAL    = 0b00001000;
19        /// Recommended dependency, installed by default but can be skipped (e.g., `--without-bar`).
20        const RECOMMENDED = 0b00010000;
21        // Add other tags as needed (e.g., :implicit)
22    }
23}
24
25impl Default for DependencyTag {
26    // By default, a dependency is considered runtime unless specified otherwise.
27    fn default() -> Self {
28        Self::RUNTIME
29    }
30}
31
32impl fmt::Display for DependencyTag {
33    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34        write!(f, "{self:?}") // Simple debug format for now
35    }
36}
37
38/// Represents a dependency declared by a Formula.
39#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
40pub struct Dependency {
41    /// The name of the formula dependency.
42    pub name: String,
43    /// Tags associated with this dependency (e.g., build, optional).
44    #[serde(default)] // Use default tags (RUNTIME) if missing in serialization
45    pub tags: DependencyTag,
46    // We could add requirements here later:
47    // pub requirements: Vec<Requirement>,
48}
49
50impl Dependency {
51    /// Creates a new runtime dependency.
52    pub fn new_runtime(name: impl Into<String>) -> Self {
53        Self {
54            name: name.into(),
55            tags: DependencyTag::RUNTIME,
56        }
57    }
58
59    /// Creates a new dependency with specific tags.
60    pub fn new_with_tags(name: impl Into<String>, tags: DependencyTag) -> Self {
61        Self {
62            name: name.into(),
63            tags,
64        }
65    }
66}
67
68/// Extension trait for Vec<Dependency> for easier filtering.
69pub trait DependencyExt {
70    /// Filters dependencies based on included tags and excluded tags.
71    /// For example, to get runtime dependencies that are *not* optional:
72    /// `filter_by_tags(DependencyTag::RUNTIME, DependencyTag::OPTIONAL)`
73    fn filter_by_tags(&self, include: DependencyTag, exclude: DependencyTag) -> Vec<&Dependency>;
74
75    /// Get only runtime dependencies (excluding build, test).
76    fn runtime(&self) -> Vec<&Dependency>;
77
78    /// Get only build-time dependencies (includes :build, excludes others unless also :build).
79    fn build_time(&self) -> Vec<&Dependency>;
80}
81
82impl DependencyExt for Vec<Dependency> {
83    fn filter_by_tags(&self, include: DependencyTag, exclude: DependencyTag) -> Vec<&Dependency> {
84        self.iter()
85            .filter(|dep| dep.tags.contains(include) && !dep.tags.intersects(exclude))
86            .collect()
87    }
88
89    fn runtime(&self) -> Vec<&Dependency> {
90        // Runtime deps are those *not* exclusively build or test
91        // (A dep could be both runtime and build, e.g., a compiler needed at runtime too)
92        self.iter()
93            .filter(|dep| {
94                !dep.tags
95                    .contains(DependencyTag::BUILD | DependencyTag::TEST)
96                    || dep.tags.contains(DependencyTag::RUNTIME)
97            })
98            // Alternatively, be more explicit: include RUNTIME | RECOMMENDED | OPTIONAL
99            // .filter(|dep| dep.tags.intersects(DependencyTag::RUNTIME | DependencyTag::RECOMMENDED
100            // | DependencyTag::OPTIONAL))
101            .collect()
102    }
103
104    fn build_time(&self) -> Vec<&Dependency> {
105        self.filter_by_tags(DependencyTag::BUILD, DependencyTag::empty())
106    }
107}
108
109// Required for bitflags!