Skip to content

Commit 0ce390f

Browse files
add config option for removing query parameters (#1)
1 parent 37e2929 commit 0ce390f

File tree

3 files changed

+51
-19
lines changed

3 files changed

+51
-19
lines changed

config.example.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ label = "Tweet"
2020
regex = "https://(?:x|twitter)\\.com"
2121
# The stem to replace the matched area with.
2222
stem = "https://vxtwitter.com"
23+
# The query params to keep in the URL -- empty ([]) to remove query string entirely or
24+
# omitted to keep entire query string
25+
keep_query = []
2326

2427
[[pass]]
2528
label = "Instagram Post"

src/pass.rs

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::fmt::Write;
1+
use std::{collections::HashMap, fmt::Write};
22

33
use regex::Regex;
44
use serde::{Deserialize, Deserializer};
@@ -9,6 +9,7 @@ pub struct Pass {
99
#[serde(deserialize_with = "pass_regex")]
1010
pub regex: Regex,
1111
pub stem: String,
12+
pub keep_query: Option<Vec<String>>,
1213
}
1314

1415
/// An enum representing the spoiler tags on a link.
@@ -25,7 +26,10 @@ pub enum SpoilerTags {
2526
}
2627

2728
impl Pass {
28-
pub fn extract<'a>(&'a self, content: &'a str) -> impl Iterator<Item = (&'a str, SpoilerTags)> {
29+
pub fn extract<'a>(
30+
&'a self,
31+
content: &'a str,
32+
) -> impl Iterator<Item = (&'a str, &'a str, SpoilerTags)> {
2933
self.regex.captures_iter(content).map(|capture| {
3034
let (_, [sp_open, path, sp_close]) = capture.extract();
3135
let spoiler_marker = match (!sp_open.is_empty(), !sp_close.is_empty()) {
@@ -34,28 +38,36 @@ impl Pass {
3438
_ => SpoilerTags::Mismatched,
3539
};
3640

37-
(path, spoiler_marker)
41+
let (path, query) = path.split_once('?').unwrap_or((path, ""));
42+
43+
(path, query, spoiler_marker)
3844
})
3945
}
4046

4147
pub fn apply<'a>(&'a self, content: &'a str) -> Option<String> {
4248
let Self { label, stem, .. } = self;
4349

44-
let out = self
45-
.extract(content)
46-
.fold(String::new(), |mut out, (path, spoiler_tags)| {
47-
let spoil = spoiler_tags != SpoilerTags::None;
50+
let out =
51+
self.extract(content)
52+
.fold(String::new(), |mut out, (path, query, spoiler_tags)| {
53+
let spoil = spoiler_tags != SpoilerTags::None;
54+
55+
let query_string = match &self.keep_query {
56+
None => format!("?{query}"),
57+
Some(keep) if !keep.is_empty() => filter_query(query, keep),
58+
_ => String::new(),
59+
};
4860

49-
if spoil {
50-
let _ = write!(&mut out, "||");
51-
}
52-
let _ = write!(&mut out, "[`{label}`]({stem}{path}) ");
53-
if spoil {
54-
let _ = write!(&mut out, "|| ");
55-
}
61+
if spoil {
62+
let _ = write!(&mut out, "||");
63+
}
64+
let _ = write!(&mut out, "[`{label}`]({stem}{path}{query_string}) ");
65+
if spoil {
66+
let _ = write!(&mut out, "|| ");
67+
}
5668

57-
out
58-
});
69+
out
70+
});
5971

6072
(!out.is_empty()).then_some(out)
6173
}
@@ -81,3 +93,16 @@ fn pass_regex<'de, D: Deserializer<'de>>(de: D) -> Result<Regex, D::Error> {
8193
Regex::new(&["(?:^|\\s)", "(\\|\\||)", &core, "(/\\S+)", "(\\s?\\|\\||)"].concat())
8294
.map_err(D::Error::custom)
8395
}
96+
97+
/// Removes all query parameters from a query string except those in the provided list
98+
fn filter_query(qs: &str, keep: &Vec<String>) -> String {
99+
let query_map: HashMap<_, _> = qs.split('&').filter_map(|p| p.split_once('=')).collect();
100+
101+
let params = query_map
102+
.iter()
103+
.filter_map(|(k, v)| keep.contains(&k.to_string()).then(|| format!("{k}={v}")))
104+
.collect::<Vec<_>>()
105+
.join("&");
106+
107+
format!("?{params}")
108+
}

tests/passes.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ fn standard_passes() {
88
pass.extract(
99
"
1010
These are just some random test urls.
11-
- https://x.com/rustbeltenjoyer/status/1776056709737320578
11+
- https://x.com/rustbeltenjoyer/status/1776056709737320578?s=46&t=owouwu
1212
- ||https://www.instagram.com/p/C5W2QwZrt-Z/ ||
1313
- https://www.tiktok.com/t/ZPRTX3AwH/
1414
",
@@ -21,14 +21,18 @@ fn standard_passes() {
2121
extracted.next(),
2222
Some((
2323
"/rustbeltenjoyer/status/1776056709737320578",
24+
"s=46&t=owouwu",
2425
SpoilerTags::None
2526
))
2627
);
2728
assert_eq!(
2829
extracted.next(),
29-
Some(("/p/C5W2QwZrt-Z/", SpoilerTags::Spoiler))
30+
Some(("/p/C5W2QwZrt-Z/", "", SpoilerTags::Spoiler))
31+
);
32+
assert_eq!(
33+
extracted.next(),
34+
Some(("/t/ZPRTX3AwH/", "", SpoilerTags::None))
3035
);
31-
assert_eq!(extracted.next(), Some(("/t/ZPRTX3AwH/", SpoilerTags::None)));
3236

3337
assert!(extracted.next().is_none());
3438
}

0 commit comments

Comments
 (0)