mod checksum;
mod mode;
mod symbols;
mod version;
pub use checksum::{checksum_bytes, verify_checksum, CHECKSUM_ALGORITHM};
pub use mode::{Capability, Mode};
pub use symbols::{
is_control_symbol, is_project_mark, symbol_id_of, ControlSymbol, CONTROL_BASE,
DSK_PROJECT_MARK, DSK_PROJECT_MARK_NAME, FONT_VERSION_MGE2, GLYPH_ENGINE_VERSION,
};
pub use version::{ProtocolVersion, PROTOCOL_NAME, PROTOCOL_VERSION, PROTOCOL_VERSION_PROTECTED};
use thiserror::Error;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, Error, Clone, PartialEq, Eq)]
pub enum Error {
#[error("invalid header: expected DSK1 magic")]
InvalidHeader,
#[error("unsupported protocol version: {0}")]
UnsupportedVersion(u8),
#[error("unsupported mode: {0}")]
UnsupportedMode(u8),
#[error("invalid codepoint: U+{0:04X}")]
InvalidCodepoint(u32),
#[error("invalid mapping: {0}")]
InvalidMapping(String),
#[error("mapping collision: {0}")]
MappingCollision(String),
#[error("checksum mismatch: expected {expected:#010x}, got {actual:#010x}")]
ChecksumMismatch {
expected: u32,
actual: u32,
},
#[error("truncated payload: needed {needed} more bytes")]
TruncatedPayload {
needed: usize,
},
#[error("invalid UTF-8 at byte offset {offset}")]
InvalidUtf8 {
offset: usize,
},
#[error("invalid manifest: {0}")]
InvalidManifest(String),
#[error("font generation error: {0}")]
FontGenerationError(String),
#[error("I/O error: {0}")]
IoError(String),
#[error("invalid length: {0}")]
InvalidLength(String),
#[error("negotiation failed: {0}")]
NegotiationFailed(String),
#[error("vision error: {0}")]
VisionError(String),
#[error("semantic error: {0}")]
Semantic(String),
#[error("query error: {0}")]
Query(String),
#[error("frame error: {0}")]
Frame(String),
#[error("ecc error: {0}")]
Ecc(String),
#[error("resource limit exceeded: {0}")]
ResourceLimit(String),
#[error("unsupported extension: {0}")]
UnsupportedExtension(String),
#[error("parse error: {0}")]
Parse(String),
#[error("access denied: {0}")]
AccessDenied(String),
#[error("{0}")]
Other(String),
}
impl From<std::io::Error> for Error {
fn from(value: std::io::Error) -> Self {
Self::IoError(value.to_string())
}
}
impl From<std::string::FromUtf8Error> for Error {
fn from(value: std::string::FromUtf8Error) -> Self {
Self::InvalidUtf8 {
offset: value.utf8_error().valid_up_to(),
}
}
}
impl From<serde_json::Error> for Error {
fn from(value: serde_json::Error) -> Self {
Self::Parse(value.to_string())
}
}
pub const MAGIC: &[u8; 4] = b"DSK1";
pub const MAGIC_DSK2: &[u8; 4] = b"DSK2";
pub const HANDSHAKE_QUERY: &str = "DSK?";
pub const HANDSHAKE_REPLY: &str = "DSK!";
pub const PUA_BASE: u32 = 0xE000;
pub const ALPHABET_SIZE: usize = 256;
pub const FONT_VERSION: u32 = FONT_VERSION_MGE2;
pub const MIME_BINARY: &str = "application/vnd.doldskrift";
pub const MIME_TEXT: &str = "text/doldskrift";
#[inline]
pub fn byte_to_symbol(byte: u8) -> char {
char::from_u32(PUA_BASE + u32::from(byte)).expect("PUA symbol must be valid")
}
#[inline]
pub fn symbol_to_byte(ch: char) -> Option<u8> {
let cp = ch as u32;
if (PUA_BASE..PUA_BASE + ALPHABET_SIZE as u32).contains(&cp) {
Some((cp - PUA_BASE) as u8)
} else {
None
}
}
#[inline]
pub fn is_symbol(ch: char) -> bool {
symbol_to_byte(ch).is_some()
}