query-forge 0.5.0

Run SQL queries on XLSX/XML/CSV/JSON/JSONL/Markdown/HTML/Parquet inputs and export results as text, CSV, JSONL, Markdown, XML, HTML, XLSX, or Parquet
Documentation
use std::path::Path;

#[derive(Debug, Clone, PartialEq)]
pub enum QueryValue {
    Null,
    Integer(i64),
    Real(f64),
    Text(String),
}

#[derive(Debug, Clone, PartialEq)]
pub struct QueryResult {
    pub columns: Vec<String>,
    pub rows: Vec<Vec<QueryValue>>,
}

#[derive(Debug, Clone, PartialEq)]
pub struct QueryParam {
    pub name: String,
    pub value: QueryValue,
}

#[derive(Debug, Clone, Copy)]
pub struct WorkbookInput<'a> {
    pub path: &'a Path,
    pub sheet_name: Option<&'a str>,
    pub table_name: Option<&'a str>,
}

#[derive(Debug, Clone)]
pub struct TypeInferenceOptions {
    pub infer_types: bool,
    pub decimal_comma: bool,
    pub date_format: Option<String>,
    pub null_values: Vec<String>,
    pub true_values: Vec<String>,
    pub false_values: Vec<String>,
}

impl Default for TypeInferenceOptions {
    fn default() -> Self {
        Self {
            infer_types: true,
            decimal_comma: false,
            date_format: None,
            null_values: vec![String::new()],
            true_values: vec!["true".to_owned()],
            false_values: vec!["false".to_owned()],
        }
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum HeaderCase {
    Snake,
}

#[derive(Debug, Clone, Default)]
pub struct InputNormalizationOptions {
    pub trim: bool,
    pub skip_empty_rows: bool,
    pub normalize_headers: bool,
    pub header_case: Option<HeaderCase>,
    pub dedupe_headers: bool,
}

/// Controls how JSON input files are parsed into rows.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum JsonMode {
    /// Each element of a top-level JSON array becomes a row (default).
    ///
    /// If the root is an object the object itself becomes a single row.
    #[default]
    Array,
    /// Each key-value pair of a JSON object becomes a row with "key" and "value" columns.
    ///
    /// Useful for flat configuration or metadata objects.
    Object,
    /// Recursively flatten nested JSON objects and arrays into a single row per top-level
    /// element, using dotted key paths (e.g. `address.city`) for nested fields.
    Flatten,
}

/// Controls how XML input files are parsed into rows.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum XmlMode {
    /// Detect and extract tabular rows from the XML structure (default).
    ///
    /// Looks first for direct `<row>` children, then for any element whose direct
    /// children are all leaf elements (no deeper nesting).
    #[default]
    Rows,
    /// Collect every leaf text element in the document as a separate row.
    ///
    /// Each row has two columns: `tag` (element name) and `value` (text content).
    Descendants,
    /// Extract XML element attributes as columns.
    ///
    /// Every element that carries at least one XML attribute becomes a row; attribute
    /// names map to column names. Text content of the element is also included as a
    /// `value` column when present.
    Attributes,
}

/// Options that control how JSON and XML inputs are extracted into rows.
#[derive(Debug, Clone, Default)]
pub struct ExtractionOptions {
    pub json_mode: JsonMode,
    pub xml_mode: XmlMode,
}

/// The SQL-compatible type inferred for a column.
#[derive(Debug, Clone, PartialEq)]
pub enum InferredType {
    Integer,
    Real,
    Text,
    /// Column contains a mix of numeric and text values.
    Mixed,
}

impl InferredType {
    pub fn as_str(&self) -> &'static str {
        match self {
            InferredType::Integer => "INTEGER",
            InferredType::Real => "REAL",
            InferredType::Text => "TEXT",
            InferredType::Mixed => "MIXED",
        }
    }
}

/// Per-column information returned by inspection commands.
#[derive(Debug, Clone)]
pub struct ColumnInfo {
    pub table_name: String,
    pub column_name: String,
    pub inferred_type: InferredType,
    pub nullable: bool,
}

/// Optional per-column statistics (enabled by `--stats`).
#[derive(Debug, Clone)]
pub struct ColumnStats {
    pub distinct_count: usize,
    pub min_value: Option<String>,
    pub max_value: Option<String>,
}

/// Summary of a single loaded table, returned by `load_table_summaries`.
#[derive(Debug, Clone)]
pub struct TableSummary {
    /// The logical SQL table name (explicit or auto-generated).
    pub table_name: String,
    /// The original file path as provided on the command line.
    pub source_path: String,
    /// The sheet/tag/key/index selector, if any.
    pub source_selector: Option<String>,
    pub row_count: usize,
    pub columns: Vec<ColumnInfo>,
    /// Up to `sample` rows of actual data (column values in column order).
    pub sample_rows: Vec<Vec<QueryValue>>,
    /// Non-fatal warnings (e.g. mixed-type columns, duplicate header normalisations).
    pub warnings: Vec<String>,
    /// Per-column statistics; `None` when `--stats` is not requested.
    pub column_stats: Option<Vec<ColumnStats>>,
}