Files
ElyOffice/crates/ely-lok/src/ipc.rs
T

109 lines
3.8 KiB
Rust

pub use ely_ipc::{MAX_FRAME, Pack, UNASKED, Unpack, check_reply, read_frame, write_frame};
/// A local path as a `file:` URL, UTF-8 escaped.
pub fn file_url_of(path: &std::path::Path) -> String {
let text = path.to_string_lossy().into_owned();
#[cfg(windows)]
let text = if let Some(rest) = text.strip_prefix(r"\\?\UNC\") {
format!("file://{}", rest.replace('\\', "/"))
} else if let Some(rest) = text.strip_prefix(r"\\?\") {
format!("file:///{}", rest.replace('\\', "/"))
} else {
format!("file:///{}", text.replace('\\', "/"))
};
#[cfg(not(windows))]
let text = format!("file://{text}");
let mut url = String::new();
for byte in text.as_bytes() {
match *byte {
b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'.' | b'_' | b'~' | b'/' | b':' => {
url.push(*byte as char)
}
other => url.push_str(&format!("%{other:02X}")),
}
}
url
}
/// The path a `file:` URL names; other URLs fail.
pub fn path_of_file_url(url: &str) -> std::io::Result<std::path::PathBuf> {
let bad = || std::io::Error::other(format!("{url} is no file URL"));
let rest = url.strip_prefix("file://").ok_or_else(bad)?;
let mut bytes = Vec::with_capacity(rest.len());
let mut iter = rest.bytes();
while let Some(byte) = iter.next() {
if byte != b'%' {
bytes.push(byte);
continue;
}
let hex = [iter.next().ok_or_else(bad)?, iter.next().ok_or_else(bad)?];
let hex = std::str::from_utf8(&hex).map_err(|_| bad())?;
bytes.push(u8::from_str_radix(hex, 16).map_err(|_| bad())?);
}
let text = String::from_utf8(bytes).map_err(|_| bad())?;
#[cfg(windows)]
let text = text.strip_prefix('/').unwrap_or(&text).replace('/', "\\");
Ok(std::path::PathBuf::from(text))
}
/// The load error a missing or wrong password gives.
pub const LOCKED: &str = "the document needs its password";
/// Request tags from the client.
pub mod tag {
pub const BUILD_ID: u8 = 1;
pub const LOAD: u8 = 2;
pub const DOC_SIZE: u8 = 3;
pub const SAVE_AS: u8 = 4;
pub const CLOSE_DOC: u8 = 5;
pub const SHUTDOWN: u8 = 6;
pub const VERIFY: u8 = 7;
pub const PAINT_TILE: u8 = 12;
/// Unasked: one document's new events.
pub const EVENTS: u8 = 13;
pub const POST_UNO: u8 = 14;
pub const POST_MOUSE: u8 = 8;
pub const RUN_MACRO: u8 = 10;
pub const POST_KEY: u8 = 11;
pub const POST_TEXT: u8 = 15;
pub const COPY: u8 = 16;
pub const PASTE: u8 = 17;
pub const CUT: u8 = 18;
pub const VALUES: u8 = 19;
pub const PAGES: u8 = 20;
pub const GRAPHIC: u8 = 21;
pub const EXPORT_PDF: u8 = 22;
pub const SHEETS: u8 = 23;
pub const SHOW_SHEET: u8 = 24;
pub const VISIBLE_AREA: u8 = 25;
pub const SELECTION_TEXT: u8 = 26;
pub const SET_AUTHOR: u8 = 27;
pub const COMPLETE_FUNCTION: u8 = 28;
pub const IMPORT: u8 = 29;
pub const EXPORT_TEXT: u8 = 30;
pub const PAINT_PART: u8 = 31;
pub const SLIDE_LAYERS: u8 = 32;
/// Refuses unless the document is still at a revision.
pub const EXPECT: u8 = 33;
pub const REVISION: u8 = 34;
/// One layer's pixels from the last `SLIDE_LAYERS`.
pub const SLIDE_LAYER: u8 = 35;
pub const OK: u8 = 100;
pub const ERR: u8 = 101;
}
#[cfg(test)]
mod tests {
#[test]
fn a_file_url_turns_back_into_its_path() {
let path = std::env::temp_dir().join("幻灯片 one%.mp4");
let url = super::file_url_of(&path);
assert_eq!(super::path_of_file_url(&url).expect("decodes"), path);
assert!(super::path_of_file_url("vnd.sun.star.Package:Media/a.mp4").is_err());
assert!(super::path_of_file_url("file:///bad%zz").is_err());
}
}
/// How a refusal for a changed document begins.
pub const STALE: &str = "stale: ";