diff --git a/Cargo.toml b/Cargo.toml index a433962..a651cf4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "dbgen" version = "0.8.0" authors = ["kennytm "] -edition = "2018" +edition = "2021" license = "MIT" description = "Generate random test cases for databases" repository = "https://github.com/kennytm/dbgen" @@ -10,57 +10,58 @@ exclude = ["fuzz.sh", "release/*"] readme = "README.md" keywords = ["cli", "generator", "database", "fake"] categories = ["command-line-utilities", "simulation"] +rust-version = "1.71.0" [workspace] members = ["dbgen-playground", "dbdbgen"] [dependencies] structopt = { version = "0.3", optional = true } -pest = "2.1" -pest_derive = "2.1" +pest = "2.7" +pest_derive = "2.7" thiserror = "1.0" -rand = { version = "0.8", default-features = false, features = ["getrandom"] } -data-encoding = "2.3" +rand = { version = "0.8.5", default-features = false, features = ["getrandom"] } +data-encoding = "2.4" data-encoding-macro = "0.1" -regex-syntax = "0.6" -pbr = { version = "1.0", optional = true } +regex-syntax = "0.7" +pbr = { version = "1.1", optional = true } num-traits = "0.2" -rayon = { version = "1.3", optional = true } +rayon = { version = "1.7", optional = true } zipf = "7.0" -chrono = { version = "0.4", default-features = false, features = ["serde"] } +chrono = { version = "0.4.26", default-features = false, features = ["std", "serde", "clock"] } tzfile = "0.1" ryu = "1.0" serde = "1.0" muldiv = { version = "1.0", optional = true } rand_distr = { version = "0.4", default-features = false, features = ["alloc"] } -rand_regex = "0.15.1" +rand_regex = "0.16" rand_pcg = { version = "0.3", optional = true } rand_isaac = { version = "0.3", optional = true } rand_chacha = { version = "0.3", optional = true } rand_hc = "0.3" rand_xorshift = { version = "0.3", optional = true } -shlex = { version = "1.0", optional = true } +shlex = { version = "1.1", optional = true } flate2 = { version = "1.0", optional = true } xz2 = { version = "0.1", optional = true } -zstd = { version = "0.9", default-features = false, optional = true } -smallvec = { version = "1.1", default-features = false } -memchr = "2.3" +zstd = { version = "0.12", default-features = false, optional = true } +smallvec = { version = "1.11", default-features = false } +memchr = "2.5" numcmp = "0.1" parse-size = { version = "1.0", optional = true } [dev-dependencies] -regex = { version = "1.3", default-features = false } -tempfile = "3.1" +regex = { version = "1.9", default-features = false } +tempfile = "3.7" serde_json = "1.0" diff = "0.1" -criterion = "0.3" +criterion = "0.5" [[bench]] name = "benchmark" harness = false [build-dependencies] -vergen = { version = "5.1", default-features = false, features = ["git", "cargo"] } +vergen = { version = "8.2", default-features = false, features = ["git", "gitcl", "cargo"] } [features] default = ["cli"] diff --git a/build.rs b/build.rs index 377d3d9..35d1536 100644 --- a/build.rs +++ b/build.rs @@ -1,14 +1,9 @@ -use vergen::{vergen, Config}; +use vergen::EmitBuilder; fn main() { - let mut cfg = Config::default(); - let git = cfg.git_mut(); - *git.branch_mut() = false; - *git.commit_timestamp_mut() = false; - *git.semver_mut() = false; - let cargo = cfg.cargo_mut(); - *cargo.features_mut() = false; - *cargo.profile_mut() = false; - - vergen(cfg).unwrap(); + EmitBuilder::builder() + .git_sha(false) + .cargo_target_triple() + .emit() + .unwrap(); } diff --git a/dbdbgen/Cargo.toml b/dbdbgen/Cargo.toml index 1db9a1e..fa7ecd9 100644 --- a/dbdbgen/Cargo.toml +++ b/dbdbgen/Cargo.toml @@ -10,9 +10,9 @@ dbgen = { path = "../" } jsonnet-rs = "0.17" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -clap = "2.33" +clap = { version = "4.3", features = ["string"] } thiserror = "1.0" rand_core = { version = "0.6", features = ["getrandom"] } -data-encoding = "2.3" -sha2 = "0.9" -parse-size = "1.0" +data-encoding = "2.4" +sha2 = "0.10" +parse-size = { version = "1.0", features = ["std"] } diff --git a/dbdbgen/src/bin/dbdbgen.rs b/dbdbgen/src/bin/dbdbgen.rs index d742bc4..0bd1e54 100644 --- a/dbdbgen/src/bin/dbdbgen.rs +++ b/dbdbgen/src/bin/dbdbgen.rs @@ -1,36 +1,38 @@ -use clap::{App, AppSettings, Arg}; +use clap::{Arg, ArgAction, Command}; use dbdbgen::{cli::ensure_seed, error::Error, jsvm::Vm}; use dbgen::{span::Registry, FULL_VERSION}; -use std::error::Error as StdError; +use std::{error::Error as StdError, ffi::OsStr}; fn run() -> Result<(), Error> { - let global_matches = App::new("dbdbgen") + let global_matches = Command::new("dbdbgen") .long_version(FULL_VERSION) - .setting(AppSettings::TrailingVarArg) + .trailing_var_arg(true) .args(&[ - Arg::with_name("dry-run") + Arg::new("dry-run") .long("dry-run") + .action(ArgAction::SetTrue) .help("Only display the evaluated dbdbgen result without generating data."), - Arg::with_name("allow-import") + Arg::new("allow-import") .long("allow-import") + .action(ArgAction::SetTrue) .help("Allows `import` and `importstr` to read files."), - Arg::with_name("file") + Arg::new("file") .help("The Jsonnet file to execute, followed by the arguments passed to it.") - .multiple(true) + .action(ArgAction::Append) .required(true) .allow_hyphen_values(true), ]) .get_matches(); - let mut args = global_matches.values_of_os("file").unwrap(); - let src_file = args.next().unwrap(); + let mut args = global_matches.get_many("file").unwrap(); + let src_file: &&OsStr = args.next().unwrap(); - let mut vm = Vm::new(src_file, global_matches.is_present("allow-import"))?; + let mut vm = Vm::new(src_file, global_matches.get_flag("allow-import"))?; let app = vm.eval_arguments()?; let mut matches = app.get_matches(args); ensure_seed(&mut matches); let steps = vm.eval_steps(matches)?; - if global_matches.is_present("dry-run") { + if global_matches.get_flag("dry-run") { println!( "/* dbdbgen{}\n*/\n{{\"steps\": {}}}", FULL_VERSION, diff --git a/dbdbgen/src/cli.rs b/dbdbgen/src/cli.rs index 4a69635..6fe45f8 100644 --- a/dbdbgen/src/cli.rs +++ b/dbdbgen/src/cli.rs @@ -1,10 +1,14 @@ +use clap::{ + self, + builder::{PossibleValuesParser, ValueParser}, + value_parser, ArgAction, Command, +}; use data_encoding::HEXLOWER_PERMISSIVE; -use parse_size::parse_size; use rand_core::{OsRng, RngCore}; use serde::{Deserialize, Serialize}; use std::{collections::HashMap, ffi::OsString}; -#[derive(Deserialize, Debug, Clone)] +#[derive(Deserialize, Debug, Clone, PartialEq)] #[serde(rename_all = "lowercase")] pub enum ArgType { Bool, @@ -16,21 +20,26 @@ pub enum ArgType { } impl ArgType { - fn parse_input(&self, input: &str) -> Result { + fn arg_action(&self) -> ArgAction { match self { - Self::Int => match input.parse() { - Ok(u) => Ok(Match::Int(u)), - Err(e) => Err(e.to_string()), - }, - Self::Float => match input.parse() { - Ok(f) => Ok(Match::Float(f)), - Err(e) => Err(e.to_string()), - }, - Self::Size => match parse_size(input) { - Ok(u) => Ok(Match::Int(u)), - Err(e) => Err(e.to_string()), - }, - _ => Ok(Match::Str(input.to_owned())), + Self::Bool => ArgAction::SetTrue, + Self::Choices { multiple: true, .. } => ArgAction::Append, + _ => ArgAction::Set, + } + } + + fn value_parser(&self) -> ValueParser { + fn specialized_parse_size(s: &str) -> Result { + parse_size::parse_size(s) + } + + match self { + Self::Bool => ValueParser::bool(), + Self::Str => ValueParser::string(), + Self::Int => value_parser!(u64).into(), + Self::Size => ValueParser::new(specialized_parse_size), + Self::Float => value_parser!(f64).into(), + Self::Choices { choices, .. } => ValueParser::new(PossibleValuesParser::new(choices)), } } } @@ -75,55 +84,25 @@ pub type Matches<'a> = HashMap<&'a str, Match>; impl App { /// Constructs the clap App from this simplified specification. - fn to_clap_app(&self) -> clap::App<'_, '_> { - let mut app = clap::App::new(&self.name) - .bin_name(format!("dbdbgen {}", self.name)) - .version(&*self.version) - .about(&*self.about) - .settings(&[ - clap::AppSettings::NoBinaryName, - clap::AppSettings::UnifiedHelpMessage, - clap::AppSettings::NextLineHelp, - ]); - - for (name, arg) in &self.args { - let mut clap_arg = clap::Arg::with_name(name) - .long(if arg.long.is_empty() { name } else { &arg.long }) - .help(&arg.help); - if !arg.short.is_empty() { - clap_arg = clap_arg.short(&arg.short); - } - match &arg.r#type { - ArgType::Bool => {} - ArgType::Str => { - clap_arg = clap_arg.takes_value(true); - } - ArgType::Int | ArgType::Float | ArgType::Size => { - let t = arg.r#type.clone(); - clap_arg = clap_arg - .takes_value(true) - .validator(move |s| t.parse_input(&s).map(drop)); - } - ArgType::Choices { choices, multiple } => { - for choice in choices { - clap_arg = clap_arg.possible_value(choice); - } - if *multiple { - clap_arg = clap_arg.required(true).use_delimiter(true).multiple(true); - } - } - } - if arg.required { - clap_arg = clap_arg.required(true); - } - if let Some(default) = &arg.default { - clap_arg = clap_arg.default_value(default); - } + fn to_clap_app(&self) -> Command { + use clap::builder::{OsStr, Resettable}; - app = app.arg(clap_arg); - } - - app + Command::new(&self.name) + .bin_name(format!("dbdbgen {}", self.name)) + .version(&self.version) + .about(&self.about) + .no_binary_name(true) + .next_line_help(true) + .args(self.args.iter().map(|(name, arg)| { + clap::Arg::new(name) + .long(if arg.long.is_empty() { name } else { &arg.long }) + .help(&arg.help) + .short(arg.short.chars().next()) + .action(arg.r#type.arg_action()) + .value_parser(arg.r#type.value_parser()) + .required(arg.required) + .default_value(Resettable::from(arg.default.as_ref().map(OsStr::from))) + })) } /// Obtains the matches from the command line. @@ -136,18 +115,25 @@ impl App { let matches = clap_app.get_matches_from(args); let mut result = HashMap::with_capacity(self.args.len()); for (name, arg) in &self.args { - let value = match &arg.r#type { - ArgType::Bool => Match::Bool(matches.is_present(name)), - ArgType::Choices { multiple: true, .. } => { - if let Some(values) = matches.values_of(name) { - Match::Array(values.map(String::from).collect()) + macro_rules! get_one { + ($ty:ty) => { + if let Some(value) = matches.get_one(name) { + let value: &$ty = value; + value.clone() } else { continue; } - } - _ => { - if let Some(value) = matches.value_of(name) { - arg.r#type.parse_input(value).unwrap() + }; + } + + let value = match &arg.r#type { + ArgType::Bool => Match::Bool(matches.get_flag(name)), + ArgType::Str | ArgType::Choices { multiple: false, .. } => Match::Str(get_one!(String)), + ArgType::Int | ArgType::Size => Match::Int(get_one!(u64)), + ArgType::Float => Match::Float(get_one!(f64)), + ArgType::Choices { multiple: true, .. } => { + if let Some(values) = matches.get_many(name) { + Match::Array(values.map(|s: &&str| s.to_string()).collect()) } else { continue; } diff --git a/dbdbgen/src/jsvm.rs b/dbdbgen/src/jsvm.rs index 5eef6f5..c8d368a 100644 --- a/dbdbgen/src/jsvm.rs +++ b/dbdbgen/src/jsvm.rs @@ -83,7 +83,7 @@ impl<'p> Vm<'p> { let mut steps = Vec::new(); for (i, steps_js) in steps_js_stream.iter().enumerate() { - let step = deserialize(&steps_js, Purpose::Execution { step: i })?; + let step = deserialize(steps_js, Purpose::Execution { step: i })?; steps.push(step); } diff --git a/dbgen-playground/Cargo.toml b/dbgen-playground/Cargo.toml index 07f52ed..540800b 100644 --- a/dbgen-playground/Cargo.toml +++ b/dbgen-playground/Cargo.toml @@ -12,7 +12,8 @@ crate-type = ["cdylib"] [dependencies] dbgen = { path = "../", default-features = false } chrono = { version = "0.4", default-features = false } -wasm-bindgen = { version = "0.2", features = ["serde-serialize"] } +wasm-bindgen = "0.2" +serde-wasm-bindgen = "0.4" rand = { version = "0.8", default-features = false } rand_hc = "0.3" serde = { version = "1.0", features = ["derive"] } \ No newline at end of file diff --git a/dbgen-playground/src/lib.rs b/dbgen-playground/src/lib.rs index 5235297..736b0ca 100644 --- a/dbgen-playground/src/lib.rs +++ b/dbgen-playground/src/lib.rs @@ -124,7 +124,7 @@ fn try_generate_rows( pub fn generate_rows(template: &str, rows: usize, now: &str, seed: &[u8]) -> Result { let mut registry = Registry::default(); match try_generate_rows(template, rows, now, seed, &mut registry) { - Ok(result) => JsValue::from_serde(&result).map_err(|e| e.to_string().into()), + Ok(result) => serde_wasm_bindgen::to_value(&result).map_err(|e| e.to_string().into()), Err(e) => Err(registry.describe(&e).into()), } } diff --git a/deny.toml b/deny.toml index b7861d4..de27e73 100644 --- a/deny.toml +++ b/deny.toml @@ -12,11 +12,21 @@ name = 'zipf' version = '7.0' [[licenses.exceptions]] -allow = ['0BSD'] -name = 'enum-iterator' # dependency of vergen v5.1 -version = '0.7' +allow = ['Apache-2.0'] +name = 'ciborium' +version = '0.2' + +[[licenses.exceptions]] +allow = ['Apache-2.0'] +name = 'ciborium-io' +version = '0.2' + +[[licenses.exceptions]] +allow = ['Apache-2.0'] +name = 'ciborium-ll' +version = '0.2' [[licenses.exceptions]] -allow = ['0BSD'] -name = 'enum-iterator-derive' # dependency of vergen v5.1 -version = '0.7' +allow = ['Unicode-DFS-2016'] +name = 'unicode-ident' +version = '1.0.11' \ No newline at end of file diff --git a/rustfmt.toml b/rustfmt.toml index 47fb6a1..0af174f 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,2 +1,2 @@ -edition = "2018" +edition = "2021" max_width = 120 \ No newline at end of file diff --git a/src/bytes.rs b/src/bytes.rs index 8fffff1..9448b12 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -310,7 +310,7 @@ impl ByteString { let mut it = self.bytes[self.ascii_len..] .iter() .zip(self.ascii_len..) - .filter_map(|(b, i)| is_utf8_leading_byte(*b).then(|| i)) + .filter_map(|(b, i)| is_utf8_leading_byte(*b).then_some(i)) .fuse(); let start; diff --git a/src/cli.rs b/src/cli.rs index 9264b33..a68adb3 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -445,7 +445,7 @@ pub fn run(args: Args, span_registry: &mut Registry) -> Result<(), S> { _ => { return Err(Error::UnsupportedCliParameter { kind: "template", - value: "".to_owned(), + value: String::new(), } .no_span()) } @@ -518,7 +518,7 @@ pub fn run(args: Args, span_registry: &mut Registry) -> Result<(), S> { let meta_seed = args.seed.unwrap_or_else(|| OsRng.gen()); let show_progress = !args.quiet; if show_progress { - println!("Using seed: {}", meta_seed); + println!("Using seed: {meta_seed}"); } let mut seeding_rng = meta_seed.make_rng(); @@ -1006,9 +1006,9 @@ impl Env { } } for (unique_name, name) in schema_names { - let path = self.out_dir.join(format!("{}-schema-create.sql", unique_name)); + let path = self.out_dir.join(format!("{unique_name}-schema-create.sql")); let mut file = BufWriter::new(File::create(&path).with_path("create schema schema file", &path)?); - writeln!(file, "CREATE SCHEMA {};", name).with_path("write schema schema file", &path)?; + writeln!(file, "CREATE SCHEMA {name};").with_path("write schema schema file", &path)?; } Ok(()) } diff --git a/src/error.rs b/src/error.rs index 07ce358..6589f51 100644 --- a/src/error.rs +++ b/src/error.rs @@ -12,7 +12,7 @@ use thiserror::Error as ThisError; pub enum Error { /// Failed to parse template. #[error("failed to parse template")] - ParseTemplate(#[from] pest::error::Error), + ParseTemplate(#[source] Box>), /// Unknown SQL function. #[error("unknown function")] @@ -126,6 +126,9 @@ pub enum Error { }, } +// ensure the size of error is ≤56 bytes +const _: usize = 56 - std::mem::size_of::(); + impl fmt::Display for S { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.inner.fmt(f) @@ -144,6 +147,12 @@ impl From for Error { } } +impl From> for Error { + fn from(e: pest::error::Error) -> Self { + Self::ParseTemplate(Box::new(e)) + } +} + impl From for Error { fn from(e: regex_syntax::Error) -> Self { Self::InvalidRegex(e.into()) diff --git a/src/eval.rs b/src/eval.rs index a663e1a..49f4128 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -34,7 +34,7 @@ impl CompileContext { Self { zoneinfo: PathBuf::from("/usr/share/zoneinfo"), time_zone: ArcTz::new(Utc.into()), - current_timestamp: NaiveDateTime::from_timestamp(0, 0), + current_timestamp: NaiveDateTime::MIN, variables: vec![Value::Null; variables_count].into_boxed_slice(), } } @@ -391,7 +391,8 @@ impl Compiled { C::RandU31Timestamp(uniform) => { let seconds = state.rng.sample(uniform); - let timestamp = NaiveDateTime::from_timestamp(seconds, 0); + let timestamp = + NaiveDateTime::from_timestamp_opt(seconds, 0).expect("u31 range of timestamp must be valid"); Value::new_timestamp(timestamp, state.compile_context.time_zone.clone()) } diff --git a/src/format.rs b/src/format.rs index 4a0098c..d05374b 100644 --- a/src/format.rs +++ b/src/format.rs @@ -100,7 +100,7 @@ fn write_timestamp(writer: &mut dyn Write, quote: &str, timestamp: &DateTime Result<(), Error> { writer.write_all(quote.as_bytes())?; if interval == i64::min_value() { - return write!(writer, "-106751991 04:00:54.775808{}", quote); + return write!(writer, "-106751991 04:00:54.775808{quote}"); } else if interval < 0 { interval = -interval; writer.write_all(b"-")?; @@ -119,11 +119,11 @@ fn write_interval(writer: &mut dyn Write, quote: &str, mut interval: i64) -> Res let hours = hours % 24; if days > 0 { - write!(writer, "{} ", days)?; + write!(writer, "{days} ")?; } - write!(writer, "{:02}:{:02}:{:02}", hours, minutes, seconds)?; + write!(writer, "{hours:02}:{minutes:02}:{seconds:02}")?; if microseconds > 0 { - write!(writer, ".{:06}", microseconds)?; + write!(writer, ".{microseconds:06}")?; } writer.write_all(quote.as_bytes()) @@ -220,7 +220,7 @@ impl Options { if bytes.encoding() == Encoding::Binary { writer.write_all(b"X'")?; for b in bytes.as_bytes() { - write!(writer, "{:02X}", b)?; + write!(writer, "{b:02X}")?; } } else { writer.write_all(b"'")?; @@ -318,7 +318,7 @@ impl Format for SqlInsertSetFormat<'_> { } fn write_value_header(&self, writer: &mut dyn Write, column: &str) -> Result<(), Error> { - write!(writer, "{} = ", column) + write!(writer, "{column} = ") } fn write_value_separator(&self, writer: &mut dyn Write) -> Result<(), Error> { diff --git a/src/functions/rand.rs b/src/functions/rand.rs index 430fb09..f36444c 100644 --- a/src/functions/rand.rs +++ b/src/functions/rand.rs @@ -83,11 +83,7 @@ impl Function for Zipf { fn compile(&self, _: &CompileContext, span: Span, args: Arguments) -> Result> { let (count, exponent) = args_2(span, args, None, None)?; Ok(C::RandZipf(ZipfDistribution::new(count, exponent).map_err(|()| { - Error::InvalidArguments(format!( - "count ({}) and exponent ({}) must be positive", - count, exponent - )) - .span(span) + Error::InvalidArguments(format!("count ({count}) and exponent ({exponent}) must be positive")).span(span) })?)) } } @@ -103,7 +99,7 @@ impl Function for LogNormal { let (mean, std_dev) = args_2::(span, args, None, None)?; let std_dev = std_dev.abs(); Ok(C::RandLogNormal(rand_distr::LogNormal::new(mean, std_dev).map_err( - |e| Error::InvalidArguments(format!("standard deviation ({}) {}", std_dev, e)).span(span), + |e| Error::InvalidArguments(format!("standard deviation ({std_dev}) {e}")).span(span), )?)) } } @@ -118,7 +114,7 @@ impl Function for Bool { fn compile(&self, _: &CompileContext, span: Span, args: Arguments) -> Result> { let p = args_1(span, args, None)?; Ok(C::RandBool(rand_distr::Bernoulli::new(p).map_err(|e| { - Error::InvalidArguments(format!("probability ({}) {}", p, e)).span(span) + Error::InvalidArguments(format!("probability ({p}) {e}")).span(span) })?)) } } @@ -173,7 +169,7 @@ pub struct Regex; impl Function for Regex { fn compile(&self, _: &CompileContext, span: Span, args: Arguments) -> Result> { - let (regex, flags, max_repeat) = args_3::(span, args, None, Some("".to_owned()), Some(100))?; + let (regex, flags, max_repeat) = args_3::(span, args, None, Some(String::new()), Some(100))?; let generator = compile_regex_generator(®ex, &flags, max_repeat).span_err(span)?; Ok(C::RandRegex(generator)) } @@ -184,8 +180,8 @@ fn compile_regex_generator(regex: &str, flags: &str, max_repeat: u32) -> Result< for flag in flags.chars() { match flag { 'o' => parser.octal(true), - 'a' => parser.allow_invalid_utf8(true).unicode(false), - 'u' => parser.allow_invalid_utf8(false).unicode(true), + 'a' => parser.utf8(false).unicode(false), + 'u' => parser.utf8(true).unicode(true), 'x' => parser.ignore_whitespace(true), 'i' => parser.case_insensitive(true), 'm' => parser.multi_line(true), diff --git a/src/number.rs b/src/number.rs index 7c9d731..6283516 100644 --- a/src/number.rs +++ b/src/number.rs @@ -102,7 +102,7 @@ impl fmt::Display for Number { #[allow(clippy::should_implement_trait)] impl Number { pub(crate) fn from_finite_f64(v: f64) -> Self { - debug_assert!(v.is_finite(), "failed: ({:?}).is_finite()", v); + debug_assert!(v.is_finite(), "failed: ({v:?}).is_finite()"); Self(N::F(v)) } @@ -119,7 +119,7 @@ impl Number { match self.0 { N::B(true) => sink.write_str(true_string), N::B(false) => sink.write_str(false_string), - N::I(v) => write!(sink, "{}", v), + N::I(v) => write!(sink, "{v}"), N::F(v) => { let mut output = ryu::Buffer::new(); sink.write_str(output.format_finite(v)) @@ -132,7 +132,7 @@ impl Number { match self.0 { N::B(true) => sink.write_all(true_string.as_bytes()), N::B(false) => sink.write_all(false_string.as_bytes()), - N::I(v) => write!(sink, "{}", v), + N::I(v) => write!(sink, "{v}"), N::F(v) => { let mut output = ryu::Buffer::new(); sink.write_all(output.format_finite(v).as_bytes()) @@ -160,6 +160,7 @@ impl Number { } /// Negates itself. + #[must_use] pub fn neg(self) -> Self { if let Ok(a) = self.try_as_i128() { if let Some(c) = a.checked_neg() { diff --git a/src/parser.rs b/src/parser.rs index beacd02..402aa36 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -272,7 +272,7 @@ impl Template { let table = alloc.table_from_pairs(pair.into_inner())?; let table_name = table.name.unique_name(); if let Some(child_name) = &expected_child_name { - if child_name.inner.unique_name() != &*table_name { + if child_name.inner.unique_name() != table_name { return Err(Error::DerivedTableNameMismatch { for_each_row: child_name.inner.table_name(true).to_owned(), create_table: table.name.table_name(true).to_owned(), @@ -656,8 +656,7 @@ impl<'a> Allocator<'a> { function: &functions::array::Subscript, args: vec![ base.span(self.register(base_span)), - self.expr_from_pairs(pair.into_inner())? - .span(self.register(span.clone())), + self.expr_from_pairs(pair.into_inner())?.span(self.register(span)), ], }; base_span = span; @@ -669,7 +668,7 @@ impl<'a> Allocator<'a> { for (function, function_span) in op_stack.into_iter().rev() { base = Expr::Function { function, - args: vec![base.span(self.register(base_span.clone()))], + args: vec![base.span(self.register(base_span))], }; base_span = pest::Position::span(&function_span.start_pos(), &base_span.end_pos()); } @@ -772,9 +771,7 @@ impl<'a> Allocator<'a> { match pair.as_rule() { Rule::kw_interval => {} Rule::expr => { - expr = self - .expr_from_pairs(pair.into_inner())? - .span(self.register(span.clone())); + expr = self.expr_from_pairs(pair.into_inner())?.span(self.register(span)); } Rule::kw_week => unit = 604_800_000_000, Rule::kw_day => unit = 86_400_000_000, diff --git a/src/schemagen_cli.rs b/src/schemagen_cli.rs index e621e09..b7990a4 100644 --- a/src/schemagen_cli.rs +++ b/src/schemagen_cli.rs @@ -119,7 +119,7 @@ fn gen_int_column(dialect: Dialect, rng: &mut dyn RngCore) -> Column { (Dialect::PostgreSQL, true, _) => "numeric(20)", (Dialect::SQLite, _, _) => "integer", }; - let ty = format!("{} not null", ty); + let ty = format!("{ty} not null"); let (min, max) = if unsigned { (0, (256 << (8 * bytes)) - 1) } else { @@ -137,7 +137,7 @@ fn gen_int_column(dialect: Dialect, rng: &mut dyn RngCore) -> Column { Column { ty, - expr: format!("rand.range_inclusive({}, {})", min, max), + expr: format!("rand.range_inclusive({min}, {max})"), neg_log2_prob, average_len, nullable: false, @@ -166,10 +166,7 @@ fn gen_decimal_column(_: Dialect, rng: &mut dyn RngCore) -> Column { let limit = "9".repeat(before); Column { ty: format!("decimal({}, {}) not null", before + after, after), - expr: format!( - "rand.range_inclusive(-{0}, {0}) || rand.regex('\\.[0-9]{{{1}}}')", - limit, after - ), + expr: format!("rand.range_inclusive(-{limit}, {limit}) || rand.regex('\\.[0-9]{{{after}}}')"), neg_log2_prob: LOG2_10 * (before + after) as f64 + 1.0, average_len: (before + after) as f64 + 17.0 / 9.0, nullable: false, @@ -184,8 +181,8 @@ fn gen_varchar_column(_: Dialect, rng: &mut dyn RngCore) -> Column { let len = rng.gen_range(1..=255); let residue = (VALID_CHARS_COUNT / (VALID_CHARS_COUNT - 1.0)).log2(); Column { - ty: format!("varchar({}) not null", len), - expr: format!("rand.regex('.{{0,{}}}', 's')", len), + ty: format!("varchar({len}) not null"), + expr: format!("rand.regex('.{{0,{len}}}', 's')"), neg_log2_prob: f64::from(len + 1).log2() - residue, average_len: AVERAGE_LEN_PER_CHAR * 0.5 * f64::from(len) + 2.0, nullable: false, @@ -196,8 +193,8 @@ fn gen_char_column(_: Dialect, rng: &mut dyn RngCore) -> Column { let len = rng.gen_range(1..=255); let factor = VALID_CHARS_COUNT.log2(); Column { - ty: format!("char({}) not null", len), - expr: format!("rand.regex('.{{{}}}', 's')", len), + ty: format!("char({len}) not null"), + expr: format!("rand.regex('.{{{len}}}', 's')"), neg_log2_prob: factor * f64::from(len), average_len: AVERAGE_LEN_PER_CHAR * f64::from(len) + 2.0, nullable: false, @@ -240,7 +237,7 @@ fn gen_nullable_bool_column(_: Dialect, rng: &mut dyn RngCore) -> Column { let p = rng.gen::(); Column { ty: "boolean".to_owned(), - expr: format!("CASE WHEN rand.bool({}) THEN '' || rand.bool(0.5) END", p), + expr: format!("CASE WHEN rand.bool({p}) THEN '' || rand.bool(0.5) END"), neg_log2_prob: -((1.5 * p - 2.0) * p + 1.0).log2(), average_len: 4.0 - p, nullable: true, @@ -260,7 +257,7 @@ fn gen_float_column(dialect: Dialect, rng: &mut dyn RngCore) -> Column { }; Column { ty: ty.to_owned(), - expr: format!("rand.finite_f{}()", bits), + expr: format!("rand.finite_f{bits}()"), neg_log2_prob: if bits == 32 { NEG_LOG2_PROB_FINITE_F32 } else { @@ -327,11 +324,7 @@ impl<'a> IndexAppender<'a> { return; } - let index_spec = index_set - .iter() - .map(|i| format!("c{}", i)) - .collect::>() - .join(", "); + let index_spec = index_set.iter().map(|i| format!("c{i}")).collect::>().join(", "); if index_set.is_empty() || !self.index_sets.insert(index_set) { return; diff --git a/src/span.rs b/src/span.rs index e3044aa..510768c 100644 --- a/src/span.rs +++ b/src/span.rs @@ -23,7 +23,7 @@ impl Registry { pub fn register(&mut self, span: pest::Span<'_>) -> Span { let res = Span(self.0.len()); self.0.push(Error::new_from_span( - ErrorVariant::CustomError { message: "".to_owned() }, + ErrorVariant::CustomError { message: String::new() }, span, )); res @@ -35,12 +35,12 @@ impl Registry { let mut buf = format!("Error: {}\n", err.inner); if let Some(e) = self.0.get(err.span.0) { - writeln!(&mut buf, "{}\n", e).unwrap(); + writeln!(&mut buf, "{e}\n").unwrap(); } let mut err: &(dyn std::error::Error + 'static) = &err.inner; while let Some(source) = err.source() { - writeln!(&mut buf, "Cause: {}", source).unwrap(); + writeln!(&mut buf, "Cause: {source}").unwrap(); err = source; } diff --git a/src/value.rs b/src/value.rs index d1f63e5..775b6af 100644 --- a/src/value.rs +++ b/src/value.rs @@ -130,12 +130,9 @@ impl Value { (Self::Bytes(a), Self::Bytes(b)) => a.partial_cmp(b), (Self::Timestamp(a, _), Self::Timestamp(b, _)) => a.partial_cmp(b), (Self::Interval(a), Self::Interval(b)) => a.partial_cmp(b), - (Self::Array(a), Self::Array(b)) => try_partial_cmp_by(a.iter(), b.iter(), |a, b| a.sql_cmp(b))?, + (Self::Array(a), Self::Array(b)) => try_partial_cmp_by(a.iter(), b.iter(), Value::sql_cmp)?, _ => { - return Err(Error::InvalidArguments(format!( - "cannot compare {} with {}", - self, other - ))); + return Err(Error::InvalidArguments(format!("cannot compare {self} with {other}"))); } }) } @@ -171,7 +168,7 @@ impl Value { Self::Interval(try_or_overflow!(a.checked_add(*b), "{} + {}", a, b)) } _ => { - return Err(Error::InvalidArguments(format!("cannot add {} to {}", self, other))); + return Err(Error::InvalidArguments(format!("cannot add {self} to {other}"))); } }) } @@ -193,10 +190,7 @@ impl Value { Self::Interval(try_or_overflow!(a.checked_sub(*b), "{} + {}", a, b)) } _ => { - return Err(Error::InvalidArguments(format!( - "cannot subtract {} from {}", - self, other - ))); + return Err(Error::InvalidArguments(format!("cannot subtract {self} from {other}"))); } }) } @@ -209,10 +203,7 @@ impl Value { try_from_number_into_interval!(Number::from(*dur).mul(*m), "interval {} microsecond * {}", dur, m) } _ => { - return Err(Error::InvalidArguments(format!( - "cannot multiply {} with {}", - self, other - ))); + return Err(Error::InvalidArguments(format!("cannot multiply {self} with {other}"))); } }) } @@ -225,7 +216,7 @@ impl Value { try_from_number_into_interval!(Number::from(*dur).float_div(*d), "interval {} microsecond / {}", dur, d) } _ => { - return Err(Error::InvalidArguments(format!("cannot divide {} by {}", self, other))); + return Err(Error::InvalidArguments(format!("cannot divide {self} by {other}"))); } }) } @@ -235,7 +226,7 @@ impl Value { if let (Self::Number(lhs), Self::Number(rhs)) = (self, other) { Ok(try_from_number!(lhs.div(*rhs), "div({}, {})", lhs, rhs)) } else { - Err(Error::InvalidArguments(format!("cannot divide {} by {}", self, other))) + Err(Error::InvalidArguments(format!("cannot divide {self} by {other}"))) } } @@ -245,8 +236,7 @@ impl Value { Ok(try_from_number!(lhs.rem(*rhs), "mod({}, {})", lhs, rhs)) } else { Err(Error::InvalidArguments(format!( - "cannot compute remainder of {} by {}", - self, other + "cannot compute remainder of {self} by {other}" ))) } } @@ -264,7 +254,7 @@ impl Value { Self::Timestamp(timestamp, tz) => { write!(res, "{}", tz.from_utc_datetime(timestamp).format(TIMESTAMP_FORMAT)).unwrap(); } - Self::Interval(interval) => write!(res, "INTERVAL {} MICROSECOND", interval).unwrap(), + Self::Interval(interval) => write!(res, "INTERVAL {interval} MICROSECOND").unwrap(), Self::Array(_) => { return Err(Error::InvalidArguments( "cannot concatenate arrays using || operator".to_owned(), @@ -283,7 +273,7 @@ impl Value { match self { Self::Null => Ok(false), Self::Number(n) => Ok(n.sql_sign() != Ordering::Equal), - _ => Err(Error::InvalidArguments(format!("truth value of {} is undefined", self))), + _ => Err(Error::InvalidArguments(format!("truth value of {self} is undefined"))), } }