Skip to main content

rakata_formats/vis/
writer.rs

1//! VIS ASCII writer.
2
3use std::io::{Cursor, Write};
4
5use crate::binary;
6
7use super::{Vis, VisError};
8
9/// Writes a VIS graph to a writer.
10///
11/// # Errors
12///
13/// [`VisError::InvalidRoom`] when a room name is empty or holds whitespace,
14/// which the line-per-room grammar has no way to represent,
15/// [`VisError::TextEncoding`] when a name has no Windows-1252 form, and
16/// [`VisError::Io`] when the writer fails.
17#[cfg_attr(
18    feature = "tracing",
19    tracing::instrument(level = "debug", skip(writer, vis))
20)]
21pub fn write_vis<W: Write>(writer: &mut W, vis: &Vis) -> Result<(), VisError> {
22    for (observer, observed) in vis.visibility() {
23        validate_room_token(observer)?;
24        binary::write_cp1252(
25            writer,
26            &format!("{observer} {}\n", observed.len()),
27            format!("observer `{observer}`"),
28            |context, source| VisError::TextEncoding { context, source },
29        )?;
30        for room in observed {
31            validate_room_token(room)?;
32            binary::write_cp1252(
33                writer,
34                &format!("  {room}\n"),
35                format!("edge `{observer}` -> `{room}`"),
36                |context, source| VisError::TextEncoding { context, source },
37            )?;
38        }
39    }
40    Ok(())
41}
42
43/// Serializes a VIS graph to bytes.
44///
45/// # Errors
46///
47/// [`VisError::InvalidRoom`] and [`VisError::TextEncoding`] on the terms
48/// [`write_vis`] gives. The `Vec` target has no I/O to fail at.
49#[cfg_attr(feature = "tracing", tracing::instrument(level = "debug", skip(vis)))]
50pub fn write_vis_to_vec(vis: &Vis) -> Result<Vec<u8>, VisError> {
51    let mut cursor = Cursor::new(Vec::new());
52    write_vis(&mut cursor, vis)?;
53    Ok(cursor.into_inner())
54}
55
56fn validate_room_token(room: &str) -> Result<(), VisError> {
57    if room.is_empty() || room.chars().any(char::is_whitespace) {
58        return Err(VisError::InvalidRoom(room.to_string()));
59    }
60    Ok(())
61}