templar_curator_primitives/policy/supply_queue/
mod.rs1use alloc::vec::Vec;
4use templar_vault_kernel::TargetId;
5use typed_builder::TypedBuilder;
6
7#[templar_vault_macros::vault_derive(borsh, serde, postcard)]
9#[derive(Clone, PartialEq, Eq, TypedBuilder)]
10#[builder(field_defaults(setter(into)))]
11pub struct SupplyQueueEntry {
12 pub target_id: TargetId,
13 pub amount: u128,
14 #[builder(default)]
15 pub priority: u8,
16 #[builder(default)]
17 pub queued_at_ns: u64,
18}
19
20impl SupplyQueueEntry {
21 #[must_use]
22 pub fn new(target_id: TargetId, amount: u128) -> Self {
23 Self {
24 target_id,
25 amount,
26 priority: 0,
27 queued_at_ns: 0,
28 }
29 }
30}
31
32impl From<(TargetId, u128)> for SupplyQueueEntry {
33 fn from(value: (TargetId, u128)) -> Self {
34 Self::new(value.0, value.1)
35 }
36}
37
38#[templar_vault_macros::vault_derive(borsh, serde, postcard)]
40#[derive(Clone, Default)]
41pub struct SupplyQueue {
42 pub entries: Vec<SupplyQueueEntry>,
43 pub max_length: usize,
44}
45
46impl SupplyQueue {
47 #[must_use]
48 pub fn is_empty(&self) -> bool {
49 self.entries.is_empty()
50 }
51
52 #[must_use]
53 pub fn len(&self) -> usize {
54 self.entries.len()
55 }
56
57 #[must_use]
58 pub fn is_full(&self) -> bool {
59 self.max_length > 0 && self.entries.len() >= self.max_length
60 }
61
62 pub fn enqueue(&self, entry: SupplyQueueEntry) -> Result<Self, SupplyQueueError> {
67 if entry.amount == 0 {
68 return Err(SupplyQueueError::ZeroAmount);
69 }
70
71 if self.is_full() {
72 return Err(SupplyQueueError::QueueFull {
73 max_length: self.max_length,
74 });
75 }
76
77 let mut new_queue = self.clone();
78
79 let insert_pos = new_queue
81 .entries
82 .iter()
83 .position(|e| e.priority < entry.priority)
84 .unwrap_or(new_queue.entries.len());
85
86 new_queue.entries.insert(insert_pos, entry);
87
88 Ok(new_queue)
89 }
90
91 pub fn dequeue(&self) -> Result<(Self, SupplyQueueEntry), SupplyQueueError> {
92 if self.is_empty() {
93 return Err(SupplyQueueError::QueueEmpty);
94 }
95
96 let mut new_queue = self.clone();
97 let entry = new_queue.entries.remove(0);
98
99 Ok((new_queue, entry))
100 }
101
102 #[must_use]
103 pub fn peek(&self) -> Option<&SupplyQueueEntry> {
104 self.entries.first()
105 }
106
107 #[must_use]
108 pub fn total(&self) -> u128 {
109 self.entries
110 .iter()
111 .fold(0u128, |acc, e| acc.saturating_add(e.amount))
112 }
113
114 #[must_use]
116 pub fn totals_by_target(&self) -> Vec<(TargetId, u128)> {
117 let mut totals: Vec<(TargetId, u128)> = Vec::new();
118 for entry in &self.entries {
119 if let Some((_, sum)) = totals
120 .iter_mut()
121 .find(|(target_id, _)| *target_id == entry.target_id)
122 {
123 *sum = sum.saturating_add(entry.amount);
124 } else {
125 totals.push((entry.target_id, entry.amount));
126 }
127 }
128 totals.sort_unstable_by_key(|(target_id, _)| *target_id);
129 totals
130 }
131
132 #[must_use]
134 pub fn remove_target(&self, target_id: TargetId) -> Self {
135 let mut new_queue = self.clone();
136 new_queue.entries.retain(|e| e.target_id != target_id);
137 new_queue
138 }
139
140 #[must_use]
142 pub fn drain(&self) -> (Self, Vec<SupplyQueueEntry>) {
143 let entries: Vec<SupplyQueueEntry> = self.entries.to_vec();
144 let empty_queue = Self {
145 entries: Vec::new(),
146 max_length: self.max_length,
147 };
148 (empty_queue, entries)
149 }
150
151 #[must_use]
156 pub fn to_allocation_plan(&self) -> Vec<(TargetId, u128)> {
157 self.totals_by_target()
158 }
159
160 #[must_use]
162 pub fn total_for_target(&self, target_id: TargetId) -> u128 {
163 self.entries
164 .iter()
165 .filter(|e| e.target_id == target_id)
166 .fold(0u128, |acc, e| acc.saturating_add(e.amount))
167 }
168
169 #[must_use]
171 pub fn has_target(&self, target_id: TargetId) -> bool {
172 self.entries.iter().any(|e| e.target_id == target_id)
173 }
174}
175
176impl From<Vec<SupplyQueueEntry>> for SupplyQueue {
177 fn from(entries: Vec<SupplyQueueEntry>) -> Self {
178 Self {
179 entries,
180 max_length: 0,
181 }
182 }
183}
184
185#[templar_vault_macros::vault_derive]
187#[derive(Clone, PartialEq, Eq)]
188pub enum SupplyQueueError {
189 QueueFull { max_length: usize },
191 ZeroAmount,
193 QueueEmpty,
195}