use crate::llm::human_formatter::HumanFormatConfig;
use crate::llm::pretty_printer::{PrettyPrinter, PrettyPrinterConfig};
use crate::llm::serializer_output::{SerializerOutput, SerializerOutputConfig};
use crate::llm::types::DxDocument;
use crate::llm::{ConvertError, document_to_llm, llm_to_document};
use std::path::PathBuf;
#[derive(Debug, Clone)]
pub struct SerializerBuilder {
indent_size: usize,
expand_keys: bool,
use_list_format: bool,
space_around_equals: bool,
validate_output: bool,
check_round_trip: bool,
output_dir: Option<PathBuf>,
generate_llm: bool,
generate_machine: bool,
preserve_comments: bool,
compact_arrays: bool,
}
impl Default for SerializerBuilder {
fn default() -> Self {
Self {
indent_size: 0,
expand_keys: true,
use_list_format: true,
space_around_equals: true,
validate_output: false, check_round_trip: false,
output_dir: None,
generate_llm: true,
generate_machine: true,
preserve_comments: false,
compact_arrays: false,
}
}
}
impl SerializerBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn indent_size(mut self, size: usize) -> Self {
self.indent_size = size;
self
}
pub fn expand_keys(mut self, expand: bool) -> Self {
self.expand_keys = expand;
self
}
pub fn use_list_format(mut self, use_list: bool) -> Self {
self.use_list_format = use_list;
self
}
pub fn space_around_equals(mut self, space: bool) -> Self {
self.space_around_equals = space;
self
}
pub fn validate_output(mut self, validate: bool) -> Self {
self.validate_output = validate;
self
}
pub fn check_round_trip(mut self, check: bool) -> Self {
self.check_round_trip = check;
self
}
pub fn output_dir<P: Into<PathBuf>>(mut self, dir: P) -> Self {
self.output_dir = Some(dir.into());
self
}
pub fn generate_llm(mut self, generate: bool) -> Self {
self.generate_llm = generate;
self
}
pub fn generate_machine(mut self, generate: bool) -> Self {
self.generate_machine = generate;
self
}
pub fn preserve_comments(mut self, preserve: bool) -> Self {
self.preserve_comments = preserve;
self
}
pub fn compact_arrays(mut self, compact: bool) -> Self {
self.compact_arrays = compact;
if compact {
self.use_list_format = false;
}
self
}
pub fn for_tables(mut self) -> Self {
self.indent_size = 20;
self.use_list_format = false;
self.expand_keys = true;
self.space_around_equals = true;
self
}
pub fn for_compact(mut self) -> Self {
self.indent_size = 0;
self.expand_keys = false;
self.use_list_format = false;
self.space_around_equals = false;
self.compact_arrays = true;
self
}
pub fn for_humans(mut self) -> Self {
self.indent_size = 0;
self.expand_keys = true;
self.use_list_format = true;
self.space_around_equals = true;
self.validate_output = false; self
}
pub fn build(self) -> Serializer {
let human_config = HumanFormatConfig {
key_padding: self.indent_size,
};
let pretty_config = PrettyPrinterConfig {
formatter_config: human_config,
validate_output: self.validate_output,
check_round_trip: self.check_round_trip,
};
let output_config = SerializerOutputConfig {
output_dir: self
.output_dir
.unwrap_or_else(|| PathBuf::from(".dx/serializer")),
generate_llm: self.generate_llm,
generate_machine: self.generate_machine,
compression: crate::llm::convert::CompressionAlgorithm::default(),
generate_metadata: false,
};
Serializer {
pretty_printer: PrettyPrinter::with_config(pretty_config),
output_generator: SerializerOutput::with_config(output_config),
}
}
}
pub struct Serializer {
pretty_printer: PrettyPrinter,
output_generator: SerializerOutput,
}
impl Serializer {
pub fn serialize(&self, doc: &DxDocument) -> String {
document_to_llm(doc)
}
pub fn deserialize(&self, input: &str) -> Result<DxDocument, ConvertError> {
llm_to_document(input)
}
pub fn format_human(
&self,
doc: &DxDocument,
) -> Result<String, crate::llm::pretty_printer::PrettyPrintError> {
self.pretty_printer.format(doc)
}
pub fn format_human_unchecked(&self, doc: &DxDocument) -> String {
self.pretty_printer.format_unchecked(doc)
}
pub fn generate_files(
&self,
doc: &DxDocument,
source_path: &std::path::Path,
) -> Result<
crate::llm::serializer_output::SerializerResult,
crate::llm::serializer_output::SerializerOutputError,
> {
self.output_generator.process_document(doc, source_path)
}
pub fn pretty_printer(&self) -> &PrettyPrinter {
&self.pretty_printer
}
pub fn output_generator(&self) -> &SerializerOutput {
&self.output_generator
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::llm::types::{DxLlmValue, DxSection};
#[test]
fn test_builder_default() {
let builder = SerializerBuilder::new();
assert_eq!(builder.indent_size, 0);
assert!(builder.expand_keys);
assert!(builder.use_list_format);
assert!(builder.space_around_equals);
assert!(!builder.validate_output); assert!(builder.generate_llm);
assert!(builder.generate_machine);
}
#[test]
fn test_builder_fluent_api() {
let builder = SerializerBuilder::new()
.indent_size(4)
.expand_keys(false)
.use_list_format(false)
.space_around_equals(false)
.validate_output(false) .generate_llm(false);
assert_eq!(builder.indent_size, 4);
assert!(!builder.expand_keys);
assert!(!builder.use_list_format);
assert!(!builder.space_around_equals);
assert!(!builder.validate_output);
assert!(!builder.generate_llm);
}
#[test]
fn test_builder_presets() {
let tables_builder = SerializerBuilder::new().for_tables();
assert_eq!(tables_builder.indent_size, 20);
assert!(!tables_builder.use_list_format);
assert!(tables_builder.expand_keys);
let compact_builder = SerializerBuilder::new().for_compact();
assert_eq!(compact_builder.indent_size, 0);
assert!(!compact_builder.expand_keys);
assert!(!compact_builder.use_list_format);
assert!(!compact_builder.space_around_equals);
let human_builder = SerializerBuilder::new().for_humans();
assert_eq!(human_builder.indent_size, 0);
assert!(human_builder.expand_keys);
assert!(human_builder.use_list_format);
assert!(human_builder.space_around_equals);
}
#[test]
fn test_serializer_basic_usage() {
let mut doc = DxDocument::new();
doc.context
.insert("name".to_string(), DxLlmValue::Str("TestApp".to_string()));
doc.context
.insert("version".to_string(), DxLlmValue::Str("1.0.0".to_string()));
let serializer = SerializerBuilder::new().build();
let text = serializer.serialize(&doc);
assert!(!text.is_empty());
let parsed = serializer.deserialize(&text);
assert!(parsed.is_ok());
let parsed_doc = parsed.unwrap();
assert_eq!(parsed_doc.context.len(), doc.context.len());
}
#[test]
fn test_serializer_human_format() {
let mut doc = DxDocument::new();
doc.context
.insert("name".to_string(), DxLlmValue::Str("TestApp".to_string()));
doc.context.insert(
"editor".to_string(),
DxLlmValue::Arr(vec![
DxLlmValue::Str("neovim".to_string()),
DxLlmValue::Str("vscode".to_string()),
]),
);
let serializer = SerializerBuilder::new().for_humans().build();
let human_text = serializer.format_human_unchecked(&doc);
assert!(!human_text.is_empty(), "Human format should produce output");
}
#[test]
fn test_serializer_compact_format() {
let mut doc = DxDocument::new();
doc.context
.insert("name".to_string(), DxLlmValue::Str("TestApp".to_string()));
doc.context.insert(
"editor".to_string(),
DxLlmValue::Arr(vec![
DxLlmValue::Str("neovim".to_string()),
DxLlmValue::Str("vscode".to_string()),
]),
);
let serializer = SerializerBuilder::new().for_compact().build();
let human_text = serializer.format_human_unchecked(&doc);
assert!(
!human_text.is_empty(),
"Compact format should produce output"
);
}
#[test]
fn test_serializer_with_sections() {
let mut doc = DxDocument::new();
let mut section = DxSection::new(vec!["id".to_string(), "name".to_string()]);
section.rows.push(vec![
DxLlmValue::Num(1.0),
DxLlmValue::Str("Alpha".to_string()),
]);
section.rows.push(vec![
DxLlmValue::Num(2.0),
DxLlmValue::Str("Beta".to_string()),
]);
doc.sections.insert('d', section);
let serializer = SerializerBuilder::new().for_tables().build();
let human_text = serializer.format_human_unchecked(&doc);
assert!(!human_text.is_empty());
}
#[test]
fn test_compact_arrays_option() {
let builder = SerializerBuilder::new().compact_arrays(true);
assert!(builder.compact_arrays);
assert!(!builder.use_list_format); }
#[test]
fn test_output_dir_option() {
let builder = SerializerBuilder::new().output_dir("custom/path");
assert_eq!(builder.output_dir, Some(PathBuf::from("custom/path")));
}
}