dag/nameset/
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::AsyncNameSetQuery;
15use super::BoxVertexStream;
16use super::Hints;
17use crate::Result;
18use crate::VertexName;
19
20/// A set backed by a concrete ordered set.
21pub struct StaticSet(pub(crate) IndexSet<VertexName>, Hints);
22
23impl StaticSet {
24    pub fn from_names(names: impl IntoIterator<Item = VertexName>) -> Self {
25        let names: IndexSet<VertexName> = 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<VertexName> = 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 AsyncNameSetQuery 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<usize> {
54        Ok(self.0.len())
55    }
56
57    async fn is_empty(&self) -> Result<bool> {
58        Ok(self.0.is_empty())
59    }
60
61    async fn contains(&self, name: &VertexName) -> Result<bool> {
62        Ok(self.0.contains(name))
63    }
64
65    async fn contains_fast(&self, name: &VertexName) -> Result<Option<bool>> {
66        Ok(Some(self.0.contains(name)))
67    }
68
69    fn as_any(&self) -> &dyn Any {
70        self
71    }
72
73    fn hints(&self) -> &Hints {
74        &self.1
75    }
76}
77
78impl fmt::Debug for StaticSet {
79    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
80        if self.0.is_empty() {
81            return f.write_str("<empty>");
82        }
83        write!(f, "<static ")?;
84        // Only show 3 commits by default.
85        let limit = f.width().unwrap_or(3);
86        f.debug_list().entries(self.0.iter().take(limit)).finish()?;
87        let remaining = self.0.len().max(limit) - limit;
88        if remaining > 0 {
89            write!(f, " + {} more>", remaining)?;
90        } else {
91            write!(f, ">")?;
92        }
93        Ok(())
94    }
95}
96
97// Test infra is unhappy about 'r#' yet (D20008157).
98#[cfg(not(fbcode_build))]
99#[cfg(test)]
100mod tests {
101    use std::collections::HashSet;
102
103    use super::super::tests::*;
104    use super::*;
105
106    fn static_set(a: &[u8]) -> StaticSet {
107        StaticSet::from_names(a.iter().map(|&b| to_name(b)))
108    }
109
110    #[test]
111    fn test_static_basic() -> Result<()> {
112        let set = static_set(b"\x11\x33\x22\x77\x22\x55\x11");
113        check_invariants(&set)?;
114        assert_eq!(shorten_iter(ni(set.iter())), ["11", "33", "22", "77", "55"]);
115        assert_eq!(
116            shorten_iter(ni(set.iter_rev())),
117            ["55", "77", "22", "33", "11"]
118        );
119        assert!(!nb(set.is_empty())?);
120        assert_eq!(nb(set.count())?, 5);
121        assert_eq!(shorten_name(nb(set.first())?.unwrap()), "11");
122        assert_eq!(shorten_name(nb(set.last())?.unwrap()), "55");
123        Ok(())
124    }
125
126    #[test]
127    fn test_debug() {
128        let set = static_set(b"");
129        assert_eq!(format!("{:?}", set), "<empty>");
130
131        let set = static_set(b"\x11\x33\x22");
132        assert_eq!(format!("{:?}", set), "<static [1111, 3333, 2222]>");
133
134        let set = static_set(b"\xaa\x00\xaa\xdd\xee\xdd\x11\x22");
135        assert_eq!(
136            format!("{:?}", &set),
137            "<static [aaaa, 0000, dddd] + 3 more>"
138        );
139        // {:#?} can be used to show commits in multi-line.
140        assert_eq!(
141            format!("{:#?}", &set),
142            "<static [\n    aaaa,\n    0000,\n    dddd,\n] + 3 more>"
143        );
144        // {:5.2} can be used to control how many commits to show, and their length.
145        assert_eq!(
146            format!("{:5.2?}", &set),
147            "<static [aa, 00, dd, ee, 11] + 1 more>"
148        );
149    }
150
151    quickcheck::quickcheck! {
152        fn test_static_quickcheck(a: Vec<u8>) -> bool {
153            let set = static_set(&a);
154            check_invariants(&set).unwrap();
155
156            let count = nb(set.count()).unwrap();
157            assert!(count <= a.len());
158
159            let set2: HashSet<_> = a.iter().cloned().collect();
160            assert_eq!(count, set2.len());
161
162            true
163        }
164    }
165}