feat: add CodeParser for barcode classification per PRD §4
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
39
lib/services/code_parser.dart
Normal file
39
lib/services/code_parser.dart
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
// lib/services/code_parser.dart
|
||||||
|
class CodeParser {
|
||||||
|
static final _zongpaiRegex = RegExp(
|
||||||
|
r'^\d{2}(B|C|T)\d+$|^\d{2}(BW|CW)\d{4}$',
|
||||||
|
);
|
||||||
|
static final _locationRegex = RegExp(
|
||||||
|
r'^[A-Z0-9]+-[A-Z0-9]+-[A-Z0-9]+$',
|
||||||
|
);
|
||||||
|
static final _transitRegex = RegExp(
|
||||||
|
r'^TRANS-',
|
||||||
|
);
|
||||||
|
|
||||||
|
static ParseResult parse(String code) {
|
||||||
|
if (code.isEmpty) {
|
||||||
|
return ParseResult(type: CodeType.invalid, value: code);
|
||||||
|
}
|
||||||
|
if (_transitRegex.hasMatch(code)) {
|
||||||
|
return ParseResult(type: CodeType.locationTransit, value: code);
|
||||||
|
}
|
||||||
|
if (_zongpaiRegex.hasMatch(code)) {
|
||||||
|
return ParseResult(type: CodeType.zongpaiNo, value: code);
|
||||||
|
}
|
||||||
|
if (_locationRegex.hasMatch(code)) {
|
||||||
|
return ParseResult(type: CodeType.locationNormal, value: code);
|
||||||
|
}
|
||||||
|
return ParseResult(type: CodeType.invalid, value: code);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isLocation(CodeType type) =>
|
||||||
|
type == CodeType.locationNormal || type == CodeType.locationTransit;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum CodeType { zongpaiNo, locationNormal, locationTransit, invalid }
|
||||||
|
|
||||||
|
class ParseResult {
|
||||||
|
final CodeType type;
|
||||||
|
final String value;
|
||||||
|
ParseResult({required this.type, required this.value});
|
||||||
|
}
|
||||||
58
test/services/code_parser_test.dart
Normal file
58
test/services/code_parser_test.dart
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:pad_scanner/services/code_parser.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('CodeParser.parse', () {
|
||||||
|
test('identifies zongpaiNo type B', () {
|
||||||
|
final result = CodeParser.parse('26B1');
|
||||||
|
expect(result.type, CodeType.zongpaiNo);
|
||||||
|
expect(result.value, '26B1');
|
||||||
|
});
|
||||||
|
test('identifies zongpaiNo type C', () {
|
||||||
|
final result = CodeParser.parse('26C12');
|
||||||
|
expect(result.type, CodeType.zongpaiNo);
|
||||||
|
});
|
||||||
|
test('identifies zongpaiNo type T', () {
|
||||||
|
final result = CodeParser.parse('26T3');
|
||||||
|
expect(result.type, CodeType.zongpaiNo);
|
||||||
|
});
|
||||||
|
test('identifies zongpaiNo type BW (4 digits)', () {
|
||||||
|
final result = CodeParser.parse('26BW0001');
|
||||||
|
expect(result.type, CodeType.zongpaiNo);
|
||||||
|
});
|
||||||
|
test('identifies zongpaiNo type CW (4 digits)', () {
|
||||||
|
final result = CodeParser.parse('26CW0015');
|
||||||
|
expect(result.type, CodeType.zongpaiNo);
|
||||||
|
});
|
||||||
|
test('rejects BW with non-4-digit serial', () {
|
||||||
|
expect(CodeParser.parse('26BW01').type, CodeType.invalid);
|
||||||
|
});
|
||||||
|
test('rejects CW with non-4-digit serial', () {
|
||||||
|
expect(CodeParser.parse('26CW15').type, CodeType.invalid);
|
||||||
|
});
|
||||||
|
test('identifies normal location code', () {
|
||||||
|
final result = CodeParser.parse('A01-02-03');
|
||||||
|
expect(result.type, CodeType.locationNormal);
|
||||||
|
expect(result.value, 'A01-02-03');
|
||||||
|
});
|
||||||
|
test('identifies transit location code', () {
|
||||||
|
final result = CodeParser.parse('TRANS-01');
|
||||||
|
expect(result.type, CodeType.locationTransit);
|
||||||
|
});
|
||||||
|
test('rejects lowercase location code', () {
|
||||||
|
expect(CodeParser.parse('a01-02-03').type, CodeType.invalid);
|
||||||
|
});
|
||||||
|
test('rejects invalid code', () {
|
||||||
|
expect(CodeParser.parse('HELLO123').type, CodeType.invalid);
|
||||||
|
});
|
||||||
|
test('rejects empty string', () {
|
||||||
|
expect(CodeParser.parse('').type, CodeType.invalid);
|
||||||
|
});
|
||||||
|
test('isLocation helper', () {
|
||||||
|
expect(CodeParser.isLocation(CodeType.locationNormal), true);
|
||||||
|
expect(CodeParser.isLocation(CodeType.locationTransit), true);
|
||||||
|
expect(CodeParser.isLocation(CodeType.zongpaiNo), false);
|
||||||
|
expect(CodeParser.isLocation(CodeType.invalid), false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user