Skip to content

Commit

Permalink
Make default config stuff disappear when the default-config feature i…
Browse files Browse the repository at this point in the history
…s disabled
  • Loading branch information
Scripter17 committed Oct 22, 2024
1 parent 844234c commit 2349c2a
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 70 deletions.
56 changes: 27 additions & 29 deletions benchmarking/benchmark.sh
Original file line number Diff line number Diff line change
Expand Up @@ -75,34 +75,32 @@ if [ $hyperfine -eq 1 ]; then
bat -pl json
fi

for url in "${URLS[@]}"; do
echo "IN : $url"
echo "OUT: $(../target/release/url-cleaner --config ../default-config.json $url)"
file_safe_in_url=$(echo $url | head -c 50 | sed "s/\//-/g")
if [ $valgrind -eq 1 ]; then
for num in "${NUMS[@]}"; do
if [ $callgrind -eq 1 ]; then
echo "Callgrind - $num"
yes "$url" | head -n $num | valgrind --quiet --tool=callgrind --callgrind-out-file="callgrind.out-$file_safe_in_url-$num-%p" $COMMAND > /dev/null
fi
if [ $cachegrind -eq 1 ]; then
echo "Cachegrind - $num"
yes "$url" | head -n $num | valgrind --quiet --tool=cachegrind --cachegrind-out-file="cachegrind.out-$file_safe_in_url-$num-%p" $COMMAND > /dev/null
fi
if [ $massif -eq 1 ]; then
echo "Massif - $num"
yes "$url" | head -n $num | valgrind --quiet --tool=massif --massif-out-file="massif.out-$file_safe_in_url-$num-%p" $COMMAND > /dev/null
fi
if [ $dhat -eq 1 ]; then
echo "Dhat - $num"
yes "$url" | head -n $num | valgrind --quiet --tool=dhat --dhat-out-file="dhat.out-$file_safe_in_url-$num-%p" $COMMAND > /dev/null
fi
if [ $memcheck -eq 1 ]; then
echo "Memcheck - $num"
yes "$url" | head -n $num | valgrind --quiet --tool=memcheck $COMMAND > /dev/null 2> "memcheck.out-$file_safe_in_url-$num"
fi
done
fi
done
if [ $valgrind -eq 1 ]; then
for url in "${URLS[@]}"; do
file_safe_in_url=$(echo $url | head -c 50 | sed "s/\//-/g")
for num in "${NUMS[@]}"; do
if [ $callgrind -eq 1 ]; then
echo "Callgrind - $num - $url"
yes "$url" | head -n $num | valgrind --quiet --tool=callgrind --callgrind-out-file="callgrind.out-$file_safe_in_url-$num-%p" $COMMAND > /dev/null
fi
if [ $cachegrind -eq 1 ]; then
echo "Cachegrind - $num - $url"
yes "$url" | head -n $num | valgrind --quiet --tool=cachegrind --cachegrind-out-file="cachegrind.out-$file_safe_in_url-$num-%p" $COMMAND > /dev/null
fi
if [ $massif -eq 1 ]; then
echo "Massif - $num - $url"
yes "$url" | head -n $num | valgrind --quiet --tool=massif --massif-out-file="massif.out-$file_safe_in_url-$num-%p" $COMMAND > /dev/null
fi
if [ $dhat -eq 1 ]; then
echo "Dhat - $num - $url"
yes "$url" | head -n $num | valgrind --quiet --tool=dhat --dhat-out-file="dhat.out-$file_safe_in_url-$num-%p" $COMMAND > /dev/null
fi
if [ $memcheck -eq 1 ]; then
echo "Memcheck - $num - $url"
yes "$url" | head -n $num | valgrind --quiet --tool=memcheck $COMMAND > /dev/null 2> "memcheck.out-$file_safe_in_url-$num"
fi
done
done
fi

tar -czf "benchmarks-$(date +%s).tar.gz" *.out*
26 changes: 11 additions & 15 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ use std::io::{self, IsTerminal};
use std::borrow::Cow;
use std::process::ExitCode;
use std::collections::HashMap;
use std::str::FromStr;

use clap::{Parser, CommandFactory};
use url::Url;
use thiserror::Error;

mod glue;
Expand Down Expand Up @@ -48,8 +48,13 @@ pub struct Args {
/// The URLs to clean before the URLs in the STDIN.
pub urls: Vec<String>,
/// The JSON config to use. If unspecified and URL Cleaner was compiled with the default-config feature, use the default config compiled into URL Cleaner.
#[cfg(feature = "default-config")]
#[arg(short , long)]
pub config: Option<PathBuf>,
/// The JSON config to use. Has to be set because this instance of URL Cleaner was compiled without a default config.
#[cfg(not(feature = "default-config"))]
#[arg(short , long)]
pub config: PathBuf,
/// Output JSON. It is intended to be identical to URL Cleaner Site's output, so while some of the output is "redundant", it's important.
#[arg(short , long)]
pub json: bool,
Expand Down Expand Up @@ -142,18 +147,6 @@ fn str_to_json_str(s: &str) -> String {
serde_json::to_string(s).expect("Serializing a string to never fail.")
}

/// Allows turing `https://..`, `"https://.."`, and `{"url": "http://..", ..}` into a [`JobConfig`].
fn url_or_job_config_str_to_job_config(line: &str) -> Result<JobConfig, JobConfigSourceError> {
Ok(if line.starts_with('"') || line.starts_with('{') {
match serde_json::from_str(line) {
Ok(x) => x,
Err(e) => Err(JobConfigSourceError::Other(Box::new(e)))?
}
} else {
Url::parse(line)?.into()
})
}

fn main() -> Result<ExitCode, CliError> {
#[cfg(feature = "debug-time")] let start_time = std::time::Instant::now();

Expand Down Expand Up @@ -192,7 +185,10 @@ fn main() -> Result<ExitCode, CliError> {

#[cfg(feature = "debug-time")] let x = std::time::Instant::now();

#[cfg(feature = "default-config")]
let mut config = Config::get_default_no_cache_or_load(args.config.as_deref())?;
#[cfg(not(feature = "default-config"))]
let mut config = Config::load_from_file(&args.config)?;

#[cfg(feature = "debug-time")] eprintln!("Load Config: {:?}", x.elapsed());
#[cfg(feature = "debug-time")] let x = std::time::Instant::now();
Expand Down Expand Up @@ -272,9 +268,9 @@ fn main() -> Result<ExitCode, CliError> {
#[cfg(feature = "cache")]
cache: args.cache_path.as_deref().unwrap_or(&*config.cache_path).into(),
job_config_source: {
let ret = args.urls.into_iter().map(|url| url_or_job_config_str_to_job_config(&url));
let ret = args.urls.into_iter().map(|url| JobConfig::from_str(&url).map_err(Into::into));
if !io::stdin().is_terminal() {
Box::new(ret.chain(io::stdin().lines().map(|line| url_or_job_config_str_to_job_config(&line?))))
Box::new(ret.chain(io::stdin().lines().map(|line| JobConfig::from_str(&line?).map_err(Into::into))))
} else {
Box::new(ret)
}
Expand Down
29 changes: 13 additions & 16 deletions src/types/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,36 +72,29 @@ impl Config {
}

/// Gets the config compiled into the URL Cleaner binary.
///
/// On the first call, it parses [`DEFAULT_CONFIG_STR`] and caches it in [`DEFAULT_CONFIG`]. On all future calls it simply returns the cached value.
/// # Errors
/// If the default config cannot be parsed, returns the error [`GetConfigError::CantParseDefaultConfig`].
///
/// If URL Cleaner was compiled without a default config, returns the error [`GetConfigError::NoDefaultConfig`].
#[allow(dead_code, reason = "Public API.")]
#[cfg(feature = "default-config")]
pub fn get_default() -> Result<&'static Self, GetConfigError> {
#[cfg(feature = "default-config")]
if let Some(config) = DEFAULT_CONFIG.get() {
Ok(config)
} else {
let config = Self::get_default_no_cache()?;
Ok(DEFAULT_CONFIG.get_or_init(|| config))
}
#[cfg(not(feature = "default-config"))]
Err(GetConfigError::NoDefaultConfig)
}

/// Useful for when you know you're only getting the config once and, if needed, caching it yourself.
///
/// Generally, [`Self::get_default`] should be used over calling this function multiple times.
/// # Errors
/// If the default config cannot be parsed, returns the error [`GetConfigError::CantParseDefaultConfig`].
///
/// If URL Cleaner was compiled without a default config, returns the error [`GetConfigError::NoDefaultConfig`].
#[cfg(feature = "default-config")]
pub fn get_default_no_cache() -> Result<Self, GetConfigError> {
#[cfg(feature = "default-config")]
return serde_json::from_str(DEFAULT_CONFIG_STR).map_err(GetConfigError::CantParseDefaultConfig);
#[cfg(not(feature = "default-config"))]
Err(GetConfigError::NoDefaultConfig)
serde_json::from_str(DEFAULT_CONFIG_STR).map_err(GetConfigError::CantParseDefaultConfig)
}

/// If `path` is `Some`, returns [`Self::load_from_file`].
Expand All @@ -112,6 +105,7 @@ impl Config {
///
/// If `path` is `Some` and the call to [`Self::load_from_file`] returns an error, that error is returned.
#[allow(dead_code, reason = "Public API.")]
#[cfg(feature = "default-config")]
pub fn get_default_or_load(path: Option<&Path>) -> Result<Cow<'static, Self>, GetConfigError> {
Ok(match path {
Some(path) => Cow::Owned(Self::load_from_file(path)?),
Expand All @@ -124,8 +118,7 @@ impl Config {
/// Generally, [`Self::get_default_or_load`] should be used over calling this function with the same argument multiple times.
/// # Errors
/// If the default config cannot be parsed, returns the error [`GetConfigError::CantParseDefaultConfig`].
///
/// If URL Cleaner was compiled without a default config, returns the error [`GetConfigError::NoDefaultConfig`].
#[cfg(feature = "default-config")]
pub fn get_default_no_cache_or_load(path: Option<&Path>) -> Result<Self, GetConfigError> {
Ok(match path {
Some(path) => Self::load_from_file(path)?,
Expand Down Expand Up @@ -205,11 +198,9 @@ pub enum GetConfigError {
/// The loaded config file did not contain valid JSON.
#[error(transparent)]
CantParseConfigFile(serde_json::Error),
/// URL Cleaner was compiled without default config.
#[error("URL Cleaner was compiled without default config.")]
NoDefaultConfig,
/// The default config compiled into URL Cleaner isn't valid JSON.
#[error(transparent)]
#[cfg(feature = "default-config")]
CantParseDefaultConfig(serde_json::Error)
}

Expand All @@ -219,29 +210,35 @@ mod tests {
use super::*;

#[test]
#[cfg(feature = "default-config")]
fn deserialize_default_config() {
Config::get_default().unwrap();
}

#[test]
#[cfg(feature = "default-config")]
fn reserialize_default_config() {
serde_json::to_string(&Config::get_default().unwrap()).unwrap();
}

/// Does not work when generic.
///
/// <'a, T: Serialize+Deserialize<'a>> throws nonsensical errors like `y.to_owned()` freed while still in use despite being an owned value.
#[cfg(feature = "default-config")]
fn de_ser(config: &Config) -> Config {
serde_json::from_str(&serde_json::to_string(config).unwrap()).unwrap()
}

#[test]
#[cfg(feature = "default-config")]
fn default_config_de_ser_identity() {
assert_eq!(Config::get_default().unwrap(), &de_ser(Config::get_default().unwrap()) );
assert_eq!(Config::get_default().unwrap(), &de_ser(&de_ser(Config::get_default().unwrap())) );
assert_eq!(Config::get_default().unwrap(), &de_ser(&de_ser(&de_ser(Config::get_default().unwrap()))));
}

#[test]
#[cfg(feature = "default-config")]
fn test_default_config() {
Config::get_default().unwrap().clone().run_tests();
}
Expand Down
24 changes: 20 additions & 4 deletions src/types/jobs/job_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use std::str::FromStr;

use serde::{Serialize, Deserialize};
use url::Url;
use thiserror::Error;

use crate::types::*;
use crate::util::*;
Expand All @@ -29,11 +30,26 @@ impl From<Url> for JobConfig {
}
}

/// The enum of errors [`JobConfig::from_str`] and [`<JobConfig as TryFrom<&str>>::try_from`] can return.
#[derive(Debug, Error)]
pub enum MakeJobConfigError {
/// Returned when a [`url::ParseError`] is encoutered.
#[error(transparent)]
UrlParseError(#[from] url::ParseError),
/// Returned when a [`serde_json::Error`] is encountered.
#[error(transparent)]
SerdeJsonError(#[from] serde_json::Error)
}

impl FromStr for JobConfig {
type Err = <Url as FromStr>::Err;
type Err = MakeJobConfigError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
Url::from_str(s).map(Into::into)
Ok(if s.starts_with('{') {
serde_json::from_str(s)?
} else {
Url::parse(s)?.into()
})
}
}

Expand All @@ -50,9 +66,9 @@ string_or_struct_magic!(JobConfig);
/// The enum of errors that can happen when [`Jobs::iter`] tries to get a URL.
#[derive(Debug, Error)]
pub enum JobConfigSourceError {
/// Returned when a [`url::ParseError`] is encountered.
/// Returned when a [`MakeJobConfigError`] is encountered.
#[error(transparent)]
UrlParseError(#[from] url::ParseError),
MakeJobConfigError(#[from] MakeJobConfigError),
/// Returned when a [`std::io::Error`] is encountered.
#[error(transparent)]
IoError(#[from] std::io::Error),
Expand Down
15 changes: 9 additions & 6 deletions src/types/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ impl TestSet {
/// # Panics
/// Panics if a call to [`Expectation::run`] panics.
pub fn run(&self, config: &Config) {
println!("Testing the following test set:\n{}", serde_json::to_string(self).expect("The entire config to be serializable")); // Only applies when testing a config.
println!("Testing the following test set:\n{}", serde_json::to_string(self).expect("The entire config to be serializable"));
let mut config = Cow::Borrowed(config);
if let Some(params_diff) = &self.params_diff {
params_diff.apply(&mut config.to_mut().params);
Expand All @@ -49,12 +49,15 @@ pub struct Expectation {
impl Expectation {
/// Runs the test.
/// # Panics
/// Panics if a making the [`Cache`], a call to [`Rules::apply`], or a test fails.
/// If serializing `self` fails, panics.
///
/// If the call to [`Config::apply`] fails, panics.
///
/// If thw expectation fails, you guessed it, panics.
pub fn run(&self, config: &Config) {
println!("Testing the following expectation set:\n{}", serde_json::to_string(self).expect("The entire config to be serializable")); // Only applies when testing a config.
println!("Testing the following expectation set:\n{}", serde_json::to_string(self).expect("The entire config to be serializable"));
let mut url = self.job_config.url.clone();
#[cfg(feature = "cache")]
config.rules.apply(&mut JobState {
config.apply(&mut JobState {
url: &mut url,
params: &config.params,
scratchpad: &mut Default::default(),
Expand All @@ -63,7 +66,7 @@ impl Expectation {
cache: &Default::default(),
commons: &config.commons,
common_args: None
}).expect("The URL to be modified without errors."); // Only applies when testing a config.
}).expect("The URL to be modified without errors.");
assert_eq!(url, self.result);
}
}

0 comments on commit 2349c2a

Please sign in to comment.