A high-performance EDI (Electronic Data Interchange) parser written in Rust, supporting X12 format with extensible segment validation and loop-aware parsing.
- ✅ X12 Standard Support: Full X12 EDI parsing with version detection (4010, 5010, 6010)
- ✅ Document Type Recognition: Automatic detection of 810 (Invoice), 850 (Purchase Order), and custom transaction types
- ✅ Segment Validation: Built-in validation for common segments (BEG, PO1, N1, DTM, etc.)
- ✅ Loop-Aware Parsing: Structured parsing of EDI loops (party loops, line item loops)
- ✅ Extensible Architecture: Easy to add new segments, document types, and validation rules
- ✅ Error Handling: Comprehensive error reporting with detailed validation messages
- ✅ Performance: Zero-copy parsing with efficient memory usage
- Rust 1.70+ installed
- Cargo package manager
git clone https://github.com/Forworddash/EDI-Parser.git
cd EDI-Parser
cargo build --release
use edi_parser::{X12Parser, EdiParser};
let parser = X12Parser::default();
let edi_content = "ISA*00*...*IEA*1*000000001~"; // Your EDI data
let result = parser.parse(&edi_content);
match result {
Ok(interchange) => {
println!("Parsed {} transactions", interchange.functional_groups[0].transactions.len());
println!("EDI Version: {}", interchange.version.as_str());
}
Err(e) => println!("Parse error: {}", e),
}
cargo test
cargo test test_850_extended_parsing
cargo test -- --nocapture
Test EDI files are located in tests/test_files/
:
sample_810.edi
- Invoice (810) transactionsample_850.edi
- Basic Purchase Order (850)sample_850_extended.edi
- Extended Purchase Order with loopsinvalid_sample.edi
- Invalid EDI for error testing
- Create test EDI file in
tests/test_files/
:
# Example: Create a new 850 test file
echo "ISA*00* *00* *01*BUYERID *01*SELLERID *230101*1300*U*00401*000000002*0*T*>~
GS*PO*BUYERID*SELLERID*20230101*1300*2*X*004010~
ST*850*0001~
BEG*00*SA*PO-001**20230101~
N1*ST*Test Company*92*12345~
PO1*1*10*EA*15.00**BP*TEST-001~
CTT*1~
SE*6*0001~
GE*1*2~
IEA*1*000000002~" > tests/test_files/my_test_850.edi
- Add test function in
tests/integration_tests.rs
:
#[test]
fn test_my_custom_850() {
let content = fs::read_to_string("tests/test_files/my_test_850.edi")
.expect("Failed to read test file");
let parser = X12Parser::default();
let result = parser.parse(&content);
assert!(result.is_ok(), "Parse failed: {:?}", result.err());
let interchange = result.unwrap();
assert_eq!(interchange.version, X12Version::V4010);
assert_eq!(interchange.functional_groups[0].transactions[0].transaction_set_id, "850");
}
- Run your test:
cargo test test_my_custom_850
cargo run --example basic_parser
This uses tests/test_files/sample_810.edi
by default.
cargo run --example extended_850_parser
Demonstrates segment validation and loop parsing.
cargo run --example loop_parsing_demo
Shows both basic and structured loop parsing approaches.
use edi_parser::{X12Parser, EdiParser};
let parser = X12Parser::default();
let edi_data = fs::read_to_string("path/to/your/file.edi")?;
let interchange = parser.parse(&edi_data)?;
// Access parsed data
println!("Version: {}", interchange.version.as_str());
for fg in &interchange.functional_groups {
for transaction in &fg.transactions {
println!("Transaction: {} ({})",
transaction.transaction_set_id,
transaction.transaction_type.as_str());
}
}
use edi_parser::{X12Parser, EdiParser, PurchaseOrder850};
let parser = X12Parser::default();
let interchange = parser.parse(&edi_data)?;
// Parse into structured loops
for fg in &interchange.functional_groups {
for transaction in &fg.transactions {
if let Ok(po850) = PurchaseOrder850::parse_from_transaction(transaction) {
println!("PO Number: {}", po850.get_total_line_items());
println!("Total Quantity: {}", po850.get_total_quantity());
// Access specific parties
let ship_to_parties = po850.get_parties_by_type("ST");
for party in ship_to_parties {
println!("Ship To: {}", party.n1_segment.elements[1]);
}
}
}
}
use edi_parser::{TransactionType, Segment};
// Add custom validation
impl TransactionType {
pub fn validate_custom_segment(&self, segment: &Segment) -> Result<(), String> {
match segment.id.as_str() {
"MY_SEG" => {
if segment.elements.len() < 2 {
return Err("MY_SEG requires at least 2 elements".to_string());
}
// Your custom validation logic
Ok(())
}
_ => Ok(()),
}
}
}
Add to src/models/transaction.rs
in the validate_850_segment()
method:
"NEW_SEG" => {
// Validate NEW_SEG elements
if segment.elements.is_empty() {
return Err("NEW_SEG requires at least 1 element".to_string());
}
let code = &segment.elements[0];
if !["A", "B", "C"].contains(&code.as_str()) {
return Err(format!("Invalid NEW_SEG code: {}", code));
}
Ok(())
}
Extend the loop structures in src/models/loops.rs
:
pub struct LineItemLoop {
pub po1_segment: Segment,
pub pid_segments: Vec<Segment>,
pub new_seg_segments: Vec<Segment>, // Add your new segment
// ... other fields
}
- Add to
TransactionType
enum insrc/models/transaction.rs
- Implement required/optional segments
- Add validation methods
EDI-Parser/
├── src/
│ ├── lib.rs # Main library exports
│ ├── error.rs # Error types
│ ├── models/
│ │ ├── mod.rs # Model exports
│ │ ├── segment.rs # Basic segment structure
│ │ ├── transaction.rs # Transaction types and validation
│ │ ├── interchange.rs # Interchange structure
│ │ ├── version.rs # X12 version handling
│ │ └── loops.rs # Loop-based parsing structures
│ ├── parsers/
│ │ ├── mod.rs # Parser exports
│ │ ├── x12.rs # X12 parser implementation
│ │ └── common.rs # Common parser utilities
│ └── utils/
│ └── mod.rs # Utility functions
├── tests/
│ ├── integration_tests.rs # Integration tests
│ └── test_files/ # EDI test files
│ ├── sample_810.edi
│ ├── sample_850.edi
│ ├── sample_850_extended.edi
│ └── invalid_sample.edi
├── examples/
│ ├── basic_parser.rs # Basic parsing example
│ ├── extended_850_parser.rs # Extended 850 parsing
│ └── loop_parsing_demo.rs # Loop parsing demonstration
├── Cargo.toml
└── README.md
X12Parser
- Main parser for X12 EDIInterchangeControl
- Top-level EDI structureFunctionalGroup
- GS-GE groupTransaction
- ST-SE transactionSegment
- Individual EDI segmentPurchaseOrder850
- Structured 850 parsingTransactionType
- Document type enumerationX12Version
- EDI version enumeration
X12Parser::parse()
- Parse EDI stringX12Parser::validate()
- Validate parsed structurePurchaseOrder850::parse_from_transaction()
- Structured parsingTransactionType::validate_segment()
- Segment validation
The parser provides detailed error messages:
use edi_parser::EdiError;
match parser.parse(&edi_data) {
Ok(interchange) => { /* Success */ }
Err(EdiError::InvalidSegmentFormat(msg)) => {
println!("Segment format error: {}", msg);
}
Err(EdiError::MissingRequiredSegment(seg)) => {
println!("Missing required segment: {}", seg);
}
Err(EdiError::ValidationError(msg)) => {
println!("Validation error: {}", msg);
}
_ => { /* Other errors */ }
}
- Zero-copy parsing where possible
- Efficient memory usage with Vec-based storage
- Fast validation with early error detection
- Scalable architecture for large EDI files
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure all tests pass
- Submit a pull request
- Add comprehensive tests
- Update documentation
- Follow existing code patterns
- Maintain backward compatibility
This project is licensed under the MIT License - see the LICENSE file for details.
For questions or issues:
- Check existing tests and examples
- Review the API documentation
- Create an issue with sample EDI data
- Include error messages and expected behavior