Constellation, Spacedust, Slingshot, UFOs: atproto crates and services for microcosm

Replace boolean reverse parameter with Order enum

Refactor link query APIs to use an explicit Order enum
(OldestToNewest, NewestToOldest) instead of a boolean
reverse flag for better clarity and maintainability.

+120 -82
+22 -4
constellation/src/server/mod.rs
··· 17 17 use tokio::task::spawn_blocking; 18 18 use tokio_util::sync::CancellationToken; 19 19 20 - use crate::storage::{LinkReader, StorageStats}; 20 + use crate::storage::{LinkReader, Order, StorageStats}; 21 21 use crate::{CountsByCount, Did, RecordId}; 22 22 23 23 mod acceptable; ··· 298 298 299 299 let path_to_other = format!(".{}", query.path_to_other); 300 300 301 + let order = if query.reverse { 302 + Order::OldestToNewest 303 + } else { 304 + Order::NewestToOldest 305 + }; 306 + 301 307 let paged = store 302 308 .get_many_to_many_counts( 303 309 &query.subject, 304 310 collection, 305 311 &path, 306 312 &path_to_other, 307 - query.reverse, 313 + order, 308 314 limit, 309 315 cursor_key, 310 316 &filter_dids, ··· 461 467 }; 462 468 let path = format!(".{path}"); 463 469 470 + let order = if query.reverse { 471 + Order::OldestToNewest 472 + } else { 473 + Order::NewestToOldest 474 + }; 475 + 464 476 let paged = store 465 477 .get_links( 466 478 &query.subject, 467 479 collection, 468 480 &path, 469 - query.reverse, 481 + order, 470 482 limit, 471 483 until, 472 484 &filter_dids, ··· 566 578 } 567 579 } 568 580 581 + let order = if query.reverse { 582 + Order::OldestToNewest 583 + } else { 584 + Order::NewestToOldest 585 + }; 586 + 569 587 let paged = store 570 588 .get_links( 571 589 &query.target, 572 590 &query.collection, 573 591 &query.path, 574 - query.reverse, 592 + order, 575 593 limit, 576 594 until, 577 595 &filter_dids,
+29 -23
constellation/src/storage/mem_store.rs
··· 1 1 use super::{ 2 - LinkReader, LinkStorage, PagedAppendingCollection, PagedOrderedCollection, StorageStats, 2 + LinkReader, LinkStorage, Order, PagedAppendingCollection, PagedOrderedCollection, 3 + StorageStats, 3 4 }; 4 5 use crate::{ActionableEvent, CountsByCount, Did, RecordId}; 5 6 use anyhow::Result; ··· 140 141 collection: &str, 141 142 path: &str, 142 143 path_to_other: &str, 143 - reverse: bool, 144 + order: Order, 144 145 limit: u64, 145 146 after: Option<String>, 146 147 filter_dids: &HashSet<Did>, ··· 199 200 .iter() 200 201 .map(|(k, (n, u, _))| (k.0.clone(), *n, u.len() as u64)) 201 202 .collect(); 202 - // sort in reverse order to show entries from oldest to newest 203 - if reverse { 204 - items.sort_by(|a, b| b.cmp(a)); 205 - } else { 206 - items.sort(); 203 + // Sort based on order: OldestToNewest uses descending order, NewestToOldest uses ascending 204 + match order { 205 + Order::OldestToNewest => items.sort_by(|a, b| b.cmp(a)), 206 + Order::NewestToOldest => items.sort(), 207 207 } 208 208 items = items 209 209 .into_iter() ··· 250 250 target: &str, 251 251 collection: &str, 252 252 path: &str, 253 - reverse: bool, 253 + order: Order, 254 254 limit: u64, 255 255 until: Option<u64>, 256 256 filter_dids: &HashSet<Did>, ··· 293 293 let end: usize; 294 294 let next: Option<u64>; 295 295 296 - if reverse { 297 - begin = until.map(|u| (u) as usize).unwrap_or(0); 298 - end = std::cmp::min(begin + limit as usize, total); 296 + match order { 297 + // OldestToNewest: start from the beginning, paginate forward 298 + Order::OldestToNewest => { 299 + begin = until.map(|u| (u) as usize).unwrap_or(0); 300 + end = std::cmp::min(begin + limit as usize, total); 299 301 300 - next = if end < total { 301 - Some(end as u64 + 1) 302 - } else { 303 - None 304 - }; 305 - } else { 306 - end = until 307 - .map(|u| std::cmp::min(u as usize, total)) 308 - .unwrap_or(total); 309 - begin = end.saturating_sub(limit as usize); 310 - next = if begin == 0 { None } else { Some(begin as u64) }; 302 + next = if end < total { 303 + Some(end as u64 + 1) 304 + } else { 305 + None 306 + }; 307 + } 308 + // NewestToOldest: start from the end, paginate backward 309 + Order::NewestToOldest => { 310 + end = until 311 + .map(|u| std::cmp::min(u as usize, total)) 312 + .unwrap_or(total); 313 + begin = end.saturating_sub(limit as usize); 314 + next = if begin == 0 { None } else { Some(begin as u64) }; 315 + } 311 316 } 312 317 313 318 let alive = did_rkeys.iter().flatten().count(); ··· 325 330 }) 326 331 .collect(); 327 332 328 - if reverse { 333 + // For OldestToNewest, reverse the items to maintain forward chronological order 334 + if order == Order::OldestToNewest { 329 335 items.reverse(); 330 336 } 331 337
+43 -34
constellation/src/storage/mod.rs
··· 11 11 #[cfg(feature = "rocks")] 12 12 pub use rocks_store::RocksStorage; 13 13 14 + /// Ordering for paginated link queries 15 + #[derive(Debug, Clone, Copy, PartialEq, Eq)] 16 + pub enum Order { 17 + /// Newest links first (default) 18 + NewestToOldest, 19 + /// Oldest links first 20 + OldestToNewest, 21 + } 22 + 14 23 #[derive(Debug, PartialEq)] 15 24 pub struct PagedAppendingCollection<T> { 16 25 pub version: (u64, u64), // (collection length, deleted item count) // TODO: change to (total, active)? since dedups isn't "deleted" ··· 72 81 collection: &str, 73 82 path: &str, 74 83 path_to_other: &str, 75 - reverse: bool, 84 + order: Order, 76 85 limit: u64, 77 86 after: Option<String>, 78 87 filter_dids: &HashSet<Did>, ··· 88 97 target: &str, 89 98 collection: &str, 90 99 path: &str, 91 - reverse: bool, 100 + order: Order, 92 101 limit: u64, 93 102 until: Option<u64>, 94 103 filter_dids: &HashSet<Did>, ··· 182 191 "a.com", 183 192 "app.t.c", 184 193 ".abc.uri", 185 - false, 194 + Order::NewestToOldest, 186 195 100, 187 196 None, 188 197 &HashSet::default() ··· 686 695 "a.com", 687 696 "app.t.c", 688 697 ".abc.uri", 689 - false, 698 + Order::NewestToOldest, 690 699 100, 691 700 None, 692 701 &HashSet::default() ··· 735 744 "a.com", 736 745 "app.t.c", 737 746 ".abc.uri", 738 - false, 747 + Order::NewestToOldest, 739 748 2, 740 749 None, 741 750 &HashSet::default(), ··· 774 783 "a.com", 775 784 "app.t.c", 776 785 ".abc.uri", 777 - false, 786 + Order::NewestToOldest, 778 787 2, 779 788 links.next, 780 789 &HashSet::default(), ··· 813 822 "a.com", 814 823 "app.t.c", 815 824 ".abc.uri", 816 - false, 825 + Order::NewestToOldest, 817 826 2, 818 827 links.next, 819 828 &HashSet::default(), ··· 862 871 )?; 863 872 } 864 873 865 - // Test reverse: true (oldest first) 874 + // Test OldestToNewest order (oldest first) 866 875 let links = storage.get_links( 867 876 "a.com", 868 877 "app.t.c", 869 878 ".abc.uri", 870 - true, 879 + Order::OldestToNewest, 871 880 2, 872 881 None, 873 882 &HashSet::default(), ··· 892 901 total: 5, 893 902 } 894 903 ); 895 - // Test reverse: false (newest first) 904 + // Test NewestToOldest order (newest first) 896 905 let links = storage.get_links( 897 906 "a.com", 898 907 "app.t.c", 899 908 ".abc.uri", 900 - false, 909 + Order::NewestToOldest, 901 910 2, 902 911 None, 903 912 &HashSet::default(), ··· 930 939 "a.com", 931 940 "app.t.c", 932 941 ".abc.uri", 933 - false, 942 + Order::NewestToOldest, 934 943 2, 935 944 None, 936 945 &HashSet::from([Did("did:plc:linker".to_string())]), ··· 964 973 "a.com", 965 974 "app.t.c", 966 975 ".abc.uri", 967 - false, 976 + Order::NewestToOldest, 968 977 2, 969 978 None, 970 979 &HashSet::from([Did("did:plc:linker".to_string())]), ··· 987 996 "a.com", 988 997 "app.t.c", 989 998 ".abc.uri", 990 - false, 999 + Order::NewestToOldest, 991 1000 2, 992 1001 None, 993 1002 &HashSet::from([Did("did:plc:someone-else".to_string())]), ··· 1035 1044 "a.com", 1036 1045 "app.t.c", 1037 1046 ".abc.uri", 1038 - false, 1047 + Order::NewestToOldest, 1039 1048 2, 1040 1049 None, 1041 1050 &HashSet::from([Did("did:plc:linker".to_string())]), ··· 1065 1074 "a.com", 1066 1075 "app.t.c", 1067 1076 ".abc.uri", 1068 - false, 1077 + Order::NewestToOldest, 1069 1078 2, 1070 1079 None, 1071 1080 &HashSet::from([ ··· 1098 1107 "a.com", 1099 1108 "app.t.c", 1100 1109 ".abc.uri", 1101 - false, 1110 + Order::NewestToOldest, 1102 1111 2, 1103 1112 None, 1104 1113 &HashSet::from([Did("did:plc:someone-unknown".to_string())]), ··· 1135 1144 "a.com", 1136 1145 "app.t.c", 1137 1146 ".abc.uri", 1138 - false, 1147 + Order::NewestToOldest, 1139 1148 2, 1140 1149 None, 1141 1150 &HashSet::default(), ··· 1164 1173 "a.com", 1165 1174 "app.t.c", 1166 1175 ".abc.uri", 1167 - false, 1176 + Order::NewestToOldest, 1168 1177 2, 1169 1178 links.next, 1170 1179 &HashSet::default(), ··· 1213 1222 "a.com", 1214 1223 "app.t.c", 1215 1224 ".abc.uri", 1216 - false, 1225 + Order::NewestToOldest, 1217 1226 2, 1218 1227 None, 1219 1228 &HashSet::default(), ··· 1256 1265 "a.com", 1257 1266 "app.t.c", 1258 1267 ".abc.uri", 1259 - false, 1268 + Order::NewestToOldest, 1260 1269 2, 1261 1270 links.next, 1262 1271 &HashSet::default(), ··· 1305 1314 "a.com", 1306 1315 "app.t.c", 1307 1316 ".abc.uri", 1308 - false, 1317 + Order::NewestToOldest, 1309 1318 2, 1310 1319 None, 1311 1320 &HashSet::default(), ··· 1342 1351 "a.com", 1343 1352 "app.t.c", 1344 1353 ".abc.uri", 1345 - false, 1354 + Order::NewestToOldest, 1346 1355 2, 1347 1356 links.next, 1348 1357 &HashSet::default(), ··· 1384 1393 "a.com", 1385 1394 "app.t.c", 1386 1395 ".abc.uri", 1387 - false, 1396 + Order::NewestToOldest, 1388 1397 2, 1389 1398 None, 1390 1399 &HashSet::default(), ··· 1417 1426 "a.com", 1418 1427 "app.t.c", 1419 1428 ".abc.uri", 1420 - false, 1429 + Order::NewestToOldest, 1421 1430 2, 1422 1431 links.next, 1423 1432 &HashSet::default(), ··· 1499 1508 "a.b.c", 1500 1509 ".d.e", 1501 1510 ".f.g", 1502 - false, 1511 + Order::NewestToOldest, 1503 1512 10, 1504 1513 None, 1505 1514 &HashSet::new(), ··· 1543 1552 "app.t.c", 1544 1553 ".abc.uri", 1545 1554 ".def.uri", 1546 - false, 1555 + Order::NewestToOldest, 1547 1556 10, 1548 1557 None, 1549 1558 &HashSet::new(), ··· 1643 1652 "app.t.c", 1644 1653 ".abc.uri", 1645 1654 ".def.uri", 1646 - false, 1655 + Order::NewestToOldest, 1647 1656 10, 1648 1657 None, 1649 1658 &HashSet::new(), ··· 1660 1669 "app.t.c", 1661 1670 ".abc.uri", 1662 1671 ".def.uri", 1663 - false, 1672 + Order::NewestToOldest, 1664 1673 10, 1665 1674 None, 1666 1675 &HashSet::from_iter([Did("did:plc:fdsa".to_string())]), ··· 1677 1686 "app.t.c", 1678 1687 ".abc.uri", 1679 1688 ".def.uri", 1680 - false, 1689 + Order::NewestToOldest, 1681 1690 10, 1682 1691 None, 1683 1692 &HashSet::new(), ··· 1753 1762 2, 1754 1763 )?; 1755 1764 1756 - // Test reverse: false (default order - by target ascending) 1765 + // Test NewestToOldest order (default order - by target ascending) 1757 1766 let counts = storage.get_many_to_many_counts( 1758 1767 "a.com", 1759 1768 "app.t.c", 1760 1769 ".abc.uri", 1761 1770 ".def.uri", 1762 - false, 1771 + Order::NewestToOldest, 1763 1772 10, 1764 1773 None, 1765 1774 &HashSet::new(), ··· 1771 1780 assert_eq!(counts.items[1].0, "c.com"); 1772 1781 assert_eq!(counts.items[2].0, "d.com"); 1773 1782 1774 - // Test reverse: true (descending order - by target descending) 1783 + // Test OldestToNewest order (descending order - by target descending) 1775 1784 let counts = storage.get_many_to_many_counts( 1776 1785 "a.com", 1777 1786 "app.t.c", 1778 1787 ".abc.uri", 1779 1788 ".def.uri", 1780 - true, 1789 + Order::OldestToNewest, 1781 1790 10, 1782 1791 None, 1783 1792 &HashSet::new(),
+26 -21
constellation/src/storage/rocks_store.rs
··· 1 1 use super::{ 2 - ActionableEvent, LinkReader, LinkStorage, PagedAppendingCollection, PagedOrderedCollection, 3 - StorageStats, 2 + ActionableEvent, LinkReader, LinkStorage, Order, PagedAppendingCollection, 3 + PagedOrderedCollection, StorageStats, 4 4 }; 5 5 use crate::{CountsByCount, Did, RecordId}; 6 6 use anyhow::{bail, Result}; ··· 941 941 collection: &str, 942 942 path: &str, 943 943 path_to_other: &str, 944 - reverse: bool, 944 + order: Order, 945 945 limit: u64, 946 946 after: Option<String>, 947 947 filter_dids: &HashSet<Did>, ··· 1084 1084 items.push((target.0 .0, *n, dids.len() as u64)); 1085 1085 } 1086 1086 1087 - // Sort in desired direction 1088 - if reverse { 1089 - items.sort_by(|a, b| b.cmp(a)); // descending 1090 - } else { 1091 - items.sort(); // ascending 1087 + // Sort based on order: OldestToNewest uses descending order, NewestToOldest uses ascending 1088 + match order { 1089 + Order::OldestToNewest => items.sort_by(|a, b| b.cmp(a)), // descending 1090 + Order::NewestToOldest => items.sort(), // ascending 1092 1091 } 1093 1092 1094 1093 let next = if grouped_counts.len() as u64 >= limit { ··· 1136 1135 target: &str, 1137 1136 collection: &str, 1138 1137 path: &str, 1139 - reverse: bool, 1138 + order: Order, 1140 1139 limit: u64, 1141 1140 until: Option<u64>, 1142 1141 filter_dids: &HashSet<Did>, ··· 1182 1181 let begin: usize; 1183 1182 let next: Option<u64>; 1184 1183 1185 - if reverse { 1186 - begin = until.map(|u| (u - 1) as usize).unwrap_or(0); 1187 - end = std::cmp::min(begin + limit as usize, total as usize); 1184 + match order { 1185 + // OldestToNewest: start from the beginning, paginate forward 1186 + Order::OldestToNewest => { 1187 + begin = until.map(|u| (u - 1) as usize).unwrap_or(0); 1188 + end = std::cmp::min(begin + limit as usize, total as usize); 1188 1189 1189 - next = if end < total as usize { 1190 - Some(end as u64 + 1) 1191 - } else { 1192 - None 1190 + next = if end < total as usize { 1191 + Some(end as u64 + 1) 1192 + } else { 1193 + None 1194 + } 1193 1195 } 1194 - } else { 1195 - end = until.map(|u| std::cmp::min(u, total)).unwrap_or(total) as usize; 1196 - begin = end.saturating_sub(limit as usize); 1197 - next = if begin == 0 { None } else { Some(begin as u64) }; 1196 + // NewestToOldest: start from the end, paginate backward 1197 + Order::NewestToOldest => { 1198 + end = until.map(|u| std::cmp::min(u, total)).unwrap_or(total) as usize; 1199 + begin = end.saturating_sub(limit as usize); 1200 + next = if begin == 0 { None } else { Some(begin as u64) }; 1201 + } 1198 1202 } 1199 1203 1200 1204 let mut did_id_rkeys = linkers.0[begin..end].iter().rev().collect::<Vec<_>>(); 1201 1205 1202 - if reverse { 1206 + // For OldestToNewest, reverse the items to maintain forward chronological order 1207 + if order == Order::OldestToNewest { 1203 1208 did_id_rkeys.reverse(); 1204 1209 } 1205 1210