diff --git a/include/just_gtfs/just_gtfs.h b/include/just_gtfs/just_gtfs.h index 4077c02..349d472 100644 --- a/include/just_gtfs/just_gtfs.h +++ b/include/just_gtfs/just_gtfs.h @@ -999,6 +999,14 @@ struct FareAttributesItem size_t transfer_duration = 0; // Length of time in seconds before a transfer expires }; +inline bool operator==(const FareAttributesItem & lhs, const FareAttributesItem & rhs) +{ + return std::tie(lhs.fare_id, lhs.price, lhs.currency_type, lhs.payment_method, lhs.transfers, + lhs.agency_id, lhs.transfer_duration) == + std::tie(rhs.fare_id, rhs.price, rhs.currency_type, rhs.payment_method, rhs.transfers, + rhs.agency_id, rhs.transfer_duration); +} + // Optional dataset file struct FareRule { @@ -1840,7 +1848,7 @@ inline Result Feed::add_fare_attributes(const ParsedCsvRow & row) item.currency_type = row.at("currency_type"); set_field(item.payment_method, row, "payment_method", false); - set_field(item.transfers, row, "transfers", false); + set_field(item.transfers, row, "transfers"); // Conditionally optional: item.agency_id = get_value_or_default(row, "agency_id"); @@ -2778,9 +2786,12 @@ inline void Feed::write_fare_attributes(std::ofstream & out) const for (const auto & attribute : fare_attributes) { std::vector fields{ - wrap(attribute.fare_id), wrap(attribute.price), attribute.currency_type, - wrap(attribute.payment_method), wrap(attribute.transfers), wrap(attribute.agency_id), - wrap(attribute.transfer_duration)}; + wrap(attribute.fare_id), wrap(attribute.price), attribute.currency_type, + wrap(attribute.payment_method), + // Here we handle GTFS specification corner case: "The fact that this field can be left + // empty is an exception to the requirement that a Required field must not be empty.": + attribute.transfers == FareTransfers::Unlimited ? "" : wrap(attribute.transfers), + wrap(attribute.agency_id), wrap(attribute.transfer_duration)}; write_joined(out, std::move(fields)); } } diff --git a/tests/data/sample_feed/fare_attributes.txt b/tests/data/sample_feed/fare_attributes.txt index 3ee7a99..22b7efb 100644 --- a/tests/data/sample_feed/fare_attributes.txt +++ b/tests/data/sample_feed/fare_attributes.txt @@ -1,3 +1,4 @@ fare_id,price,currency_type,payment_method,transfers,transfer_duration p,1.25,USD,0,0, -a,5.25,USD,0,0, \ No newline at end of file +a,5.25,USD,1,1, +x,20,USD,0,,60 \ No newline at end of file diff --git a/tests/unit_tests.cpp b/tests/unit_tests.cpp index 4fc646e..8e738b5 100644 --- a/tests/unit_tests.cpp +++ b/tests/unit_tests.cpp @@ -310,7 +310,7 @@ TEST_CASE("Read GTFS feed") CHECK_EQ(feed.get_attributions().size(), 1); CHECK_EQ(feed.get_calendar().size(), 2); CHECK_EQ(feed.get_calendar_dates().size(), 1); - CHECK_EQ(feed.get_fare_attributes().size(), 2); + CHECK_EQ(feed.get_fare_attributes().size(), 3); CHECK_EQ(feed.get_fare_rules().size(), 4); CHECK(!feed.get_feed_info().feed_publisher_name.empty()); CHECK_EQ(feed.get_levels().size(), 3); @@ -494,7 +494,7 @@ TEST_CASE("Fare attributes") REQUIRE_EQ(feed.read_fare_attributes(), ResultCode::OK); const auto & attributes = feed.get_fare_attributes(); - REQUIRE_EQ(attributes.size(), 2); + REQUIRE_EQ(attributes.size(), 3); CHECK_EQ(attributes[0].fare_id, "p"); CHECK_EQ(attributes[0].price, 1.25); CHECK_EQ(attributes[0].currency_type, "USD"); @@ -502,9 +502,28 @@ TEST_CASE("Fare attributes") CHECK_EQ(attributes[0].transfers, FareTransfers::No); CHECK_EQ(attributes[0].transfer_duration, 0); + CHECK_EQ(attributes[1].fare_id, "a"); + CHECK_EQ(attributes[1].price, 5.25); + CHECK_EQ(attributes[1].currency_type, "USD"); + CHECK_EQ(attributes[1].payment_method, FarePayment::BeforeBoarding); + CHECK_EQ(attributes[1].transfers, FareTransfers::Once); + CHECK_EQ(attributes[1].transfer_duration, 0); + + CHECK_EQ(attributes[2].fare_id, "x"); + CHECK_EQ(attributes[2].price, 20); + CHECK_EQ(attributes[2].currency_type, "USD"); + CHECK_EQ(attributes[2].payment_method, FarePayment::OnBoard); + CHECK_EQ(attributes[2].transfers, FareTransfers::Unlimited); + CHECK_EQ(attributes[2].transfer_duration, 60); + const auto & attributes_for_id = feed.get_fare_attributes("a"); REQUIRE_EQ(attributes_for_id.size(), 1); CHECK_EQ(attributes_for_id[0].price, 5.25); + + REQUIRE_EQ(feed.write_fare_attributes("data/output_feed"), ResultCode::OK); + Feed feed_copy("data/output_feed"); + REQUIRE_EQ(feed_copy.read_fare_attributes(), ResultCode::OK); + CHECK_EQ(attributes, feed_copy.get_fare_attributes()); } TEST_CASE("Fare rules")