clawft_kernel/licence/
floor_preview.rs1use serde::Serialize;
8
9use super::store::{CheckoutGrantStore, grant_valid};
10use super::LicenceError;
11
12#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
14pub struct RevivedGrant {
15 pub cog_id: String,
17 pub version: String,
19 pub expires_at: u64,
21}
22
23#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
25pub struct FloorPreview {
26 pub now: u64,
28 pub floor: u64,
30 pub floor_after: u64,
32 pub revived: Vec<RevivedGrant>,
34}
35
36impl CheckoutGrantStore {
37 pub fn floor_preview(&self) -> Result<FloorPreview, LicenceError> {
40 self.run(|inner, ev| {
41 let b = self.binding_in_effect(inner, ev)?;
42 Ok(preview_of(inner, &b.grant_pubkey, &b.mesh_id, (self.clock)()))
43 })
44 }
45
46 pub fn reset_floor_checked(&self, expected_floor: Option<u64>) -> Result<FloorPreview, LicenceError> {
50 self.run(|inner, ev| {
51 let b = self.binding_in_effect(inner, ev)?;
52 let now = (self.clock)();
53 let p = preview_of(inner, &b.grant_pubkey, &b.mesh_id, now);
54 if expected_floor.is_some_and(|f| f != p.floor) {
55 return Err(LicenceError::CheckFailed("floor_changed".into()));
56 }
57 let mut next = inner.clone();
58 next.floors.entry(b.grant_pubkey).or_default().reset(now);
59 ev.push(super::LicenceEvent::FloorReset(now));
60 self.commit(inner, next)?;
61 Ok(p)
62 })
63 }
64}
65
66fn preview_of(inner: &super::store::Inner, key: &str, mesh_id: &str, now: u64) -> FloorPreview {
67 let cur = inner.floors.get(key).copied().unwrap_or_default();
68 let mut after = cur;
69 after.reset(now);
70 let (eff_now, eff_after) = (cur.effective_now(now), after.effective_now(now));
71 let mut revived: Vec<RevivedGrant> = inner
72 .slots
73 .values()
74 .filter_map(|s| s.current.as_ref())
75 .filter(|h| {
76 h.body.mesh_id == mesh_id && !grant_valid(&h.body, eff_now) && grant_valid(&h.body, eff_after)
77 })
78 .map(|h| RevivedGrant {
79 cog_id: h.body.cog_id.clone(),
80 version: h.body.version.clone(),
81 expires_at: h.body.expires_at,
82 })
83 .collect();
84 revived.sort_by(|a, c| (&a.cog_id, &a.version).cmp(&(&c.cog_id, &c.version)));
85 FloorPreview { now, floor: cur.floor(), floor_after: after.floor(), revived }
86}