rsonpath_test_codegen/
lib.rs

1// Generic pedantic lints.
2#![warn(
3    explicit_outlives_requirements,
4    semicolon_in_expressions_from_macros,
5    unreachable_pub,
6    unused_import_braces,
7    unused_lifetimes
8)]
9// Clippy pedantic lints.
10#![warn(
11    clippy::allow_attributes_without_reason,
12    clippy::cast_lossless,
13    clippy::cloned_instead_of_copied,
14    clippy::empty_drop,
15    clippy::empty_line_after_outer_attr,
16    clippy::equatable_if_let,
17    clippy::expl_impl_clone_on_copy,
18    clippy::explicit_deref_methods,
19    clippy::explicit_into_iter_loop,
20    clippy::explicit_iter_loop,
21    clippy::fallible_impl_from,
22    clippy::flat_map_option,
23    clippy::if_then_some_else_none,
24    clippy::inconsistent_struct_constructor,
25    clippy::large_digit_groups,
26    clippy::let_underscore_must_use,
27    clippy::manual_ok_or,
28    clippy::map_err_ignore,
29    clippy::map_unwrap_or,
30    clippy::match_same_arms,
31    clippy::match_wildcard_for_single_variants,
32    clippy::mod_module_files,
33    clippy::must_use_candidate,
34    clippy::needless_continue,
35    clippy::needless_for_each,
36    clippy::needless_pass_by_value,
37    clippy::ptr_as_ptr,
38    clippy::redundant_closure_for_method_calls,
39    clippy::ref_binding_to_reference,
40    clippy::ref_option_ref,
41    clippy::rest_pat_in_fully_bound_structs,
42    clippy::undocumented_unsafe_blocks,
43    clippy::unneeded_field_pattern,
44    clippy::unseparated_literal_suffix,
45    clippy::unreadable_literal,
46    clippy::unused_self,
47    clippy::use_self
48)]
49
50use crate::files::Files;
51use proc_macro2::TokenStream;
52use quote::quote;
53use std::{
54    fmt::Display,
55    io,
56    path::{Path, PathBuf},
57    time::{Duration, Instant},
58};
59
60mod compression;
61mod files;
62mod gen;
63mod model;
64
65/// Parsed TOML document declaration annotated with its name and path.
66#[derive(Clone)]
67pub(crate) struct DiscoveredDocument {
68    /// Name of the file.
69    pub(crate) name: String,
70    /// Path relative to the source TOML directory.
71    pub(crate) relative_path: PathBuf,
72    /// Parsed TOML document.
73    pub(crate) document: model::Document,
74}
75
76/// Generate the source of end-to-end tests based on the TOML configuration in `toml_directory_path`.
77/// As a side-effect, JSON files are written to `output_json_directory_path`, and additional variants
78/// with compressed inputs of TOML configs are generated.
79pub fn generate_tests<P1, P2>(toml_directory_path: P1, output_json_directory_path: P2) -> Result<TokenStream, io::Error>
80where
81    P1: AsRef<Path>,
82    P2: AsRef<Path>,
83{
84    println!("discovery...");
85
86    let discovery_start = Instant::now();
87    let mut files = Files::new(output_json_directory_path, toml_directory_path)?;
88
89    println!("generating compressed variants...");
90
91    compression::generate_compressed_documents(&mut files)?;
92
93    let stats = files.stats();
94    let discovery_elapsed = FormatDuration(discovery_start.elapsed());
95
96    println!(
97        "prepared {} documents with a total of {} queries; finished in {}",
98        stats.number_of_documents(),
99        stats.number_of_queries(),
100        discovery_elapsed
101    );
102
103    println!("generating tests...");
104
105    let imports = gen::generate_imports();
106    let sources = gen::generate_test_fns(&mut files).into_iter();
107
108    println!("writing files...");
109    files.flush()?;
110
111    Ok(quote! {
112        #imports
113
114        #(#sources)*
115    })
116}
117
118/// Wrapper implementing [`Display`] for [`Duration`] which shows the duration in seconds.
119struct FormatDuration(Duration);
120
121impl Display for FormatDuration {
122    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
123        write!(f, "{:.2}s", self.0.as_secs_f32())
124    }
125}