use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SchemaInferenceConfig {
pub ignore_outer_array: bool,
pub delimiter: Option<u8>,
pub schema_uri: Option<String>,
pub map_threshold: usize,
pub map_max_required_keys: Option<usize>,
pub unify_maps: bool,
pub no_unify: std::collections::HashSet<String>,
pub force_field_types: HashMap<String, String>,
pub wrap_scalars: bool,
pub wrap_root: Option<String>,
pub no_root_map: bool,
#[cfg(feature = "avro")]
pub avro: bool,
pub debug: bool,
pub verbosity: DebugVerbosity,
}
#[derive(Default, Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
pub enum DebugVerbosity {
#[default]
Normal,
Verbose,
}
impl SchemaInferenceConfig {
pub(crate) fn debug(&self, args: std::fmt::Arguments) {
if self.debug {
let message = format!("{}", args);
eprintln!("{}", self.maybe_truncate(message));
}
}
pub(crate) fn debug_verbose(&self, args: std::fmt::Arguments) {
if self.debug && matches!(self.verbosity, DebugVerbosity::Verbose) {
let message = format!("{}", args);
eprintln!("{}", self.maybe_truncate(message));
}
}
fn maybe_truncate(&self, message: String) -> String {
let lines: Vec<&str> = message.lines().collect();
if lines.len() > 20 && self.verbosity == DebugVerbosity::Normal {
let mut truncated = String::new();
for line in lines.iter().take(10) {
truncated.push_str(line);
truncated.push('\n');
}
truncated.push_str(&format!("... ({} lines truncated) ...\n", lines.len() - 15));
for line in lines.iter().skip(lines.len() - 5) {
truncated.push_str(line);
truncated.push('\n');
}
truncated
} else {
message
}
}
}
impl Default for SchemaInferenceConfig {
fn default() -> Self {
Self {
ignore_outer_array: true,
delimiter: None,
schema_uri: Some("AUTO".to_string()),
map_threshold: 20,
map_max_required_keys: None,
unify_maps: false,
no_unify: std::collections::HashSet::new(),
force_field_types: std::collections::HashMap::new(),
wrap_scalars: true,
wrap_root: None,
no_root_map: true,
#[cfg(feature = "avro")]
avro: false,
debug: false,
verbosity: DebugVerbosity::default(),
}
}
}
#[macro_export]
macro_rules! debug {
($cfg:expr, $($arg:tt)*) => {
$cfg.debug(format_args!($($arg)*))
};
}
#[macro_export]
macro_rules! debug_verbose {
($cfg:expr, $($arg:tt)*) => {
$cfg.debug_verbose(format_args!($($arg)*))
};
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SchemaInferenceResult {
pub schema: Value,
pub processed_count: usize,
}
#[cfg(feature = "avro")]
impl SchemaInferenceResult {
pub fn to_avro_schema(
&self,
namespace: &str,
utility_namespace: Option<&str>,
base_uri: Option<&str>,
split_top_level: bool,
) -> Value {
avrotize::converter::jsons_to_avro(
&self.schema,
namespace,
utility_namespace.unwrap_or(""),
base_uri.unwrap_or("genson-core"),
split_top_level,
)
}
}
pub fn make_promoted_scalar_key(field_prefix: &str, scalar_type: &str) -> String {
format!("{}__{}", field_prefix, scalar_type)
}