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!