1use rand::distr::Distribution;
4use rand::seq::IndexedRandom;
5use rand::{Rng, RngExt};
6
7use crate::rand::Boundary;
8
9#[derive(Clone, Copy, Debug)]
11pub enum Vibe {
12 Clean,
14 Feral,
16}
17
18pub fn sample_vibe<R: Rng + ?Sized>(rng: &mut R) -> Vibe {
20 match [Vibe::Clean, Vibe::Feral].choose(rng) {
21 Some(Vibe::Feral) => Vibe::Feral,
22 _ => Vibe::Clean,
23 }
24}
25
26pub(crate) const NAME_SEPARATORS: &[u8] = b"._- ";
28
29pub(crate) const COMPLIANT_WORD: &[&[u8]] = &[
31 b"adp",
32 b"dogstatsd",
33 b"requests",
34 b"latency",
35 b"errors",
36 b"count",
37 b"total",
38 b"bytes",
39 b"queue",
40 b"workers",
41];
42
43pub(crate) const ABERRANT_WORD: &[&[u8]] = &[
47 b" ",
48 b"\t",
49 b"\0",
50 b"\x80", b"\xc3", b"\xed\xa0\x80", b"\xc0\x80", b"\xff\xfe", "café".as_bytes(), "Ωμέγα".as_bytes(), "日本語".as_bytes(), "🦆".as_bytes(), "a\u{0301}".as_bytes(), "\u{200d}".as_bytes(), "\u{202e}".as_bytes(), "\u{feff}".as_bytes(), ];
64
65pub(crate) const ABERRANT_VALUE: &[&[u8]] = &[
67 b"0",
68 b"-0",
69 b"inf",
70 b"-inf",
71 b"+inf",
72 b"nan",
73 b"infinity",
74 b"0x1p4",
75 b"1_000",
76 b"1.",
77 b".5",
78 b"00000000000000000000000000000000000000000000000000000001.5",
79 b"3.141592653589793115997963468544185161590576171875000000000000000000000000",
80];
81
82const COMPLIANT_TAG_KEYS: &[&[u8]] = &[b"env", b"service", b"region", b"version", b"team", b"shard"];
88const ABERRANT_TAG_KEYS: &[&[u8]] = &[b" ", b"\0", b"\x80", b"\xc3", "café".as_bytes(), "🦆".as_bytes()];
89const COMPLIANT_TAG_VALUES: &[&[u8]] = &[
90 b"prod",
91 b"staging",
92 b"adp",
93 b"us-east-1",
94 b"eu-west-1",
95 b"1.2.3",
96 b"web01",
97 b"0",
98];
99const ABERRANT_TAG_VALUES: &[&[u8]] = &[
100 b"",
101 b":",
102 b"\xff",
103 b"\xed\xa0\x80",
104 "café".as_bytes(),
105 "🦆".as_bytes(),
106 "\u{202e}".as_bytes(),
107];
108
109#[derive(Clone, Copy)]
111enum Form {
112 Compact,
113 Expanded,
114}
115
116pub(crate) fn extend_choice<R: Rng + ?Sized>(
119 rng: &mut R, buf: &mut Vec<u8>, vibe: Vibe, compliant: &[&[u8]], aberrant: &[&[u8]],
120) {
121 let pools: &[&[&[u8]]] = match vibe {
122 Vibe::Clean => &[compliant],
123 Vibe::Feral => &[compliant, aberrant],
124 };
125 if let Some(&pool) = pools.choose(rng) {
126 if let Some(&item) = pool.choose(rng) {
127 buf.extend_from_slice(item);
128 }
129 }
130}
131
132const ELEMENT_COUNTS_CLEAN: &[u8] = &[1, 1, 2, 2, 3, 3, 4, 5, 6];
134
135const ELEMENT_COUNTS_FERAL: &[u8] = &[1, 1, 2, 2, 3, 3, 4, 5, 6, 0, 127, 255];
137
138fn sample_count<R: Rng + ?Sized>(rng: &mut R, vibe: Vibe) -> u8 {
139 let counts = match vibe {
140 Vibe::Clean => ELEMENT_COUNTS_CLEAN,
141 Vibe::Feral => ELEMENT_COUNTS_FERAL,
142 };
143 counts[rng.random_range(0..counts.len())]
144}
145
146pub(crate) fn write_segments<R: Rng + ?Sized>(
149 rng: &mut R, buf: &mut Vec<u8>, vibe: Vibe, compliant: &[&[u8]], aberrant: &[&[u8]], separators: &[u8],
150) {
151 let count = sample_count(rng, vibe);
152 for i in 0..count {
153 if i > 0 {
154 if let Some(&sep) = separators.choose(rng) {
155 buf.push(sep);
156 }
157 }
158 extend_choice(rng, buf, vibe, compliant, aberrant);
159 }
160}
161
162pub(crate) fn write_words<R: Rng + ?Sized>(rng: &mut R, buf: &mut Vec<u8>, vibe: Vibe) {
166 let start = buf.len();
167 write_segments(rng, buf, vibe, COMPLIANT_WORD, ABERRANT_WORD, NAME_SEPARATORS);
168 if buf.len() == start {
169 extend_choice(rng, buf, vibe, COMPLIANT_WORD, ABERRANT_WORD);
170 }
171}
172
173pub(crate) fn write_field<R: Rng + ?Sized>(
175 rng: &mut R, buf: &mut Vec<u8>, vibe: Vibe, prefix: &[u8], compliant: &[&[u8]], aberrant: &[&[u8]],
176) {
177 buf.push(b'|');
178 buf.extend_from_slice(prefix);
179 extend_choice(rng, buf, vibe, compliant, aberrant);
180}
181
182pub(crate) fn write_tags<R: Rng + ?Sized>(rng: &mut R, buf: &mut Vec<u8>, vibe: Vibe) {
187 let count = sample_count(rng, vibe);
188 for t in 0..count {
189 if t == 0 {
190 buf.extend_from_slice(b"|#");
191 } else {
192 buf.push(b',');
193 }
194 write_segments(rng, buf, vibe, COMPLIANT_TAG_KEYS, ABERRANT_TAG_KEYS, NAME_SEPARATORS);
195 buf.push(b':');
196 write_segments(
197 rng,
198 buf,
199 vibe,
200 COMPLIANT_TAG_VALUES,
201 ABERRANT_TAG_VALUES,
202 NAME_SEPARATORS,
203 );
204 }
205}
206
207pub(crate) fn write_number<R: Rng + ?Sized>(rng: &mut R, buf: &mut Vec<u8>, digits: &[u8]) {
210 match [Form::Compact, Form::Expanded].choose(rng) {
211 Some(Form::Expanded) => {
212 let (sign, rest) = match digits.first() {
213 Some(&(b'-' | b'+')) => (&digits[..1], &digits[1..]),
214 _ => (&digits[..0], digits),
215 };
216 buf.extend_from_slice(sign);
217 pad_zeros(rng, buf);
218 buf.extend_from_slice(rest);
219 let fractional = rest.contains(&b'.') && !rest.iter().any(|&c| c == b'e' || c == b'E');
220 if fractional {
221 pad_zeros(rng, buf);
222 }
223 }
224 _ => buf.extend_from_slice(digits),
225 }
226}
227
228fn pad_zeros<R: Rng + ?Sized>(rng: &mut R, buf: &mut Vec<u8>) {
230 let zeros = usize::from(Boundary::<u8>::new().sample(rng));
231 buf.resize(buf.len() + zeros, b'0');
232}