From 07a16c064247ddc64d2a5ce6520eab8a547cc51e Mon Sep 17 00:00:00 2001 From: Marco Date: Fri, 13 Sep 2024 22:35:32 +0200 Subject: [PATCH] Fix comma bug This fixes a bug that was caused by a name containing a comma. We store the food entries in a csv so logically a comma in the name, causes the parser to crash. We fix this, by wrapping the name in quotation marks and writing a new parser for food entries. --- lib/food_entry/food_entry_bloc.dart | 4 +- lib/storage/storage.dart | 43 ++++- pubspec.lock | 255 +++++++++++++++++++++++++++- pubspec.yaml | 3 +- test/split_with_ignore_test.dart | 59 +++++++ 5 files changed, 359 insertions(+), 5 deletions(-) create mode 100644 test/split_with_ignore_test.dart diff --git a/lib/food_entry/food_entry_bloc.dart b/lib/food_entry/food_entry_bloc.dart index 10ef2a8..4a95671 100644 --- a/lib/food_entry/food_entry_bloc.dart +++ b/lib/food_entry/food_entry_bloc.dart @@ -97,6 +97,8 @@ class FoodEntry { @override String toString() { - return '$id,$name,$mass,$kcalPerMass'; + //we use quotation marks around the name because the name might contain + //commas and we want to store it in a csv file + return '$id,"$name",$mass,$kcalPerMass'; } } diff --git a/lib/storage/storage.dart b/lib/storage/storage.dart index e3cdbf7..e5c989c 100644 --- a/lib/storage/storage.dart +++ b/lib/storage/storage.dart @@ -43,9 +43,9 @@ class FoodStorage { var lines = await file.readAsLines(); for (var line in lines) { - var fields = line.split(','); + var fields = line.splitWithIgnore(',', ignoreIn: '"'); var entry = FoodEntry( - name: fields[1], + name: fields[1].replaceAll('"', ""), mass: double.parse(fields[2]), kcalPerMass: double.parse(fields[3])); entries.add(entry); @@ -164,3 +164,42 @@ class FoodStorage { Map get getFoodEntryLookupDatabase => _foodLookupDatabase; } + +extension SplitWithIgnore on String { + List splitWithIgnore(String delimiter, {String? ignoreIn}) { + List parts = []; + + if (ignoreIn == null) { + return split(delimiter); + } + + int index = -1; + int indexCharAfterDelimiter = 0; + bool inIgnore = false; + for (var rune in runes) { + var char = String.fromCharCode(rune); + + index += 1; + + if (char == ignoreIn) { + inIgnore = !inIgnore; + continue; + } + + if (inIgnore) { + continue; + } + + if (char == delimiter) { + parts.add(substring(indexCharAfterDelimiter, index)); + indexCharAfterDelimiter = index + 1; + } + + if (index + 1 == length) { + parts.add(substring(indexCharAfterDelimiter, length)); + } + } + + return parts; + } +} diff --git a/pubspec.lock b/pubspec.lock index 9ed67d0..ec2f85e 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,6 +1,27 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + sha256: f256b0c0ba6c7577c15e2e4e114755640a875e885099367bf6e012b19314c834 + url: "https://pub.dev" + source: hosted + version: "72.0.0" + _macros: + dependency: transitive + description: dart + source: sdk + version: "0.3.2" + analyzer: + dependency: transitive + description: + name: analyzer + sha256: b652861553cd3990d8ed361f7979dc6d7053a9ac8843fa73820ab68ce5410139 + url: "https://pub.dev" + source: hosted + version: "6.7.0" archive: dependency: transitive description: @@ -89,6 +110,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.18.0" + convert: + dependency: transitive + description: + name: convert + sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" + url: "https://pub.dev" + source: hosted + version: "3.1.1" + coverage: + dependency: transitive + description: + name: coverage + sha256: c1fb2dce3c0085f39dc72668e85f8e0210ec7de05345821ff58530567df345a5 + url: "https://pub.dev" + source: hosted + version: "1.9.2" crypto: dependency: transitive description: @@ -113,6 +150,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.3" + file: + dependency: transitive + description: + name: file + sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" + url: "https://pub.dev" + source: hosted + version: "7.0.0" fixnum: dependency: transitive description: @@ -160,6 +205,38 @@ packages: description: flutter source: sdk version: "0.0.0" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 + url: "https://pub.dev" + source: hosted + version: "4.0.0" + glob: + dependency: transitive + description: + name: glob + sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" + url: "https://pub.dev" + source: hosted + version: "3.2.1" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + url: "https://pub.dev" + source: hosted + version: "4.0.2" image: dependency: transitive description: @@ -176,6 +253,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.19.0" + io: + dependency: transitive + description: + name: io + sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" + url: "https://pub.dev" + source: hosted + version: "1.0.4" + js: + dependency: transitive + description: + name: js + sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf + url: "https://pub.dev" + source: hosted + version: "0.7.1" json_annotation: dependency: transitive description: @@ -216,6 +309,22 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.0" + logging: + dependency: transitive + description: + name: logging + sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + macros: + dependency: transitive + description: + name: macros + sha256: "0acaed5d6b7eab89f63350bccd82119e6c602df0f391260d0e32b5e23db79536" + url: "https://pub.dev" + source: hosted + version: "0.1.2-main.4" matcher: dependency: transitive description: @@ -240,6 +349,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.15.0" + mime: + dependency: transitive + description: + name: mime + sha256: "801fd0b26f14a4a58ccb09d5892c3fbdeff209594300a542492cf13fba9d247a" + url: "https://pub.dev" + source: hosted + version: "1.0.6" nested: dependency: transitive description: @@ -248,6 +365,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.0" + node_preamble: + dependency: transitive + description: + name: node_preamble + sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db" + url: "https://pub.dev" + source: hosted + version: "2.0.2" + package_config: + dependency: transitive + description: + name: package_config + sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" + url: "https://pub.dev" + source: hosted + version: "2.1.0" path: dependency: transitive description: @@ -328,6 +461,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.8" + pool: + dependency: transitive + description: + name: pool + sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" + url: "https://pub.dev" + source: hosted + version: "1.5.1" protobuf: dependency: transitive description: @@ -344,6 +485,14 @@ packages: url: "https://pub.dev" source: hosted version: "6.1.2" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" + url: "https://pub.dev" + source: hosted + version: "2.1.4" settings_ui: dependency: "direct main" description: @@ -352,11 +501,59 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.2" + shelf: + dependency: transitive + description: + name: shelf + sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 + url: "https://pub.dev" + source: hosted + version: "1.4.1" + shelf_packages_handler: + dependency: transitive + description: + name: shelf_packages_handler + sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + shelf_static: + dependency: transitive + description: + name: shelf_static + sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3 + url: "https://pub.dev" + source: hosted + version: "1.1.3" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + sha256: "073c147238594ecd0d193f3456a5fe91c4b0abbcc68bf5cd95b36c4e194ac611" + url: "https://pub.dev" + source: hosted + version: "2.0.0" sky_engine: dependency: transitive description: flutter source: sdk version: "0.0.99" + source_map_stack_trace: + dependency: transitive + description: + name: source_map_stack_trace + sha256: c0713a43e323c3302c2abe2a1cc89aa057a387101ebd280371d6a6c9fa68516b + url: "https://pub.dev" + source: hosted + version: "2.1.2" + source_maps: + dependency: transitive + description: + name: source_maps + sha256: "708b3f6b97248e5781f493b765c3337db11c5d2c81c3094f10904bfa8004c703" + url: "https://pub.dev" + source: hosted + version: "0.10.12" source_span: dependency: transitive description: @@ -405,6 +602,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.1" + test: + dependency: "direct main" + description: + name: test + sha256: "7ee44229615f8f642b68120165ae4c2a75fe77ae2065b1e55ae4711f6cf0899e" + url: "https://pub.dev" + source: hosted + version: "1.25.7" test_api: dependency: transitive description: @@ -413,6 +618,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.2" + test_core: + dependency: transitive + description: + name: test_core + sha256: "55ea5a652e38a1dfb32943a7973f3681a60f872f8c3a05a14664ad54ef9c6696" + url: "https://pub.dev" + source: hosted + version: "0.6.4" typed_data: dependency: transitive description: @@ -453,6 +666,46 @@ packages: url: "https://pub.dev" source: hosted version: "14.2.5" + watcher: + dependency: transitive + description: + name: watcher + sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + web: + dependency: transitive + description: + name: web + sha256: d43c1d6b787bf0afad444700ae7f4db8827f701bc61c255ac8d328c6f4d52062 + url: "https://pub.dev" + source: hosted + version: "1.0.0" + web_socket: + dependency: transitive + description: + name: web_socket + sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83" + url: "https://pub.dev" + source: hosted + version: "0.1.6" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + sha256: "9f187088ed104edd8662ca07af4b124465893caf063ba29758f97af57e61da8f" + url: "https://pub.dev" + source: hosted + version: "3.0.1" + webkit_inspection_protocol: + dependency: transitive + description: + name: webkit_inspection_protocol + sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572" + url: "https://pub.dev" + source: hosted + version: "1.2.1" xdg_directories: dependency: transitive description: @@ -478,5 +731,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.5.2 <4.0.0" + dart: ">=3.5.3 <4.0.0" flutter: ">=3.22.0" diff --git a/pubspec.yaml b/pubspec.yaml index b9eec07..b84d1dd 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: 'none' version: 1.1.0 environment: - sdk: ^3.5.2 + sdk: ^3.5.3 dependencies: flutter: @@ -19,6 +19,7 @@ dependencies: uuid: ^4.5.0 barcode_scan2: ^4.3.3 provider: ^6.1.2 + test: ^1.25.7 dev_dependencies: flutter_test: diff --git a/test/split_with_ignore_test.dart b/test/split_with_ignore_test.dart new file mode 100644 index 0000000..af7e403 --- /dev/null +++ b/test/split_with_ignore_test.dart @@ -0,0 +1,59 @@ +import 'package:calorimeter/storage/storage.dart'; +import 'package:test/test.dart'; + +void main() { + group( + 'Test custom split with ignore', + () { + test('string without ignoring', () { + var testString = 'This is a test string'; + var resultingList = testString.splitWithIgnore(' '); + + expect(resultingList[0], equals('This')); + expect(resultingList[1], equals('is')); + expect(resultingList[2], equals('a')); + expect(resultingList[3], equals('test')); + expect(resultingList[4], equals('string')); + }); + + test('string that does not contain the ignored character', () { + var testString = 'This is a test string'; + var resultingList = testString.splitWithIgnore(' ', ignoreIn: '"'); + + expect(resultingList[0], equals('This')); + expect(resultingList[1], equals('is')); + expect(resultingList[2], equals('a')); + expect(resultingList[3], equals('test')); + expect(resultingList[4], equals('string')); + }); + + test( + 'string that contains ignored character', + () { + var testString = 'This is "a test" string'; + var resultingList = testString.splitWithIgnore(' ', ignoreIn: '"'); + + expect(resultingList[0], equals('This')); + expect(resultingList[1], equals('is')); + expect(resultingList[2], equals('"a test"')); + expect(resultingList[3], equals('string')); + }, + ); + + test( + 'string that contains commas that should be ignored', + () { + var testString = + 'f9a96b80-71f9-11ef-8df4-f3628a737a16,"Erdnüsse, geröstet",120.0,100.0'; + var resultingList = testString.splitWithIgnore(',', ignoreIn: '"'); + + expect( + resultingList[0], equals('f9a96b80-71f9-11ef-8df4-f3628a737a16')); + expect(resultingList[1], equals('"Erdnüsse, geröstet"')); + expect(resultingList[2], equals('120.0')); + expect(resultingList[3], equals('100.0')); + }, + ); + }, + ); +}