harness/contexts/
service_check.rs1use rand::{Rng, RngExt};
9
10use super::TS_DIGITS;
11use super::{fresh_timestamp, get_bytes, get_tags, put_bytes, put_tags};
12use crate::payload::dogstatsd::common;
13
14const STATUS: &[&[u8]] = &[b"0", b"1", b"2", b"3"];
16
17const OPT_COUNTS: &[usize] = &[0, 0, 0, 1];
19
20#[derive(Clone, Debug, PartialEq, Eq, Hash)]
23pub struct ServiceCheckContext {
24 pub name: Vec<u8>,
26 pub tags: Vec<Vec<u8>>,
28 pub options: Vec<Vec<u8>>,
30}
31
32impl ServiceCheckContext {
33 pub(crate) fn mint_within(rng: &mut (impl Rng + ?Sized), budget: usize) -> Option<Self> {
37 const RESERVED: usize = "_sc|".len() + 1 + 1 + "|d:".len() + TS_DIGITS + "|m:".len();
38 let name = common::identifier_within(rng, budget.checked_sub(RESERVED)?);
39 if name.is_empty() {
40 return None;
41 }
42 let mut room = budget - RESERVED - name.len();
43 let count = OPT_COUNTS[rng.random_range(0..OPT_COUNTS.len())];
44 let mut options = Vec::new();
45 for _ in 0..count {
46 let Some(body_room) = room.checked_sub(1 + "h:".len()) else {
47 break;
48 };
49 let mut chunk = b"h:".to_vec();
50 chunk.extend_from_slice(&common::optional_text_within(rng, body_room));
51 room -= 1 + chunk.len();
52 options.push(chunk);
53 }
54 Some(Self {
55 name,
56 tags: common::tags_within(rng, room),
57 options,
58 })
59 }
60
61 pub(crate) fn floor(&self) -> usize {
66 "_sc|".len()
67 + self.name.len()
68 + 1
69 + 1
70 + self.options.iter().map(|opt| 1 + opt.len()).sum::<usize>()
71 + "|d:".len()
72 + TS_DIGITS
73 + common::tags_len(&self.tags)
74 + "|m:".len()
75 }
76
77 pub(crate) fn render_within(
80 &self, rng: &mut (impl Rng + ?Sized), out: &mut Vec<u8>, budget: usize,
81 ) -> Option<usize> {
82 let message_room = budget.checked_sub(self.floor())?;
83 out.extend_from_slice(b"_sc|");
84 out.extend_from_slice(&self.name);
85 out.push(b'|');
86 out.extend_from_slice(STATUS[rng.random_range(0..STATUS.len())]);
87 for opt in &self.options {
88 out.push(b'|');
89 out.extend_from_slice(opt);
90 }
91 let mut itoa = itoa::Buffer::new();
92 out.extend_from_slice(b"|d:");
93 out.extend_from_slice(itoa.format(fresh_timestamp(rng)).as_bytes());
94 common::serialize_tags(&self.tags, out);
95 out.extend_from_slice(b"|m:");
96 out.extend_from_slice(&common::optional_text_within(rng, message_room));
97 Some(0)
98 }
99
100 pub(crate) fn render(&self, rng: &mut (impl Rng + ?Sized), out: &mut Vec<u8>) -> usize {
101 out.extend_from_slice(b"_sc|");
102 out.extend_from_slice(&self.name);
103 out.push(b'|');
104 out.extend_from_slice(STATUS[rng.random_range(0..STATUS.len())]);
105 for opt in &self.options {
106 out.push(b'|');
107 out.extend_from_slice(opt);
108 }
109 let mut itoa = itoa::Buffer::new();
110 out.extend_from_slice(b"|d:");
111 out.extend_from_slice(itoa.format(fresh_timestamp(rng)).as_bytes());
112 common::serialize_tags(&self.tags, out);
113 out.extend_from_slice(b"|m:");
114 out.extend_from_slice(&common::optional_text(rng));
115 0
116 }
117
118 pub(crate) fn encode(&self, out: &mut Vec<u8>) {
120 put_bytes(out, &self.name);
121 put_tags(out, &self.tags);
122 put_tags(out, &self.options);
123 }
124
125 pub(crate) fn decode(buf: &[u8], pos: &mut usize) -> Option<Self> {
127 let name = get_bytes(buf, pos)?.to_vec();
128 let tags = get_tags(buf, pos)?;
129 let options = get_tags(buf, pos)?;
130 Some(Self { name, tags, options })
131 }
132}