Skip to content

Commit

Permalink
i'm tired.,
Browse files Browse the repository at this point in the history
  • Loading branch information
Scripter17 committed Mar 3, 2024
1 parent f342e34 commit c042eda
Show file tree
Hide file tree
Showing 12 changed files with 162 additions and 287 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ callgrind*
hyperfine*
*.zip
mprocs.yaml
bacon.toml
26 changes: 13 additions & 13 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,13 @@ percent-encoding = "2.3.1"
crate-type = ["cdylib", "rlib"]

[features]
default = ["http", "commands", "cache-redirects", "stdin", "default-config", "minify-included-strings", "regex", "glob", "string-location", "string-matcher", "string-modification", "string-source", "string-cmp", "bool-source"]
default = ["http", "commands", "cache-redirects", "stdin", "default-config", "minify-included-strings", "regex", "glob", "string-location", "string-matcher", "string-modification", "string-source", "bool-source", "bypass-vip"]
# Enables HTTP stuff. DOES NOT WORK ON WASM.
http = ["dep:reqwest"]
# Enables [`url_cleaner::glue::CommandWrapper`].
commands = []
# Write the result of [`url_cleaner::rules::mappers::Mapper::ExpandShortLink`] to redirect-cache.txt.
cache-redirects = []
cache-redirects = ["http"]
# Allow reading URLs from STDIN.
stdin = ["dep:atty"]
# Include default-config.json in the binary.
Expand All @@ -58,10 +58,10 @@ string-matcher = []
string-modification = []
# Enables [`types::StringSource`].
string-source = []
# Enables [`types::StringCmp`].
string-cmp = []
# Enables [`types::BoolSource`].
bool-source = []
# Enables [`rules::Mapper::BypassVip`]
bypass-vip = ["http"]

# https://stackoverflow.com/a/54842093/10720231
[profile.release]
Expand Down
11 changes: 11 additions & 0 deletions default-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,17 @@



{
"comment": "https://https//example.com/example.com/abc -> https://example.com/abc",
"condition": {"All": [
{"Not": {"FlagIsSet": "no-unmangle"}},
{"HostIsOneOf": ["http", "https"]}
]},
"mapper": {"All": [
{"CopyPart": {"from": {"PathSegment": 2}, "to": "Host"}},
{"CopyPart": {"from": {"PartSegments": {"part": "Path", "split": "/", "start": 4}}, "to": "Path"}}
]}
},
{
"comment": "https://abc.com/https://abc.com/user",
"condition": {"All": [
Expand Down
27 changes: 0 additions & 27 deletions src/rules/conditions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -510,28 +510,6 @@ pub enum Condition {
/// assert!(Condition::FlagIsSet("abc".to_string()).satisfied_by(&Url::parse("https://example.com").unwrap(), &Params::default() ).is_ok_and(|x| x==false));
/// ```
FlagIsSet(String),
/// Get two strings them compare them.
/// Passes if the comparison returns `true`.
/// # Errors
/// If either `l` or `r` return an error, that error is returned.
/// If either `l` or `r` return `None` because the respective `none_to_empty_string` is `false`, returns the error [`ConditionError::StringSourceIsNone`].
#[cfg(all(feature = "string-source", feature = "string-cmp"))]
StringCmp {
/// The source of the left hand side of the comparison.
#[serde(deserialize_with = "string_or_struct")]
l: StringSource,
/// If `l` returns `None` and this is `true`, pretend `l` returned `Some("")`.
#[serde(default = "get_true")]
l_none_to_empty_string: bool,
/// The source of the right hand side of the comparison.
#[serde(deserialize_with = "string_or_struct")]
r: StringSource,
/// If `r` returns `None` and this is `true`, pretend `r` returned `Some("")`.
#[serde(default = "get_true")]
r_none_to_empty_string: bool,
/// How to compare the strings from `l` and `r`.
cmp: StringCmp
},
/// Get a boolean value and pass if it's `true`.
/// # Errors
/// If the call to [`BoolSource::get`] returns an error, that error is returned.
Expand Down Expand Up @@ -723,11 +701,6 @@ impl Condition {
#[cfg(not(feature = "string-source"))]
Self::VarIs {name, name_none_to_empty_string: _, value, value_none_to_empty_string} => params.vars.get(name).map(|x| &**x).or(if *value_none_to_empty_string {Some("")} else {None})==value.as_deref(),
Self::FlagIsSet(name) => params.flags.contains(name),
#[cfg(all(feature = "string-source", feature = "string-cmp"))]
Self::StringCmp {l, r, l_none_to_empty_string, r_none_to_empty_string, cmp} => cmp.satisfied_by(
&l.get(url, params, *l_none_to_empty_string)?.ok_or(ConditionError::StringSourceIsNone)?,
&r.get(url, params, *r_none_to_empty_string)?.ok_or(ConditionError::StringSourceIsNone)?
),
#[cfg(feature = "bool-source")]
Self::BoolSource(bool_source) => bool_source.get(url, params)?,

Expand Down
109 changes: 42 additions & 67 deletions src/rules/mappers.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
//! The logic for how to modify a URL.
#[cfg(feature = "cache-redirects")]
use std::{
path::Path,
io::{self, BufRead, Write, Error as IoError},
fs::{OpenOptions, File}
};

use std::str::Utf8Error;
use std::collections::hash_set::HashSet;
use std::collections::HashMap;
Expand Down Expand Up @@ -305,20 +298,21 @@ pub enum Mapper {
regex: RegexWrapper,
/// The pattern the extracted parts are put into.
/// See [`regex::Regex::replace`] for details.
#[serde(default = "efup_expand")]
#[serde(default = "eufp_expand")]
replace: String
},

// Miscellaneous.

/// Sends an HTTP request to the current URL and replaces it with the URL the website responds with.
/// Useful for link shorteners like `bit.ly` and `t.co`.
/// This mapper only works on non-WASM targets.
/// This is both because CORS makes this mapper useless and because `reqwest::blocking` does not work on WASM targets.
/// See [reqwest#891](https://github.com/seanmonstar/reqwest/issues/891) and [reqwest#1068](https://github.com/seanmonstar/reqwest/issues/1068) for details.
/// # Errors
/// If URL Cleaner is compiled with the feature `cache-redirects`, the provided URL is found in the cache, but its cached result cannot be parsed as a URL, returns the error [`MapperError::UrlParseError`].
/// If the call to [`Params::get_url_from_cache`] returns an error, that error is returned.
/// If the [`reqwest::blocking::Client`] is not able to send the HTTP request, returns the error [`MapperError::ReqwestError`].
/// All errors regarding caching the redirect to disk are ignored. This may change in the future.
/// This is both because CORS makes this mapper useless and because `reqwest::blocking` does not work on WASM targets.
/// See [reqwest#891](https://github.com/seanmonstar/reqwest/issues/891) and [reqwest#1068](https://github.com/seanmonstar/reqwest/issues/1068) for details.
/// # Examples
/// ```
/// # use url_cleaner::rules::Mapper;
Expand Down Expand Up @@ -359,7 +353,7 @@ pub enum Mapper {
regex: RegexWrapper,
/// The substitution for use in [`regex::Captures::expand`].
/// Defaults to `"$1"`.
#[serde(default = "efup_expand")]
#[serde(default = "eufp_expand")]
expand: String
},
/// Execute a command and sets the URL to its output. Any argument parameter with the value `"{}"` is replaced with the URL. If the command STDOUT ends in a newline it is stripped.
Expand Down Expand Up @@ -401,10 +395,6 @@ pub enum MapperError {
#[cfg(all(feature = "http", not(target_family = "wasm")))]
#[error(transparent)]
ReqwestError(#[from] ReqwestError),
/// Returned when an [`IoError`] is encountered.
#[cfg(feature = "cache-redirects")]
#[error(transparent)]
IoError(#[from] IoError),
/// Returned when a [`Utf8Error`] is encountered.
#[error(transparent)]
Utf8Error(#[from] Utf8Error),
Expand Down Expand Up @@ -446,19 +436,16 @@ pub enum MapperError {
#[cfg(feature = "string-modification")]
#[error(transparent)]
StringModificationError(#[from] StringModificationError),
#[error("ResponseJsonIsNotAMap")]
ResponseJsonIsNotAMap,
#[error("ResponseJsonMapDoesNotHaveKey")]
ResponseJsonMapDoesNotHaveKey,
#[error("ResponseJsonIsNotAStr")]
ResponseJsonIsNotAStr
}

#[cfg(feature = "cache-redirects")]
fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>>
where P: AsRef<Path>, {
let file = File::open(filename)?;
Ok(io::BufReader::new(file).lines())
/// Returned by Mapper::BypassVip when an unexpected API response is returned.
#[cfg(feature = "bypass-vip")]
#[error("Returned by Mapper::BypassVip when an unexpected API response is returned.")]
UnexpectedBypassVipResponse,
/// Returned when a [`ReadCacheError`] is encountered.
#[error(transparent)]
ReadCacheError(#[from] ReadCacheError),
/// Returned when a [`WriteCacheError`] is encountered.
#[error(transparent)]
WriteCacheError(#[from] WriteCacheError)
}

impl Mapper {
Expand Down Expand Up @@ -574,12 +561,18 @@ impl Mapper {
#[cfg(feature = "string-modification")]
Self::ModifyPart{part, part_none_to_empty_string, how} => part.modify(url, *part_none_to_empty_string, how, params)?,
Self::CopyPart{from, from_none_to_empty_string, to} => to.set(url, from.get(url, *from_none_to_empty_string).map(|x| x.into_owned()).as_deref())?,
#[cfg(feature = "regex")]
#[cfg(all(feature = "regex", feature = "string-source"))]
Self::RegexSubUrlPart {part, part_none_to_empty_string, regex, replace} => {
let old_part_value=part.get(url, *part_none_to_empty_string).ok_or(MapperError::UrlPartNotFound)?;
#[allow(clippy::unnecessary_to_owned)]
part.set(url, Some(&regex.replace(&old_part_value, replace.get(url, params, false)?.ok_or(MapperError::StringSourceIsNone)?).into_owned()))?;
},
#[cfg(all(feature = "regex", not(feature = "string-source")))]
Self::RegexSubUrlPart {part, part_none_to_empty_string, regex, replace} => {
let old_part_value=part.get(url, *part_none_to_empty_string).ok_or(MapperError::UrlPartNotFound)?;
#[allow(clippy::unnecessary_to_owned)]
part.set(url, Some(&regex.replace(&old_part_value, replace).to_string()))?;
},

// Error handling

Expand All @@ -590,65 +583,47 @@ impl Mapper {

#[cfg(all(feature = "http", not(target_family = "wasm")))]
Self::ExpandShortLink{headers} => {
#[cfg(feature = "cache-redirects")]
if let Ok(lines) = read_lines("redirect-cache.txt") {
for line in lines.map_while(Result::ok) {
if let Some((short, long)) = line.split_once('\t') {
if url.as_str()==short {
*url=Url::parse(long)?;
return Ok(());
}
}
}
if let Some(cached_result) = params.get_url_from_cache(url)? {
*url = cached_result;
return Ok(())
}
let new_url=params.http_client()?.get(url.as_str()).headers(headers.clone()).send()?.url().clone();
// Intentionally ignore any and all file writing errors.
#[cfg(feature = "cache-redirects")]
if !params.amnesia {
if let Ok(mut x) = OpenOptions::new().create(true).append(true).open("redirect-cache.txt") {
let _=x.write(format!("\n{}\t{}", url.as_str(), new_url.as_str()).as_bytes());
}
}
params.write_url_map_to_cache(url, &new_url)?;
*url=new_url;
},
#[cfg(all(feature = "http", feature = "regex", not(target_family = "wasm")))]
#[cfg(all(feature = "http", not(target_family = "wasm"), feature = "regex", feature = "string-source"))]
Self::ExtractUrlFromPage{headers, regex, expand} => if let Some(expand) = expand.get(url, params, false)? {
let mut ret = String::new();
regex.captures(&params.http_client()?.get(url.as_str()).headers(headers.clone()).send()?.text()?).ok_or(MapperError::NoRegexMatchesFound)?.expand(&expand, &mut ret);
*url=Url::parse(&ret)?;
} else {
Err(MapperError::StringSourceIsNone)?
},
#[cfg(all(feature = "http", not(target_family = "wasm"), feature = "regex", not(feature = "string-source")))]
Self::ExtractUrlFromPage{headers, regex, expand} => {
let mut ret = String::new();
regex.captures(&params.http_client()?.get(url.as_str()).headers(headers.clone()).send()?.text()?).ok_or(MapperError::NoRegexMatchesFound)?.expand(expand, &mut ret);
*url=Url::parse(&ret)?;
},
#[cfg(feature = "commands")]
Self::ReplaceWithCommandOutput(command) => {*url=command.get_url(Some(url))?;},

#[cfg(all(feature = "http", not(target_family = "wasm")))]
Self::BypassVip => {
// requests.post("https://api.bypass.vip/", data="url=https://t.co/3XdBbanQpQ", headers={"Origin": "https://bypass.vip", "Content-Type": "application/x-www-form-urlencoded"}).json()["destination"]g
#[cfg(feature = "cache-redirects")]
if let Ok(lines) = read_lines("redirect-cache.txt") {
for line in lines.map_while(Result::ok) {
if let Some((short, long)) = line.split_once('\t') {
if url.as_str()==short {
*url=Url::parse(long)?;
return Ok(());
}
}
}
if let Some(cached_result) = params.get_url_from_cache(url)? {
*url = cached_result;
return Ok(())
}
let new_url=Url::parse(params.http_client()?.post("https://api.bypass.vip")
.form(&HashMap::<&str, &str>::from_iter([("url", url.as_str())]))
.headers(HeaderMap::from_iter([(HeaderName::from_static("origin"), HeaderValue::from_static("https://bypass.vip"))]))
.send()?
.json::<serde_json::value::Value>()?
.as_object().ok_or(MapperError::ResponseJsonIsNotAMap)?
.get("destination").ok_or(MapperError::ResponseJsonMapDoesNotHaveKey)?
.as_str().ok_or(MapperError::ResponseJsonIsNotAStr)?)?;
#[cfg(feature = "cache-redirects")]
if !params.amnesia {
if let Ok(mut x) = OpenOptions::new().create(true).append(true).open("redirect-cache.txt") {
let _=x.write(format!("\n{}\t{}", url.as_str(), new_url.as_str()).as_bytes());
}
}
.as_object().ok_or(MapperError::UnexpectedBypassVipResponse)?
.get("destination").ok_or(MapperError::UnexpectedBypassVipResponse)?
.as_str().ok_or(MapperError::UnexpectedBypassVipResponse)?)?;
params.write_url_map_to_cache(url, &new_url)?;
*url=new_url;
},

Expand Down
4 changes: 2 additions & 2 deletions src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ pub use config::*;
#[cfg(feature = "string-source" )] pub use string_source::*;
#[cfg(feature = "string-matcher" )] mod string_matcher;
#[cfg(feature = "string-matcher" )] pub use string_matcher::*;
#[cfg(feature = "string-cmp" )] mod string_cmp;
#[cfg(feature = "string-cmp" )] pub use string_cmp::*;
#[cfg(feature = "bool-source" )] mod bool_source;
#[cfg(feature = "bool-source" )] pub use bool_source::*;
// #[cfg(feature = "http" )] mod request_config;
// #[cfg(feature = "http" )] pub use request_config::*;

/// An enum that, if I've done my job properly, contains any possible error that can happen when cleaning a URL.
/// Except for if a [`crate::rules::Mapper::ExpandShortLink`] response can't be cached. That error is ignored pending a version of [`Result`] that can handle partial errors.
Expand Down
Loading

0 comments on commit c042eda

Please sign in to comment.