Files
brahman/crates/protocol/brahman-handshake/src/codec.rs
T
sergio 550c98f275 refactor(monorepo): reorganización lógica + renames + SDDs + split CHANGELOG
Reorganización física de crates/:
- core/ (mezclaba 6 propósitos) se divide en protocol/, init/, runtime/, compat/
- shared/ (3 crates) se redistribuye en protocol/ e init/
- lapaloma (sub-módulo de ui_engine) se promueve a modules/pineal/

Renames de proyectos:
- shipote → shuma (runtime de sandboxes)
- nouser → akasha (explorador de Mónadas)
- yahweh → nahual (motor GPUI, antes ui_engine/)
- lapaloma → pineal (data-viz agnóstica)

Fraccionamiento UI → core agnóstico:
- vista-core (DeckState + snap, 175 LOC, 5 tests verdes)
- barra-core (Task + render_html + sanitize, 90 LOC, 5 tests verdes)
- vista-web y barra-web ahora son thin DOM bindings

Documentación nueva:
- 16 SDDs por subdirectorio (≤80 LOC c/u): protocol/init/runtime/compat
  + 10 módulos + apps/
- docs/STATUS.md con cifras reales por proyecto
- docs/ROADMAP.md con plan a finalización (6 hitos, ~6-8 semanas)
- CHANGELOG.md particionado en docs/changelog/<proyecto>.md (7 buckets)

Automatización:
- scripts/reorg.py — script idempotente que: git mv directorios, renombra
  package names, recomputa path = refs, reescribe imports rust, actualiza
  workspace Cargo.toml. Soporta --dry-run.
- scripts/split-changelog.py — particiona CHANGELOG por componente.

Validación:
- cargo check --workspace pasa (124 crates + 2 nuevos cores).
- 10 tests adicionales (5 en vista-core + 5 en barra-core) verdes.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-19 14:48:34 +00:00

73 lines
2.4 KiB
Rust

//! Codec de wire: frames length-prefixed con cuerpo postcard.
//!
//! Cada frame en el stream tiene la forma:
//! ```text
//! [4 bytes LE: longitud N] [N bytes: postcard(Frame)]
//! ```
//!
//! El `MAX_FRAME_BYTES` evita que un cliente malicioso/buggy reserve memoria
//! arbitraria al anunciar un length absurdo.
use std::io::{Error, ErrorKind, Result};
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
use crate::messages::Frame;
/// Tamaño máximo de un frame antes de que el reader rechace la conexión.
/// 4 MiB cubre cualquier Card razonable con margen amplio.
pub const MAX_FRAME_BYTES: usize = 4 * 1024 * 1024;
/// Escribe un frame al stream.
pub async fn write_frame<W: AsyncWrite + Unpin>(w: &mut W, frame: &Frame) -> Result<()> {
let bytes = postcard::to_allocvec(frame)
.map_err(|e| Error::new(ErrorKind::InvalidData, format!("postcard encode: {e}")))?;
if bytes.len() > MAX_FRAME_BYTES {
return Err(Error::new(
ErrorKind::InvalidData,
format!("frame demasiado grande: {} bytes", bytes.len()),
));
}
let len = bytes.len() as u32;
w.write_all(&len.to_le_bytes()).await?;
w.write_all(&bytes).await?;
w.flush().await?;
Ok(())
}
/// Lee un frame del stream.
pub async fn read_frame<R: AsyncRead + Unpin>(r: &mut R) -> Result<Frame> {
let mut len_buf = [0u8; 4];
r.read_exact(&mut len_buf).await?;
let len = u32::from_le_bytes(len_buf) as usize;
if len > MAX_FRAME_BYTES {
return Err(Error::new(
ErrorKind::InvalidData,
format!("frame anunciado demasiado grande: {len} bytes"),
));
}
let mut buf = vec![0u8; len];
r.read_exact(&mut buf).await?;
postcard::from_bytes(&buf)
.map_err(|e| Error::new(ErrorKind::InvalidData, format!("postcard decode: {e}")))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::messages::{Frame, HandshakeError};
#[tokio::test]
async fn frame_roundtrip() {
let frame = Frame::Error(HandshakeError::Rejected("test".into()));
let mut buf = Vec::new();
write_frame(&mut buf, &frame).await.unwrap();
let mut cursor = std::io::Cursor::new(buf);
let decoded = read_frame(&mut cursor).await.unwrap();
match decoded {
Frame::Error(HandshakeError::Rejected(s)) => assert_eq!(s, "test"),
_ => panic!("variant mismatch"),
}
}
}