ACH (Automated Clearing House) file parser for Rust following the NACHA (National Automated Clearing House Association) specifications.
- Parse ACH file headers and batch records
- Support for PPD, CCD, and other standard entry class codes
- Entry detail records with addenda support
- Type-safe parsing with comprehensive error handling
- Zero-copy parsing for maximum performance
Add this to your Cargo.toml:
[dependencies]
rs-ach = "0.1.2"use rs_ach::AchFile;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Read ACH file content
let ach_content = std::fs::read_to_string("sample.ach")?;
// Parse the ACH file
let ach_file = AchFile::parse(&ach_content)?;
// Access file header information
println!("File Header:");
println!(" Destination: {}", ach_file.file_header.immediate_destination.trim());
println!(" Origin: {}", ach_file.file_header.immediate_origin.trim());
println!(" Creation Date: {}", ach_file.file_header.file_creation_date);
// Iterate through batches
for (i, batch) in ach_file.batches.iter().enumerate() {
println!("\nBatch {}:", i + 1);
println!(" Company: {}", batch.header.company_name.trim());
println!(" SEC Code: {}", batch.header.standard_entry_class_code);
println!(" Description: {}", batch.header.company_entry_description.trim());
// Iterate through entries in the batch
for (j, entry) in batch.entries.iter().enumerate() {
println!(" Entry {}:", j + 1);
println!(" Transaction Code: {}", entry.transaction_code);
println!(" Routing Number: {}{}",
entry.receiving_dfi_identification,
entry.check_digit
);
println!(" Account: {}", entry.dfi_account_number.trim());
println!(" Amount: ${:.2}", entry.amount as f64 / 100.0);
println!(" Name: {}", entry.individual_name.trim());
// Check for addenda records
if !entry.addenda.is_empty() {
println!(" Addenda:");
for addenda in &entry.addenda {
println!(" {}", addenda.payment_related_information.trim());
}
}
}
}
// Access file control totals
println!("\nFile Control:");
println!(" Batch Count: {}", ach_file.file_control.batch_count);
println!(" Total Debits: ${:.2}", ach_file.file_control.total_debit_amount as f64 / 100.0);
println!(" Total Credits: ${:.2}", ach_file.file_control.total_credit_amount as f64 / 100.0);
Ok(())
}An ACH file consists of the following record types:
Contains file-level information including:
- Immediate destination and origin (routing numbers)
- File creation date and time
- File identification
Contains batch-level information including:
- Service class code (200=mixed, 220=credits only, 225=debits only)
- Company name and identification
- Standard entry class code (PPD, CCD, WEB, etc.)
- Effective entry date
Contains individual transaction information including:
- Transaction code (22=checking credit, 27=checking debit, 32=savings credit, 37=savings debit)
- Receiving DFI identification (routing number)
- Account number
- Amount (in cents)
- Individual name
Optional additional information for an entry detail record.
Contains batch totals and counts:
- Entry/addenda count
- Entry hash
- Total debit and credit amounts
Contains file-level totals and counts:
- Batch count
- Block count
- Total debit and credit amounts
The parser provides detailed error information through the AchError enum:
use rs_ach::{AchFile, AchError};
match AchFile::parse(content) {
Ok(ach_file) => {
// Process the file
},
Err(AchError::InvalidLineLength(len)) => {
eprintln!("Invalid line length: {}", len);
},
Err(AchError::InvalidRecordType(rt)) => {
eprintln!("Invalid record type: {}", rt);
},
Err(e) => {
eprintln!("Parse error: {}", e);
}
}Run the test suite:
cargo testThe test suite includes:
- Basic ACH file parsing
- Credits-only batches (service class 220)
- Debits-only batches (service class 225)
- Entries with addenda records
- Error handling for invalid formats
MIT