• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

facet-rs / facet / 19803020611

30 Nov 2025 06:23PM UTC coverage: 57.923% (-3.0%) from 60.927%
19803020611

push

github

fasterthanlime
Convert facet-kdl to use define_attr_grammar!

- Replace ~140 lines of hand-written macros with concise grammar DSL
- Enhance make_parse_attr.rs grammar compiler:
  - Add `ns "kdl";` syntax for namespace declaration
  - Add to_snake_case() helper (NodeName → node_name)
  - Generate __attr! macro with proper ExtensionAttr return
  - Use () data for unit variants, full dispatch for complex ones
  - Add #[repr(u8)] to generated enums for Facet derive
  - Use HashMap for O(1) struct lookup
- Update design diagrams with namespace fixes

50 of 53 new or added lines in 1 file covered. (94.34%)

3583 existing lines in 40 files now uncovered.

18788 of 32436 relevant lines covered (57.92%)

179.8 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

65.48
/facet-value/src/bytes.rs
1
//! Bytes value type for binary data.
2

3
#[cfg(feature = "alloc")]
4
use alloc::alloc::{Layout, alloc, dealloc};
5
#[cfg(feature = "alloc")]
6
use alloc::vec::Vec;
7
use core::borrow::Borrow;
8
use core::cmp::Ordering;
9
use core::fmt::{self, Debug, Formatter};
10
use core::hash::{Hash, Hasher};
11
use core::ops::Deref;
12
use core::ptr;
13

14
use crate::value::{TypeTag, Value};
15

16
/// Header for heap-allocated bytes.
17
#[repr(C, align(8))]
18
struct BytesHeader {
19
    /// Length of the data in bytes
20
    len: usize,
21
    // Byte data follows immediately after
22
}
23

24
/// A binary data value.
25
///
26
/// `VBytes` stores arbitrary binary data. This is useful for binary serialization
27
/// formats like MessagePack, CBOR, etc. that support raw bytes.
28
#[repr(transparent)]
29
#[derive(Clone)]
30
pub struct VBytes(pub(crate) Value);
31

32
impl VBytes {
33
    fn layout(len: usize) -> Layout {
20✔
34
        Layout::new::<BytesHeader>()
20✔
35
            .extend(Layout::array::<u8>(len).unwrap())
20✔
36
            .unwrap()
20✔
37
            .0
20✔
38
            .pad_to_align()
20✔
39
    }
20✔
40

41
    #[cfg(feature = "alloc")]
42
    fn alloc(data: &[u8]) -> *mut BytesHeader {
9✔
43
        unsafe {
44
            let layout = Self::layout(data.len());
9✔
45
            let ptr = alloc(layout).cast::<BytesHeader>();
9✔
46
            (*ptr).len = data.len();
9✔
47

48
            // Copy byte data
49
            let data_ptr = ptr.add(1).cast::<u8>();
9✔
50
            ptr::copy_nonoverlapping(data.as_ptr(), data_ptr, data.len());
9✔
51

52
            ptr
9✔
53
        }
54
    }
9✔
55

56
    #[cfg(feature = "alloc")]
57
    fn dealloc_ptr(ptr: *mut BytesHeader) {
10✔
58
        unsafe {
10✔
59
            let len = (*ptr).len;
10✔
60
            let layout = Self::layout(len);
10✔
61
            dealloc(ptr.cast::<u8>(), layout);
10✔
62
        }
10✔
63
    }
10✔
64

65
    fn header(&self) -> &BytesHeader {
17✔
66
        unsafe { &*(self.0.heap_ptr() as *const BytesHeader) }
17✔
67
    }
17✔
68

69
    fn data_ptr(&self) -> *const u8 {
13✔
70
        // Go through heap_ptr directly to avoid creating intermediate reference
71
        // that would limit provenance to just the header
72
        unsafe { (self.0.heap_ptr() as *const BytesHeader).add(1).cast() }
13✔
73
    }
13✔
74

75
    /// Creates new bytes from a byte slice.
76
    #[cfg(feature = "alloc")]
77
    #[must_use]
78
    pub fn new(data: &[u8]) -> Self {
9✔
79
        if data.is_empty() {
9✔
UNCOV
80
            return Self::empty();
×
81
        }
9✔
82
        unsafe {
83
            let ptr = Self::alloc(data);
9✔
84
            VBytes(Value::new_ptr(ptr.cast(), TypeTag::BytesOrFalse))
9✔
85
        }
86
    }
9✔
87

88
    /// Creates empty bytes.
89
    #[cfg(feature = "alloc")]
90
    #[must_use]
91
    pub fn empty() -> Self {
1✔
92
        unsafe {
93
            let layout = Self::layout(0);
1✔
94
            let ptr = alloc(layout).cast::<BytesHeader>();
1✔
95
            (*ptr).len = 0;
1✔
96
            VBytes(Value::new_ptr(ptr.cast(), TypeTag::BytesOrFalse))
1✔
97
        }
98
    }
1✔
99

100
    /// Returns the length of the bytes.
101
    #[must_use]
102
    pub fn len(&self) -> usize {
17✔
103
        self.header().len
17✔
104
    }
17✔
105

106
    /// Returns `true` if the bytes are empty.
107
    #[must_use]
108
    pub fn is_empty(&self) -> bool {
2✔
109
        self.len() == 0
2✔
110
    }
2✔
111

112
    /// Returns the data as a byte slice.
113
    #[must_use]
114
    pub fn as_slice(&self) -> &[u8] {
13✔
115
        unsafe { core::slice::from_raw_parts(self.data_ptr(), self.len()) }
13✔
116
    }
13✔
117

118
    pub(crate) fn clone_impl(&self) -> Value {
1✔
119
        VBytes::new(self.as_slice()).0
1✔
120
    }
1✔
121

122
    pub(crate) fn drop_impl(&mut self) {
10✔
123
        unsafe {
10✔
124
            Self::dealloc_ptr(self.0.heap_ptr_mut().cast());
10✔
125
        }
10✔
126
    }
10✔
127
}
128

129
impl Deref for VBytes {
130
    type Target = [u8];
131

132
    fn deref(&self) -> &[u8] {
×
UNCOV
133
        self.as_slice()
×
UNCOV
134
    }
×
135
}
136

137
impl Borrow<[u8]> for VBytes {
138
    fn borrow(&self) -> &[u8] {
×
UNCOV
139
        self.as_slice()
×
UNCOV
140
    }
×
141
}
142

143
impl AsRef<[u8]> for VBytes {
144
    fn as_ref(&self) -> &[u8] {
×
UNCOV
145
        self.as_slice()
×
UNCOV
146
    }
×
147
}
148

149
impl PartialEq for VBytes {
150
    fn eq(&self, other: &Self) -> bool {
3✔
151
        self.as_slice() == other.as_slice()
3✔
152
    }
3✔
153
}
154

155
impl Eq for VBytes {}
156

157
impl PartialOrd for VBytes {
158
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1✔
159
        Some(self.cmp(other))
1✔
160
    }
1✔
161
}
162

163
impl Ord for VBytes {
164
    fn cmp(&self, other: &Self) -> Ordering {
1✔
165
        self.as_slice().cmp(other.as_slice())
1✔
166
    }
1✔
167
}
168

169
impl Hash for VBytes {
170
    fn hash<H: Hasher>(&self, state: &mut H) {
×
UNCOV
171
        self.as_slice().hash(state);
×
UNCOV
172
    }
×
173
}
174

175
impl Debug for VBytes {
176
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1✔
177
        // Display as hex for readability
178
        write!(f, "b\"")?;
1✔
179
        for byte in self.as_slice() {
2✔
180
            write!(f, "\\x{byte:02x}")?;
2✔
181
        }
182
        write!(f, "\"")
1✔
183
    }
1✔
184
}
185

186
impl Default for VBytes {
187
    fn default() -> Self {
×
UNCOV
188
        Self::empty()
×
UNCOV
189
    }
×
190
}
191

192
// === PartialEq with [u8] ===
193

194
impl PartialEq<[u8]> for VBytes {
195
    fn eq(&self, other: &[u8]) -> bool {
×
UNCOV
196
        self.as_slice() == other
×
UNCOV
197
    }
×
198
}
199

200
impl PartialEq<VBytes> for [u8] {
201
    fn eq(&self, other: &VBytes) -> bool {
×
UNCOV
202
        self == other.as_slice()
×
UNCOV
203
    }
×
204
}
205

206
impl PartialEq<&[u8]> for VBytes {
207
    fn eq(&self, other: &&[u8]) -> bool {
1✔
208
        self.as_slice() == *other
1✔
209
    }
1✔
210
}
211

212
#[cfg(feature = "alloc")]
213
impl PartialEq<Vec<u8>> for VBytes {
214
    fn eq(&self, other: &Vec<u8>) -> bool {
×
UNCOV
215
        self.as_slice() == other.as_slice()
×
UNCOV
216
    }
×
217
}
218

219
#[cfg(feature = "alloc")]
220
impl PartialEq<VBytes> for Vec<u8> {
221
    fn eq(&self, other: &VBytes) -> bool {
×
UNCOV
222
        self.as_slice() == other.as_slice()
×
UNCOV
223
    }
×
224
}
225

226
// === From implementations ===
227

228
#[cfg(feature = "alloc")]
229
impl From<&[u8]> for VBytes {
230
    fn from(data: &[u8]) -> Self {
×
UNCOV
231
        Self::new(data)
×
UNCOV
232
    }
×
233
}
234

235
#[cfg(feature = "alloc")]
236
impl From<Vec<u8>> for VBytes {
237
    fn from(data: Vec<u8>) -> Self {
×
UNCOV
238
        Self::new(&data)
×
UNCOV
239
    }
×
240
}
241

242
#[cfg(feature = "alloc")]
243
impl From<&Vec<u8>> for VBytes {
244
    fn from(data: &Vec<u8>) -> Self {
×
UNCOV
245
        Self::new(data)
×
UNCOV
246
    }
×
247
}
248

249
#[cfg(feature = "alloc")]
250
impl From<VBytes> for Vec<u8> {
251
    fn from(b: VBytes) -> Self {
×
UNCOV
252
        b.as_slice().to_vec()
×
UNCOV
253
    }
×
254
}
255

256
// === Value conversions ===
257

258
impl AsRef<Value> for VBytes {
259
    fn as_ref(&self) -> &Value {
×
UNCOV
260
        &self.0
×
UNCOV
261
    }
×
262
}
263

264
impl AsMut<Value> for VBytes {
265
    fn as_mut(&mut self) -> &mut Value {
×
UNCOV
266
        &mut self.0
×
UNCOV
267
    }
×
268
}
269

270
impl From<VBytes> for Value {
271
    fn from(b: VBytes) -> Self {
×
UNCOV
272
        b.0
×
UNCOV
273
    }
×
274
}
275

276
impl VBytes {
277
    /// Converts this VBytes into a Value, consuming self.
278
    #[inline]
279
    pub fn into_value(self) -> Value {
×
UNCOV
280
        self.0
×
UNCOV
281
    }
×
282
}
283

284
#[cfg(feature = "alloc")]
285
impl From<&[u8]> for Value {
286
    fn from(data: &[u8]) -> Self {
×
UNCOV
287
        VBytes::new(data).0
×
UNCOV
288
    }
×
289
}
290

291
#[cfg(feature = "alloc")]
292
impl From<Vec<u8>> for Value {
293
    fn from(data: Vec<u8>) -> Self {
×
UNCOV
294
        VBytes::new(&data).0
×
UNCOV
295
    }
×
296
}
297

298
#[cfg(test)]
299
mod tests {
300
    use super::*;
301

302
    #[test]
303
    fn test_new() {
1✔
304
        let b = VBytes::new(&[1, 2, 3, 4, 5]);
1✔
305
        assert_eq!(b.as_slice(), &[1, 2, 3, 4, 5]);
1✔
306
        assert_eq!(b.len(), 5);
1✔
307
        assert!(!b.is_empty());
1✔
308
    }
1✔
309

310
    #[test]
311
    fn test_empty() {
1✔
312
        let b = VBytes::empty();
1✔
313
        assert_eq!(b.as_slice(), &[] as &[u8]);
1✔
314
        assert_eq!(b.len(), 0);
1✔
315
        assert!(b.is_empty());
1✔
316
    }
1✔
317

318
    #[test]
319
    fn test_equality() {
1✔
320
        let a = VBytes::new(&[1, 2, 3]);
1✔
321
        let b = VBytes::new(&[1, 2, 3]);
1✔
322
        let c = VBytes::new(&[4, 5, 6]);
1✔
323

324
        assert_eq!(a, b);
1✔
325
        assert_ne!(a, c);
1✔
326
        assert_eq!(a, [1, 2, 3].as_slice());
1✔
327
    }
1✔
328

329
    #[test]
330
    fn test_clone() {
1✔
331
        let a = VBytes::new(&[0xDE, 0xAD, 0xBE, 0xEF]);
1✔
332
        let b = a.clone();
1✔
333
        assert_eq!(a, b);
1✔
334
    }
1✔
335

336
    #[test]
337
    fn test_ordering() {
1✔
338
        let a = VBytes::new(&[1, 2, 3]);
1✔
339
        let b = VBytes::new(&[1, 2, 4]);
1✔
340
        assert!(a < b);
1✔
341
    }
1✔
342

343
    #[test]
344
    fn test_debug() {
1✔
345
        let b = VBytes::new(&[0xDE, 0xAD]);
1✔
346
        let s = format!("{b:?}");
1✔
347
        assert_eq!(s, "b\"\\xde\\xad\"");
1✔
348
    }
1✔
349
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc