11use std:: collections:: HashSet ;
22use std:: str:: FromStr ;
3+ use std:: string:: ToString ;
34
4- use crate :: config;
5+ use irc:: proto:: { self , Tags , command, format} ;
6+
7+ use crate :: { Target , User , config, message} ;
58
69// This is not an exhaustive list of IRCv3 capabilities, just the ones that
710// Halloy will request when available. When adding new IRCv3 capabilities to
@@ -62,11 +65,78 @@ impl FromStr for Capability {
6265}
6366
6467#[ derive( Debug , Clone , Copy ) ]
65- pub struct Multiline {
68+ pub struct MultilineLimits {
6669 pub max_bytes : usize ,
6770 pub max_lines : Option < usize > ,
6871}
6972
73+ impl MultilineLimits {
74+ pub fn concat_bytes (
75+ & self ,
76+ relay_bytes : usize ,
77+ batch_kind : MultilineBatchKind ,
78+ target : & Target ,
79+ ) -> usize {
80+ // Message byte limit - relay bytes - space - command - space - target - message separator - crlf
81+ format:: BYTE_LIMIT . saturating_sub (
82+ match batch_kind {
83+ MultilineBatchKind :: PRIVMSG => 7 ,
84+ MultilineBatchKind :: NOTICE => 6 ,
85+ } + target. as_str ( ) . len ( )
86+ + relay_bytes
87+ + 6 ,
88+ )
89+ }
90+ }
91+
92+ pub fn multiline_concat_lines ( concat_bytes : usize , text : & str ) -> Vec < & str > {
93+ let mut lines = Vec :: new ( ) ;
94+ let mut last_line_start = 0 ;
95+ let mut prev_char_index = 0 ;
96+
97+ for ( char_index, _) in text. char_indices ( ) {
98+ if char_index. saturating_sub ( last_line_start) > concat_bytes {
99+ lines. push ( & text[ last_line_start..prev_char_index] ) ;
100+ last_line_start = prev_char_index;
101+ }
102+
103+ prev_char_index = char_index;
104+ }
105+
106+ lines. push ( & text[ last_line_start..] ) ;
107+
108+ lines
109+ }
110+
111+ pub fn multiline_encoded (
112+ user : Option < & User > ,
113+ batch_kind : MultilineBatchKind ,
114+ target : & Target ,
115+ text : & str ,
116+ tags : Tags ,
117+ ) -> message:: Encoded {
118+ let mut encoded = command ! (
119+ match batch_kind {
120+ MultilineBatchKind :: PRIVMSG => "PRIVMSG" ,
121+ MultilineBatchKind :: NOTICE => "NOTICE" ,
122+ } ,
123+ target. as_str( ) ,
124+ text,
125+ ) ;
126+
127+ if let Some ( user) = user {
128+ encoded. source = Some ( proto:: Source :: User ( proto:: User {
129+ nickname : user. nickname ( ) . to_string ( ) ,
130+ username : user. username ( ) . map ( ToString :: to_string) ,
131+ hostname : user. hostname ( ) . map ( ToString :: to_string) ,
132+ } ) ) ;
133+ }
134+
135+ encoded. tags = tags;
136+
137+ message:: Encoded ( encoded)
138+ }
139+
70140#[ derive( Debug , Clone , Copy , PartialEq ) ]
71141pub enum MultilineBatchKind {
72142 PRIVMSG ,
@@ -78,7 +148,7 @@ pub struct Capabilities {
78148 listed : HashSet < String > ,
79149 pending : HashSet < String > ,
80150 acknowledged : HashSet < Capability > ,
81- multiline : Option < Multiline > ,
151+ multiline : Option < MultilineLimits > ,
82152}
83153
84154impl Capabilities {
@@ -234,7 +304,7 @@ impl Capabilities {
234304 . strip_prefix ( "max-bytes=" )
235305 . and_then ( |value| value. parse :: < usize > ( ) . ok ( ) )
236306 } ) {
237- self . multiline = Some ( Multiline {
307+ self . multiline = Some ( MultilineLimits {
238308 max_bytes,
239309 max_lines : dictionary. iter ( ) . find_map ( |key_value| {
240310 key_value
@@ -272,7 +342,7 @@ impl Capabilities {
272342 }
273343 }
274344
275- pub fn multiline ( & self ) -> Option < Multiline > {
345+ pub fn multiline_limits ( & self ) -> Option < MultilineLimits > {
276346 if self . acknowledged ( Capability :: Multiline ) {
277347 self . multiline
278348 } else {
0 commit comments