use ngdp_bpsv::{BpsvDocument, BpsvValue, Error};
fn main() -> Result<(), Error> {
println!("=== Typed Access Example ===\n");
let bpsv_data = r#"Region!STRING:0|BuildConfig!HEX:0|BuildId!DEC:4|Active!STRING:0
## seqn = 12345
us|be2bb98dc28aee05bbee519393696cdb|61491|true
eu|1234567890abcdef1234567890abcdef|61492|false
kr||61493|true
cn|deadbeefcafebabedeadbeefcafebabe|61494|"#;
println!("1. Parsing BPSV document with mixed types...");
let mut document = BpsvDocument::parse(bpsv_data)?;
println!(
" ✅ Parsed {} rows with {} fields",
document.row_count(),
document.schema().field_count()
);
println!("\n2. Accessing typed values...");
let schema = document.schema().clone();
for (i, row) in document.rows_mut().iter_mut().enumerate() {
println!(" Row {}:", i + 1);
let typed_values = row.get_typed_values(&schema)?;
for (field, value) in schema.fields().iter().zip(typed_values.iter()) {
println!(
" {} ({}) = {} [{}]",
field.name,
field.field_type,
value.to_bpsv_string(),
value.value_type()
);
}
if let Ok(Some(region_value)) = row.get_typed_by_name("Region", &schema) {
if let Some(region_str) = region_value.as_string() {
println!(" → Region as string: '{region_str}'");
}
}
if let Ok(Some(build_id_value)) = row.get_typed_by_name("BuildId", &schema) {
if let Some(build_id) = build_id_value.as_decimal() {
println!(" → Build ID as number: {build_id}");
}
}
if let Ok(Some(hex_value)) = row.get_typed_by_name("BuildConfig", &schema) {
match hex_value {
BpsvValue::Hex(hex_str) => {
println!(
" → BuildConfig as hex: '{}' ({} chars)",
hex_str,
hex_str.len()
);
}
BpsvValue::Empty => {
println!(" → BuildConfig: <empty>");
}
_ => println!(" → BuildConfig: unexpected type"),
}
}
println!();
}
println!("3. Converting rows to typed maps...");
for (i, row) in document.rows_mut().iter_mut().enumerate() {
let typed_map = row.to_typed_map(&schema)?;
println!(" Row {} as typed map:", i + 1);
for (field_name, value) in &typed_map {
println!(" {field_name}: {value:?}");
}
if let Some(BpsvValue::String(region)) = typed_map.get("Region") {
println!(" → Type-safe region access: '{region}'");
}
if let Some(BpsvValue::Decimal(build_id)) = typed_map.get("BuildId") {
println!(" → Type-safe build ID access: {build_id}");
}
println!();
}
println!("4. Demonstrating value conversions...");
let string_value = BpsvValue::String("hello".to_string());
let decimal_value = BpsvValue::Decimal(12345);
let hex_value = BpsvValue::Hex("deadbeef".to_string());
let empty_value = BpsvValue::Empty;
println!(" String value: {string_value}");
println!(" Decimal value: {decimal_value}");
println!(" Hex value: {hex_value}");
println!(" Empty value: '{empty_value}'");
println!("\n Conversion attempts:");
match String::try_from(string_value.clone()) {
Ok(s) => println!(" String → String: '{s}'"),
Err(e) => println!(" String → String failed: {e}"),
}
match i64::try_from(decimal_value.clone()) {
Ok(n) => println!(" Decimal → i64: {n}"),
Err(e) => println!(" Decimal → i64 failed: {e}"),
}
match i64::try_from(string_value.clone()) {
Ok(n) => println!(" String → i64: {n} (unexpected!)"),
Err(e) => println!(" String → i64 correctly failed: {e}"),
}
println!("\n5. Demonstrating field type validation...");
use ngdp_bpsv::{BpsvBuilder, BpsvFieldType};
let mut builder = BpsvBuilder::new();
builder
.add_field("StringField", BpsvFieldType::String(5))? .add_field("HexField", BpsvFieldType::Hex(8))? .add_field("DecField", BpsvFieldType::Decimal(4))?;
println!(" Testing valid values:");
match builder.add_raw_row(&[
"hello".to_string(),
"deadbeef".to_string(),
"1234".to_string(),
]) {
Ok(_) => println!(" ✅ Valid row accepted"),
Err(e) => println!(" ❌ Error: {e}"),
}
println!(" Testing string length validation:");
let mut builder2 = BpsvBuilder::new();
builder2.add_field("ShortString", BpsvFieldType::String(3))?;
match builder2.add_raw_row(&["ok".to_string()]) {
Ok(_) => println!(" ✅ Short string accepted"),
Err(e) => println!(" ❌ Error: {e}"),
}
match builder2.add_raw_row(&["toolong".to_string()]) {
Ok(_) => println!(" ❌ Long string should have been rejected!"),
Err(e) => println!(" ✅ Long string correctly rejected: {e}"),
}
println!(" Testing hex validation:");
let mut builder3 = BpsvBuilder::new();
builder3.add_field("HexField", BpsvFieldType::Hex(4))?;
match builder3.add_raw_row(&["abcd".to_string()]) {
Ok(_) => println!(" ✅ Valid hex accepted"),
Err(e) => println!(" ❌ Error: {e}"),
}
match builder3.add_raw_row(&["xyz".to_string()]) {
Ok(_) => println!(" ❌ Invalid hex should have been rejected!"),
Err(e) => println!(" ✅ Invalid hex correctly rejected: {e}"),
}
println!("\n6. Handling empty values...");
let empty_data = r#"Field1!STRING:0|Field2!DEC:4|Field3!HEX:8
row1||deadbeef
|123|
value|456|abcd1234"#;
let mut empty_doc = BpsvDocument::parse(empty_data)?;
println!(" Parsed document with empty values:");
let empty_schema = empty_doc.schema().clone();
for (i, row) in empty_doc.rows_mut().iter_mut().enumerate() {
let typed_values = row.get_typed_values(&empty_schema)?;
println!(" Row {}: {:?}", i + 1, typed_values);
for (j, value) in typed_values.iter().enumerate() {
if value.is_empty() {
let field_name = &empty_schema.fields()[j].name;
println!(" Field '{field_name}' is empty");
}
}
}
println!("\n✅ All typed access examples completed successfully!");
Ok(())
}