draft/multiline: inbound BATCH parsing + concat assembly, deliver assembled lines; advertise limits
This commit is contained in:
parent
5a34ec57da
commit
d4c4c1a7d0
6 changed files with 148 additions and 1 deletions
|
|
@ -97,6 +97,20 @@ pub struct HistMsg {
|
|||
pub text: String,
|
||||
}
|
||||
|
||||
/// Limits advertised in the `draft/multiline` cap and enforced while buffering.
|
||||
pub const MLINE_MAX_BYTES: usize = 4096;
|
||||
pub const MLINE_MAX_LINES: usize = 24;
|
||||
|
||||
/// An in-progress inbound draft/multiline batch — one long client message being
|
||||
/// assembled from several `@batch=`-tagged PRIVMSG/NOTICE lines.
|
||||
pub struct MlineBatch {
|
||||
pub bref: String,
|
||||
pub target: String,
|
||||
pub notice: bool,
|
||||
pub parts: Vec<(String, bool)>, // (text, concat-with-previous-part)
|
||||
pub bytes: usize,
|
||||
}
|
||||
|
||||
/// A recently-departed identity, kept for WHOWAS.
|
||||
pub struct WhowasEntry {
|
||||
pub nick: String,
|
||||
|
|
@ -152,6 +166,7 @@ pub struct Server {
|
|||
pub history: HashMap<String, VecDeque<HistMsg>>, // channel key -> recent messages (CHATHISTORY)
|
||||
pub read_markers: HashMap<String, HashMap<String, u64>>, // identity -> target -> read ts (MARKREAD)
|
||||
pub metadata: HashMap<String, HashMap<String, String>>, // target key -> key -> value (draft/metadata-2)
|
||||
pub mline: HashMap<Uid, MlineBatch>, // in-progress inbound multiline batches
|
||||
pub event_tx: Sender<Event>, // self-inject events (DNS results)
|
||||
pub conn_counter: Arc<AtomicU64>, // mints connection uids (for CONNECT dials)
|
||||
}
|
||||
|
|
@ -198,6 +213,7 @@ impl Server {
|
|||
history: HashMap::new(),
|
||||
read_markers: HashMap::new(),
|
||||
metadata: HashMap::new(),
|
||||
mline: HashMap::new(),
|
||||
event_tx,
|
||||
conn_counter,
|
||||
}
|
||||
|
|
@ -252,6 +268,64 @@ impl Server {
|
|||
}
|
||||
}
|
||||
|
||||
/// Open an inbound draft/multiline batch for `uid` (a client assembling one
|
||||
/// long message from several tagged PRIVMSG/NOTICE lines).
|
||||
pub fn multiline_open(&mut self, uid: Uid, bref: &str, target: &str) {
|
||||
self.mline.insert(
|
||||
uid,
|
||||
MlineBatch {
|
||||
bref: bref.to_string(),
|
||||
target: target.to_string(),
|
||||
notice: false,
|
||||
parts: Vec::new(),
|
||||
bytes: 0,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Buffer one PRIVMSG/NOTICE line into `uid`'s open multiline batch when `bref`
|
||||
/// matches (bounded by the advertised byte/line limits). Returns true if it was
|
||||
/// part of the batch — i.e. it should not be delivered on its own.
|
||||
pub fn multiline_accumulate(
|
||||
&mut self,
|
||||
uid: Uid,
|
||||
bref: &str,
|
||||
notice: bool,
|
||||
text: &str,
|
||||
concat: bool,
|
||||
) -> bool {
|
||||
match self.mline.get_mut(&uid) {
|
||||
Some(mb) if mb.bref == bref => {
|
||||
if mb.parts.len() < MLINE_MAX_LINES && mb.bytes + text.len() <= MLINE_MAX_BYTES {
|
||||
mb.notice = notice;
|
||||
mb.bytes += text.len();
|
||||
mb.parts.push((text.to_string(), concat));
|
||||
}
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Close `uid`'s multiline batch `bref` and return `(target, is_notice, lines)`
|
||||
/// with `concat` parts joined into single logical lines. `None` if no match.
|
||||
pub fn multiline_close(&mut self, uid: Uid, bref: &str) -> Option<(String, bool, Vec<String>)> {
|
||||
match self.mline.get(&uid) {
|
||||
Some(mb) if mb.bref == bref => {}
|
||||
_ => return None,
|
||||
}
|
||||
let mb = self.mline.remove(&uid)?;
|
||||
let mut lines: Vec<String> = Vec::new();
|
||||
for (text, concat) in mb.parts {
|
||||
if concat && !lines.is_empty() {
|
||||
lines.last_mut().unwrap().push_str(&text);
|
||||
} else {
|
||||
lines.push(text);
|
||||
}
|
||||
}
|
||||
Some((mb.target, mb.notice, lines))
|
||||
}
|
||||
|
||||
/// Resolve a METADATA target (a nick or `#channel`) to its metadata storage
|
||||
/// key, or `None` if it doesn't exist. User keys are `u<uid>` (stable across
|
||||
/// nick changes); channel keys are the lowercased name.
|
||||
|
|
@ -453,6 +527,7 @@ impl Server {
|
|||
self.uuid_local.remove(&user.uuid);
|
||||
self.read_markers.remove(&format!("~{uid}")); // session read-markers (kept if account-keyed)
|
||||
self.metadata.remove(&format!("u{uid}")); // per-user metadata
|
||||
self.mline.remove(&uid); // any half-open multiline batch
|
||||
if user.registered {
|
||||
self.push_whowas(
|
||||
&user.nick,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue