Skip to content

Commit 3a56755

Browse files
committed
Initial implementation supporting the soju.im/search extension.
Adds command/completion support for soju.im/search, funnels the results to a search results history, and includes a pane for creating search queries and viewing search results.
1 parent ab24e2b commit 3a56755

31 files changed

+1662
-126
lines changed

book/src/commands.md

Whitespace-only changes.

data/src/buffer.rs

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ pub enum Internal {
3737
Highlights,
3838
#[strum(serialize = "Channel Discovery")]
3939
ChannelDiscovery(Option<Server>),
40+
#[strum(serialize = "Search Results")]
41+
SearchResults(Server),
4042
}
4143

4244
impl Buffer {
@@ -103,19 +105,19 @@ impl Upstream {
103105
pub fn server_message_target(
104106
self,
105107
source: Option<message::source::Server>,
106-
) -> message::Target {
108+
) -> Option<message::Target> {
107109
match self {
108-
Self::Server(_) => message::Target::Server {
110+
Self::Server(_) => Some(message::Target::Server {
109111
source: message::Source::Server(source),
110-
},
111-
Self::Channel(_, channel) => message::Target::Channel {
112+
}),
113+
Self::Channel(_, channel) => Some(message::Target::Channel {
112114
channel,
113115
source: message::Source::Server(source),
114-
},
115-
Self::Query(_, query) => message::Target::Query {
116+
}),
117+
Self::Query(_, query) => Some(message::Target::Query {
116118
query,
117119
source: message::Source::Server(source),
118-
},
120+
}),
119121
}
120122
}
121123
}
@@ -130,12 +132,14 @@ impl Internal {
130132

131133
pub fn key(&self) -> String {
132134
match self {
133-
Internal::FileTransfers => "file-transfers",
134-
Internal::Logs => "logs",
135-
Internal::Highlights => "highlights",
136-
Internal::ChannelDiscovery(_) => "channel-discovery",
135+
Internal::FileTransfers => "file-transfers".to_string(),
136+
Internal::Logs => "logs".to_string(),
137+
Internal::Highlights => "highlights".to_string(),
138+
Internal::ChannelDiscovery(_) => "channel-discovery".to_string(),
139+
Internal::SearchResults(server) => {
140+
format!("server:{server}:search-results")
141+
}
137142
}
138-
.to_string()
139143
}
140144
}
141145

data/src/capabilities.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ pub enum Capability {
3030
MultiPrefix,
3131
ReadMarker,
3232
Sasl,
33+
Search,
3334
ServerTime,
3435
Setname,
3536
UserhostInNames,
@@ -58,6 +59,7 @@ impl FromStr for Capability {
5859
"server-time" => Ok(Self::ServerTime),
5960
"setname" => Ok(Self::Setname),
6061
"soju.im/bouncer-networks" => Ok(Self::BouncerNetworks),
62+
"soju.im/search" => Ok(Self::Search),
6163
"userhost-in-names" => Ok(Self::UserhostInNames),
6264
_ if cap.starts_with("sasl") => Ok(Self::Sasl),
6365
_ => Err("unknown capability"),
@@ -328,6 +330,12 @@ impl Capabilities {
328330
requested.push("setname");
329331
}
330332

333+
if self.pending.contains("soju.im/search")
334+
&& !self.acknowledged(Capability::Search)
335+
{
336+
requested.push("soju.im/search");
337+
}
338+
331339
if self.pending.contains("soju.im/bouncer-networks")
332340
&& !self.acknowledged(Capability::BouncerNetworks)
333341
{

data/src/client.rs

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -845,6 +845,7 @@ impl Client {
845845
)
846846
})
847847
}
848+
Some("soju.im/search") => Some(BatchKind::Search),
848849
_ => None,
849850
};
850851

@@ -970,6 +971,7 @@ impl Client {
970971
_,
971972
_,
972973
))
974+
| Some(BatchKind::Search)
973975
| None => (),
974976
};
975977

@@ -1002,6 +1004,7 @@ impl Client {
10021004
self.handle_multiline(message, batch_tag);
10031005
vec![]
10041006
}
1007+
Some(BatchKind::Search) => self.handle_search(message),
10051008
Some(BatchKind::ChathistoryTargets) | None => {
10061009
self.handle(message, context, config)?
10071010
}
@@ -1024,7 +1027,7 @@ impl Client {
10241027
_ if context.as_ref().is_some_and(Context::is_whois) => {
10251028
if let Some(source) = context
10261029
.map(Context::buffer)
1027-
.map(|buffer| buffer.server_message_target(None))
1030+
.and_then(|buffer| buffer.server_message_target(None))
10281031
{
10291032
return Ok(vec![Event::WithTarget(
10301033
message,
@@ -1052,7 +1055,7 @@ impl Client {
10521055
if let Some(source) = self
10531056
.reroute_responses_to
10541057
.clone()
1055-
.map(|buffer| buffer.server_message_target(None))
1058+
.and_then(|buffer| buffer.server_message_target(None))
10561059
{
10571060
return Ok(vec![Event::WithTarget(
10581061
message,
@@ -1787,7 +1790,7 @@ impl Client {
17871790
} else if let Some(source) = self
17881791
.reroute_responses_to
17891792
.clone()
1790-
.map(|buffer| buffer.server_message_target(None))
1793+
.and_then(|buffer| buffer.server_message_target(None))
17911794
{
17921795
return Ok(vec![Event::WithTarget(
17931796
message,
@@ -1898,7 +1901,7 @@ impl Client {
18981901
} else if let Some(source) = self
18991902
.reroute_responses_to
19001903
.clone()
1901-
.map(|buffer| buffer.server_message_target(None))
1904+
.and_then(|buffer| buffer.server_message_target(None))
19021905
{
19031906
return Ok(vec![Event::WithTarget(
19041907
message,
@@ -1973,7 +1976,7 @@ impl Client {
19731976
} else if let Some(source) = self
19741977
.reroute_responses_to
19751978
.clone()
1976-
.map(|buffer| buffer.server_message_target(None))
1979+
.and_then(|buffer| buffer.server_message_target(None))
19771980
{
19781981
return Ok(vec![Event::WithTarget(
19791982
message,
@@ -2264,10 +2267,7 @@ impl Client {
22642267
)));
22652268
let timestamp =
22662269
Posix::from_seconds(ok!(args.get(3)).parse::<u64>()?);
2267-
channel.topic.time =
2268-
Some(timestamp.datetime().ok_or_else(|| {
2269-
anyhow!("Unable to parse timestamp: {timestamp:?}")
2270-
})?);
2270+
channel.topic.time = Some(timestamp.datetime());
22712271
}
22722272
// Exclude topic message from history to prevent spam during dev
22732273
#[cfg(debug_assertions)]
@@ -3123,6 +3123,31 @@ impl Client {
31233123
}
31243124
}
31253125

3126+
fn handle_search(&mut self, message: message::Encoded) -> Vec<Event> {
3127+
if let Command::PRIVMSG(target, _) | Command::NOTICE(target, _) =
3128+
&message.command
3129+
&& let Some(user) = message.user(self.casemapping())
3130+
{
3131+
let target = Target::parse(
3132+
target,
3133+
self.chantypes(),
3134+
self.statusmsg(),
3135+
self.casemapping(),
3136+
);
3137+
3138+
vec![Event::WithTarget(
3139+
message,
3140+
self.nickname().to_owned(),
3141+
message::Target::SearchResults {
3142+
target: Some(target),
3143+
source: source::Source::User(user),
3144+
},
3145+
)]
3146+
} else {
3147+
vec![]
3148+
}
3149+
}
3150+
31263151
fn send_markread(
31273152
&mut self,
31283153
target: Target,
@@ -4574,6 +4599,12 @@ impl Map {
45744599
})
45754600
}
45764601

4602+
pub fn get_server_supports_search(&self, server: &Server) -> bool {
4603+
self.client(server).is_some_and(|client| {
4604+
client.capabilities.acknowledged(Capability::Search)
4605+
})
4606+
}
4607+
45774608
pub fn get_chathistory_request(
45784609
&self,
45794610
server: &Server,
@@ -4783,6 +4814,7 @@ pub enum BatchKind {
47834814
Option<MultilineBatchKind>,
47844815
String,
47854816
),
4817+
Search,
47864818
}
47874819

47884820
impl BatchKind {
@@ -4792,7 +4824,7 @@ impl BatchKind {
47924824
| Self::Multiline(_, _, batch_target, _, _) => {
47934825
Some(batch_target.clone())
47944826
}
4795-
Self::ChathistoryTargets => None,
4827+
Self::ChathistoryTargets | Self::Search => None,
47964828
}
47974829
}
47984830
}

data/src/client/on_connect.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,8 @@ pub fn on_connect(
106106
| command::Internal::Exec(_)
107107
| command::Internal::Hop(_, _)
108108
| command::Internal::SysInfo
109-
| command::Internal::Reconnect => None,
109+
| command::Internal::Reconnect
110+
| command::Internal::SearchResults(_) => None,
110111
},
111112
}
112113
}

data/src/command.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ pub enum Internal {
3636
/// - Part message
3737
Hop(Option<String>, Option<String>),
3838
ChannelDiscovery,
39+
SearchResults(Option<String>),
3940
Delay(u64),
4041
SysInfo,
4142
Detach(Vec<target::Channel>),
@@ -77,6 +78,7 @@ pub enum Irc {
7778
value: Typing,
7879
},
7980
Raw(String),
81+
Search(String),
8082
Unknown(String, Vec<String>),
8183
Ctcp(ctcp::Command, String, Option<String>),
8284
Chathistory(String, Vec<String>),
@@ -192,6 +194,18 @@ impl Irc {
192194
supports_echoes.then_some(self.clone()),
193195
)])
194196
}
197+
Irc::Search(search_query) => {
198+
let message_target = message::Target::SearchResults {
199+
target: None,
200+
source: message::Source::Server(None),
201+
};
202+
203+
Some(vec![Message::sent(
204+
message_target,
205+
message::search_query_text(search_query)?,
206+
None,
207+
)])
208+
}
195209
_ => None,
196210
}
197211
}
@@ -233,6 +247,7 @@ pub enum Kind {
233247
Connect,
234248
Reconnect,
235249
Exec,
250+
Search,
236251
Raw,
237252
}
238253

@@ -276,6 +291,7 @@ impl FromStr for Kind {
276291
"connect" => Ok(Kind::Connect),
277292
"reconnect" => Ok(Kind::Reconnect),
278293
"exec" => Ok(Kind::Exec),
294+
"search" => Ok(Kind::Search),
279295
_ => Err(()),
280296
}
281297
}
@@ -1522,6 +1538,9 @@ fn parse_command(
15221538
Ok(Command::Internal(Internal::Exec(command.to_string())))
15231539
}
15241540
}
1541+
Kind::Search => validated::<0, 1, true>(args, |_, [text]| {
1542+
Ok(Command::Internal(Internal::SearchResults(text)))
1543+
}),
15251544
},
15261545
Err(()) => Ok(unknown()),
15271546
}
@@ -1715,6 +1734,7 @@ impl TryFrom<Irc> for proto::Command {
17151734
Irc::List(channels, elistcond) => {
17161735
proto::Command::LIST(channels, elistcond)
17171736
}
1737+
Irc::Search(params) => proto::Command::SEARCH(params),
17181738
})
17191739
}
17201740
}

data/src/config/actions.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@ pub struct Buffer {
1616
pub click_highlight: BufferAction,
1717
pub click_username: BufferAction,
1818
pub join_channel: Option<BufferAction>,
19+
pub list: BufferAction,
1920
pub local: BufferAction,
2021
pub message_channel: BufferAction,
2122
pub message_user: BufferAction,
23+
pub search: BufferAction,
2224
}
2325

2426
#[derive(Debug, Default, Clone, Deserialize)]

0 commit comments

Comments
 (0)