strop_engine/editor/remote/
view.rs1use strop_remote::{ReadLimit, ReadSelection, RemoteOffset};
3
4#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
5pub enum RemoteView {
6 Snapshot(ReadSelection),
7 Follow(ReadLimit),
8}
9impl Default for RemoteView {
10 fn default() -> Self {
11 Self::Snapshot(ReadSelection::Full)
12 }
13}
14impl RemoteView {
15 pub fn selection(&self) -> ReadSelection {
16 match self {
17 Self::Snapshot(selection) => *selection,
18 Self::Follow(limit) => ReadSelection::Tail(*limit),
19 }
20 }
21 pub fn follow_limit(&self) -> Option<ReadLimit> {
22 match self {
23 Self::Follow(limit) => Some(*limit),
24 Self::Snapshot(_) => None,
25 }
26 }
27}
28
29#[derive(Debug, thiserror::Error)]
30pub enum ViewError {
31 #[error("byte counts must be nonnegative decimal integers")]
32 Number,
33 #[error("a range is START:BYTES")]
34 Range,
35 #[error(transparent)]
36 Limit(#[from] strop_remote::ReadLimitError),
37}
38pub(crate) fn offset(text: &str) -> Result<RemoteOffset, ViewError> {
39 Ok(RemoteOffset::new(number(text)?))
40}
41pub fn limit(text: &str) -> Result<ReadLimit, ViewError> {
42 Ok(ReadLimit::new(number(text)?)?)
43}
44pub fn range(text: &str) -> Result<ReadSelection, ViewError> {
45 let (start, length) = text.split_once(':').ok_or(ViewError::Range)?;
46 Ok(ReadSelection::Range {
47 start: offset(start)?,
48 length: limit(length)?,
49 })
50}
51fn number(text: &str) -> Result<u64, ViewError> {
52 if text.is_empty() || !text.bytes().all(|byte| byte.is_ascii_digit()) {
53 return Err(ViewError::Number);
54 }
55 text.parse().map_err(|_| ViewError::Number)
56}