agentmux_srv\backend/
syncbuf.rs

1#![allow(dead_code)]
2// Copyright 2025-2026, AgentMux Corp.
3// SPDX-License-Identifier: Apache-2.0
4
5//! Thread-safe byte buffer.
6//! Port of Go's `pkg/util/syncbuf/syncbuf.go`.
7//!
8//! A `SyncBuffer` wraps a `Vec<u8>` behind a mutex and implements `Write`.
9
10
11use std::io::{self, Write};
12use std::sync::Mutex;
13
14/// A thread-safe byte buffer.
15pub struct SyncBuffer {
16    buf: Mutex<Vec<u8>>,
17}
18
19impl Default for SyncBuffer {
20    fn default() -> Self {
21        Self::new()
22    }
23}
24
25impl SyncBuffer {
26    /// Create a new empty buffer.
27    pub fn new() -> Self {
28        Self {
29            buf: Mutex::new(Vec::new()),
30        }
31    }
32
33    /// Get the buffer contents as a string.
34    pub fn to_string_lossy(&self) -> String {
35        let buf = self.buf.lock().unwrap();
36        String::from_utf8_lossy(&buf).into_owned()
37    }
38
39    /// Get the buffer contents as bytes.
40    pub fn to_bytes(&self) -> Vec<u8> {
41        self.buf.lock().unwrap().clone()
42    }
43
44    /// Get the current length in bytes.
45    pub fn len(&self) -> usize {
46        self.buf.lock().unwrap().len()
47    }
48
49    /// Check if the buffer is empty.
50    pub fn is_empty(&self) -> bool {
51        self.buf.lock().unwrap().is_empty()
52    }
53}
54
55impl Write for SyncBuffer {
56    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
57        let mut inner = self.buf.lock().unwrap();
58        inner.extend_from_slice(buf);
59        Ok(buf.len())
60    }
61
62    fn flush(&mut self) -> io::Result<()> {
63        Ok(())
64    }
65}
66
67/// Allow writing via shared reference (for thread-safe use).
68impl SyncBuffer {
69    /// Write data via shared reference (thread-safe).
70    pub fn write_shared(&self, buf: &[u8]) -> io::Result<usize> {
71        let mut inner = self.buf.lock().unwrap();
72        inner.extend_from_slice(buf);
73        Ok(buf.len())
74    }
75}
76
77#[cfg(test)]
78mod tests {
79    use super::*;
80
81    #[test]
82    fn test_new_empty() {
83        let buf = SyncBuffer::new();
84        assert!(buf.is_empty());
85        assert_eq!(buf.len(), 0);
86        assert_eq!(buf.to_string_lossy(), "");
87    }
88
89    #[test]
90    fn test_write() {
91        let mut buf = SyncBuffer::new();
92        buf.write_all(b"hello").unwrap();
93        assert_eq!(buf.to_string_lossy(), "hello");
94        assert_eq!(buf.len(), 5);
95    }
96
97    #[test]
98    fn test_multiple_writes() {
99        let mut buf = SyncBuffer::new();
100        buf.write_all(b"hello ").unwrap();
101        buf.write_all(b"world").unwrap();
102        assert_eq!(buf.to_string_lossy(), "hello world");
103    }
104
105    #[test]
106    fn test_write_shared() {
107        let buf = SyncBuffer::new();
108        buf.write_shared(b"shared write").unwrap();
109        assert_eq!(buf.to_string_lossy(), "shared write");
110    }
111
112    #[test]
113    fn test_to_bytes() {
114        let buf = SyncBuffer::new();
115        buf.write_shared(b"\x00\x01\x02").unwrap();
116        assert_eq!(buf.to_bytes(), vec![0, 1, 2]);
117    }
118
119    #[test]
120    fn test_default() {
121        let buf = SyncBuffer::default();
122        assert!(buf.is_empty());
123    }
124}