use koicore::writer::{NumberFormat, ParamFormatSelector};
use koicore::{
Command, FormatterOptions, Parameter, Writer, WriterConfig,
parser::{Parser, ParserConfig, StringInputSource},
};
use std::collections::HashMap;
#[test]
fn test_writer_parser_compatibility() {
let test_cases = vec![
Command::new(
"test1",
vec![
Parameter::from("string"),
Parameter::from(42),
Parameter::from(3.14),
],
),
Command::new(
"test2",
vec![Parameter::from("no_quotes"), Parameter::from(0xff)],
),
Command::new(
"test3",
vec![Parameter::from("with spaces"), Parameter::from(1000)],
),
];
let config = WriterConfig::default();
for command in test_cases {
let mut output = Vec::new();
let mut writer = Writer::new(&mut output, config.clone());
writer
.write_command(&command)
.expect("Failed to write command");
let generated = String::from_utf8(output).unwrap();
let input = StringInputSource::new(generated.as_str());
let parser_config = ParserConfig::default();
let mut parser = Parser::new(input, parser_config);
let parsed = parser.next_command();
assert!(
parsed.is_ok(),
"Failed to parse generated command: {}",
generated
);
let parsed_command = parsed.unwrap();
assert!(
parsed_command.is_some(),
"No command parsed from: {}",
generated
);
let parsed_command = parsed_command.unwrap();
assert_eq!(parsed_command, command);
}
}
#[test]
fn test_parser_writer_compatibility() {
let test_cases: Vec<(&str, WriterConfig)> = vec![
(
"#simple param1 param2",
WriterConfig::default(),
),
(
"#cmd \"Hello World\"",
WriterConfig::default(),
),
(
"#cmd 123",
WriterConfig::default(),
),
(
"#cmd key(value)",
WriterConfig::default(),
),
(
"#cmd pos(x: 10, y: 20)",
WriterConfig::default(),
),
(
"#hex_cmd 0xff",
WriterConfig {
command_options: vec![(
"hex_cmd".to_string(),
FormatterOptions {
number_format: NumberFormat::Hex,
..Default::default()
},
)]
.into_iter()
.collect(),
..Default::default()
},
),
(
"#hex_cmd 0x2a 0x10",
WriterConfig {
command_options: vec![(
"hex_cmd".to_string(),
FormatterOptions {
number_format: NumberFormat::Hex,
..Default::default()
},
)]
.into_iter()
.collect(),
..Default::default()
},
),
(
"#bin_cmd 0b1010",
WriterConfig {
command_options: vec![(
"bin_cmd".to_string(),
FormatterOptions {
number_format: NumberFormat::Binary,
..Default::default()
},
)]
.into_iter()
.collect(),
..Default::default()
},
),
(
"#oct_cmd 0o77",
WriterConfig {
command_options: vec![(
"oct_cmd".to_string(),
FormatterOptions {
number_format: NumberFormat::Octal,
..Default::default()
},
)]
.into_iter()
.collect(),
..Default::default()
},
),
(
"#quoted_cmd \"var1\" \"var2\"",
WriterConfig {
command_options: vec![(
"quoted_cmd".to_string(),
FormatterOptions {
force_quotes_for_vars: true,
..Default::default()
},
)]
.into_iter()
.collect(),
..Default::default()
},
),
(
"#cmd true false",
WriterConfig::default(),
),
(
"#cmd 3.14",
WriterConfig::default(),
),
(
"Just plain text",
WriterConfig::default(),
),
(
"## This is an annotation",
WriterConfig::default(),
),
(
"#123 extra_param",
WriterConfig::default(),
),
(
"#cmd a b c d e",
WriterConfig::default(),
),
(
"#cmd",
WriterConfig::default(),
),
(
"#cmd 42 string_var true false 3.14",
WriterConfig::default(),
),
(
"#cmd color(255, 255, 255)",
WriterConfig::default(),
),
(
"#cmd with_underscore and123number",
WriterConfig::default(),
),
(
"#cmd \"with space\" \"and tab\"",
WriterConfig::default(),
),
(
"#cmd \"escaped\\\\backslash\" \"newline\\nhere\"",
WriterConfig::default(),
),
(
"#cmd -42 -3.14",
WriterConfig::default(),
),
];
let parser_config = ParserConfig::default();
for (original, config) in test_cases {
let input = StringInputSource::new(original);
let mut parser = Parser::new(input, parser_config.clone());
let parsed = parser.next_command();
assert!(
parsed.is_ok(),
"Failed to parse: {}",
original
);
let parsed_command = parsed.unwrap();
assert!(
parsed_command.is_some(),
"No command parsed from: {}",
original
);
let parsed_command = parsed_command.unwrap();
let mut output = Vec::new();
let mut writer = Writer::new(&mut output, config);
writer
.write_command(&parsed_command)
.expect("Failed to write command");
let generated = String::from_utf8(output).unwrap();
assert_eq!(
generated.trim_end(),
original,
"Generated text does not match original:\n original: {:?}\n generated: {:?}",
original,
generated.trim_end()
);
}
}
#[test]
fn test_writer_parser_param_specific() {
let command = Command::new(
"param_test",
vec![
Parameter::from(42),
Parameter::from(255),
Parameter::from(100),
],
);
let config = WriterConfig::default();
let mut output = Vec::new();
let mut writer = Writer::new(&mut output, config.clone());
let mut param_options = HashMap::new();
let hex_options = FormatterOptions {
number_format: NumberFormat::Hex,
..Default::default()
};
param_options.insert(ParamFormatSelector::Position(0), &hex_options);
let bin_options = FormatterOptions {
number_format: NumberFormat::Binary,
..Default::default()
};
param_options.insert(ParamFormatSelector::Position(1), &bin_options);
let oct_options = FormatterOptions {
number_format: NumberFormat::Octal,
..Default::default()
};
param_options.insert(ParamFormatSelector::Position(2), &oct_options);
writer
.write_command_with_options(&command, None, Some(¶m_options))
.expect("Failed to write with parameter-specific options");
let generated = String::from_utf8(output).unwrap();
let input = StringInputSource::new(generated.as_str());
let parser_config = ParserConfig::default();
let mut parser = Parser::new(input, parser_config);
let parsed = parser.next_command();
assert!(
parsed.is_ok(),
"Failed to parse generated command: {}",
generated
);
let parsed_command = parsed.unwrap();
assert!(
parsed_command.is_some(),
"No command parsed from: {}",
generated
);
let parsed_command = parsed_command.unwrap();
assert_eq!(parsed_command.name(), command.name());
assert_eq!(parsed_command.params.len(), command.params.len());
}
#[test]
fn test_writer_parser_newlines() {
let command = Command::new(
"newline_test",
vec![
Parameter::from("param1"),
Parameter::from("param2"),
Parameter::from("param3"),
],
);
let config = WriterConfig::default();
let mut output = Vec::new();
let mut writer = Writer::new(&mut output, config.clone());
let mut param_options = HashMap::new();
let newline_options = FormatterOptions {
newline_after_param: true,
..Default::default()
};
param_options.insert(ParamFormatSelector::Position(0), &newline_options);
param_options.insert(ParamFormatSelector::Position(1), &newline_options);
param_options.insert(ParamFormatSelector::Position(2), &newline_options);
writer
.write_command_with_options(&command, None, Some(¶m_options))
.expect("Failed to write with newlines");
let generated = String::from_utf8(output).unwrap();
let input = StringInputSource::new(generated.as_str());
let parser_config = ParserConfig::default();
let mut parser = Parser::new(input, parser_config);
let parsed = parser.next_command();
assert!(
parsed.is_ok(),
"Failed to parse generated command with newlines: {}",
generated
);
let parsed_command = parsed.unwrap();
assert!(
parsed_command.is_some(),
"No command parsed from: {}",
generated
);
let parsed_command = parsed_command.unwrap();
assert_eq!(parsed_command.name(), command.name());
}
#[test]
fn test_writer_parser_compact() {
let command = Command::new(
"compact_test",
vec![
Parameter::from("string"),
Parameter::from(42),
Parameter::from(3.14),
],
);
let config = WriterConfig::default();
let mut output = Vec::new();
let mut writer = Writer::new(&mut output, config.clone());
let mut compact_options = FormatterOptions::default();
compact_options.compact = true;
writer
.write_command_with_options(&command, Some(&compact_options), None)
.expect("Failed to write compact");
let generated = String::from_utf8(output).unwrap();
let input = StringInputSource::new(generated.as_str());
let parser_config = ParserConfig::default();
let mut parser = Parser::new(input, parser_config);
let parsed = parser.next_command();
assert!(
parsed.is_ok(),
"Failed to parse generated compact command: {}",
generated
);
let parsed_command = parsed.unwrap();
assert!(
parsed_command.is_some(),
"No command parsed from compact output: {}",
generated
);
let parsed_command = parsed_command.unwrap();
assert_eq!(parsed_command.name(), command.name());
assert_eq!(parsed_command.params.len(), command.params.len());
}
#[test]
fn test_writer_parser_force_quotes() {
let command = Command::new(
"quote_test",
vec![
Parameter::from("valid_name"),
Parameter::from("valid123"),
Parameter::from("invalid-name"),
],
);
let config = WriterConfig::default();
let mut output = Vec::new();
let mut writer = Writer::new(&mut output, config.clone());
let mut force_quote_options = FormatterOptions::default();
force_quote_options.force_quotes_for_vars = true;
writer
.write_command_with_options(&command, Some(&force_quote_options), None)
.expect("Failed to write with force quotes");
let generated = String::from_utf8(output).unwrap();
let input = StringInputSource::new(generated.as_str());
let parser_config = ParserConfig::default();
let mut parser = Parser::new(input, parser_config);
let parsed = parser.next_command();
assert!(
parsed.is_ok(),
"Failed to parse generated command with force quotes: {}",
generated
);
let parsed_command = parsed.unwrap();
assert!(
parsed_command.is_some(),
"No command parsed from: {}",
generated
);
let parsed_command = parsed_command.unwrap();
assert_eq!(parsed_command.name(), command.name());
assert_eq!(parsed_command.params.len(), command.params.len());
}
#[test]
fn test_write_duplicate_keys() {
let mut entries = Vec::new();
entries.push(("k".to_string(), koicore::Value::Int(1)));
entries.push(("k".to_string(), koicore::Value::Int(2)));
let param = Parameter::Composite(
"p".to_string(),
koicore::command::CompositeValue::Dict(entries),
);
let command = Command::new("dup_test", vec![param]);
let config = WriterConfig::default();
let mut output = Vec::new();
let mut writer = Writer::new(&mut output, config);
writer
.write_command(&command)
.expect("Failed to write duplicate keys");
let generated = String::from_utf8(output).unwrap();
println!("{}", generated);
assert!(generated.contains("p(k: 1, k: 2)"));
}
#[test]
fn test_write_special_strings() {
let special = vec![
"normal",
"with space",
"with \"quote\"",
"with \n newline",
"", ];
let mut params = Vec::new();
for s in &special {
params.push(Parameter::from(*s));
}
let command = Command::new("str_test", params);
let config = WriterConfig::default();
let mut output = Vec::new();
let mut writer = Writer::new(&mut output, config);
writer
.write_command(&command)
.expect("Failed to write special strings");
let generated = String::from_utf8(output).unwrap();
println!("{}", generated);
let input = StringInputSource::new(generated.as_str());
let parser_config = ParserConfig::default();
let mut parser = Parser::new(input, parser_config);
let parsed = parser
.next_command()
.expect("Failed to parse back")
.unwrap();
assert_eq!(parsed.params.len(), special.len());
if let Parameter::Basic(koicore::Value::String(s)) = &parsed.params[2] {
assert_eq!(s, "with \"quote\"");
} else {
panic!("Wrong type for quote test");
}
if let Parameter::Basic(koicore::Value::String(s)) = &parsed.params[3] {
assert_eq!(s, "with \n newline");
} else {
panic!("Wrong type for newline test");
}
if let Parameter::Basic(koicore::Value::String(s)) = &parsed.params[4] {
assert_eq!(s, "");
} else {
panic!("Wrong type for empty string");
}
}