Skip to main content

dag/set/
static.rs

1/*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8use std::any::Any;
9use std::fmt;
10
11use indexmap::IndexSet;
12
13use super::hints::Flags;
14use super::AsyncSetQuery;
15use super::BoxVertexStream;
16use super::Hints;
17use crate::Result;
18use crate::Vertex;
19
20/// A set backed by a concrete ordered set.
21pub struct StaticSet(pub(crate) IndexSet<Vertex>, Hints);
22
23impl StaticSet {
24    pub fn from_names(names: impl IntoIterator<Item = Vertex>) -> Self {
25        let names: IndexSet<Vertex> = names.into_iter().collect();
26        let hints = Hints::default();
27        if names.is_empty() {
28            hints.add_flags(Flags::EMPTY);
29        }
30        Self(names, hints)
31    }
32
33    pub fn empty() -> Self {
34        let names: IndexSet<Vertex> = Default::default();
35        let hints = Hints::default();
36        hints.add_flags(Flags::EMPTY);
37        Self(names, hints)
38    }
39}
40
41#[async_trait::async_trait]
42impl AsyncSetQuery for StaticSet {
43    async fn iter(&self) -> Result<BoxVertexStream> {
44        let iter = self.0.clone().into_iter().map(Ok);
45        Ok(Box::pin(futures::stream::iter(iter)))
46    }
47
48    async fn iter_rev(&self) -> Result<BoxVertexStream> {
49        let iter = self.0.clone().into_iter().rev().map(Ok);
50        Ok(Box::pin(futures::stream::iter(iter)))
51    }
52
53    async fn count(&self) -> Result<u64> {
54        Ok(self.0.len().try_into()?)
55    }
56
57    async fn size_hint(&self) -> (u64, Option<u64>) {
58        let size = self.0.len() as u64;
59        (size, Some(size))
60    }
61
62    async fn is_empty(&self) -> Result<bool> {
63        Ok(self.0.is_empty())
64    }
65
66    async fn contains(&self, name: &Vertex) -> Result<bool> {
67        Ok(self.0.contains(name))
68    }
69
70    async fn contains_fast(&self, name: &Vertex) -> Result<Option<bool>> {
71        Ok(Some(self.0.contains(name)))
72    }
73
74    fn as_any(&self) -> &dyn Any {
75        self
76    }
77
78    fn hints(&self) -> &Hints {
79        &self.1
80    }
81}
82
83impl fmt::Debug for StaticSet {
84    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
85        if self.0.is_empty() {
86            return f.write_str("<empty>");
87        }
88        write!(f, "<static ")?;
89        // Only show 3 commits by default.
90        let limit = f.width().unwrap_or(3);
91        f.debug_list().entries(self.0.iter().take(limit)).finish()?;
92        let remaining = self.0.len().max(limit) - limit;
93        if remaining > 0 {
94            write!(f, " + {} more>", remaining)?;
95        } else {
96            write!(f, ">")?;
97        }
98        Ok(())
99    }
100}
101
102// Test infra is unhappy about 'r#' yet (D20008157).
103#[cfg(not(fbcode_build))]
104#[cfg(test)]
105mod tests {
106    use std::collections::HashSet;
107
108    use super::super::tests::*;
109    use super::*;
110
111    fn static_set(a: &[u8]) -> StaticSet {
112        StaticSet::from_names(a.iter().map(|&b| to_name(b)))
113    }
114
115    #[test]
116    fn test_static_basic() -> Result<()> {
117        let set = static_set(b"\x11\x33\x22\x77\x22\x55\x11");
118        check_invariants(&set)?;
119        assert_eq!(shorten_iter(ni(set.iter())), ["11", "33", "22", "77", "55"]);
120        assert_eq!(
121            shorten_iter(ni(set.iter_rev())),
122            ["55", "77", "22", "33", "11"]
123        );
124        assert!(!nb(set.is_empty())?);
125        assert_eq!(nb(set.count())?, 5);
126        assert_eq!(shorten_name(nb(set.first())?.unwrap()), "11");
127        assert_eq!(shorten_name(nb(set.last())?.unwrap()), "55");
128        assert_eq!(nb(set.size_hint()), (5, Some(5)));
129        Ok(())
130    }
131
132    #[test]
133    fn test_debug() {
134        let set = static_set(b"");
135        assert_eq!(dbg(set), "<empty>");
136
137        let set = static_set(b"\x11\x33\x22");
138        assert_eq!(dbg(set), "<static [1111, 3333, 2222]>");
139
140        let set = static_set(b"\xaa\x00\xaa\xdd\xee\xdd\x11\x22");
141        assert_eq!(dbg(&set), "<static [aaaa, 0000, dddd] + 3 more>");
142        // {:#?} can be used to show commits in multi-line.
143        assert_eq!(
144            format!("{:#?}", &set),
145            "<static [\n    aaaa,\n    0000,\n    dddd,\n] + 3 more>"
146        );
147        // {:5.2} can be used to control how many commits to show, and their length.
148        assert_eq!(
149            format!("{:5.2?}", &set),
150            "<static [aa, 00, dd, ee, 11] + 1 more>"
151        );
152    }
153
154    quickcheck::quickcheck! {
155        fn test_static_quickcheck(a: Vec<u8>) -> bool {
156            let set = static_set(&a);
157            check_invariants(&set).unwrap();
158
159            let count = nb(set.count()).unwrap() as usize;
160            assert!(count <= a.len());
161
162            let set2: HashSet<_> = a.iter().cloned().collect();
163            assert_eq!(count, set2.len());
164
165            true
166        }
167    }
168}