diff --git a/models/rust/Cargo.toml b/models/rust/Cargo.toml index 421b9f2..55fe501 100644 --- a/models/rust/Cargo.toml +++ b/models/rust/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gbfs_types" -version = "0.1.4" +version = "0.1.5" edition = "2021" license = "Apache-2.0" description = "Types for GBFS" diff --git a/models/rust/src/2.3/files/examples/specification/free_bike_status-1.json b/models/rust/src/2.3/files/examples/specification/free_bike_status-1.json new file mode 100644 index 0000000..608eef1 --- /dev/null +++ b/models/rust/src/2.3/files/examples/specification/free_bike_status-1.json @@ -0,0 +1,32 @@ +{ + "last_updated": 1640887163, + "ttl": 0, + "version": "2.3", + "data": { + "bikes": [ + { + "bike_id": "973a5c94-c288-4a2b-afa6-de8aeb6ae2e5", + "last_reported": 1609866109, + "lat": 12.34, + "lon": 56.78, + "is_reserved": false, + "is_disabled": false, + "vehicle_type_id": "abc123", + "rental_uris": { + "android": "https://www.example.com/app?bike_id=973a5c94-c288-4a2b-afa6-de8aeb6ae2e5&platform=android&", + "ios": "https://www.example.com/app?bike_id=973a5c94-c288-4a2b-afa6-de8aeb6ae2e5&platform=ios" + } + }, + { + "bike_id": "987fd100-b822-4347-86a4-b3eef8ca8b53", + "last_reported": 1609866204, + "is_reserved": false, + "is_disabled": false, + "vehicle_type_id": "def456", + "current_range_meters": 6543.0, + "station_id": "86", + "pricing_plan_id": "plan3" + } + ] + } +} diff --git a/models/rust/src/2.3/files/examples/specification/free_bike_status-2.json b/models/rust/src/2.3/files/examples/specification/free_bike_status-2.json new file mode 100644 index 0000000..7a5ded9 --- /dev/null +++ b/models/rust/src/2.3/files/examples/specification/free_bike_status-2.json @@ -0,0 +1,40 @@ +{ + "last_updated": 1640887163, + "ttl": 0, + "version": "2.3", + "data": { + "bikes": [ + { + "bike_id": "45bd3fb7-a2d5-4def-9de1-c645844ba962", + "last_reported": 1609866109, + "lat": 12.345678, + "lon": 56.789012, + "is_reserved": false, + "is_disabled": false, + "vehicle_type_id": "abc123", + "current_range_meters": 400000.0, + "available_until": "2021-05-17T15:00:00Z", + "home_station_id": "station1", + "vehicle_equipment": [ + "child_seat_a", + "winter_tires" + ] + }, + { + "bike_id": "d4521def-7922-4e46-8e1d-8ac397239bd0", + "last_reported": 1609866204, + "is_reserved": false, + "is_disabled": false, + "vehicle_type_id": "def456", + "current_fuel_percent": 0.7, + "current_range_meters": 6543.0, + "station_id": "86", + "pricing_plan_id": "plan3", + "home_station_id": "146", + "vehicle_equipment": [ + "child_seat_a" + ] + } + ] + } +} diff --git a/models/rust/src/2.3/files/examples/specification/gbfs.json b/models/rust/src/2.3/files/examples/specification/gbfs.json new file mode 100644 index 0000000..80c9f28 --- /dev/null +++ b/models/rust/src/2.3/files/examples/specification/gbfs.json @@ -0,0 +1,31 @@ +{ + "last_updated": 1640887163, + "ttl": 0, + "version": "2.3", + "data": { + "en": { + "feeds": [ + { + "name": "system_information", + "url": "https://www.example.com/gbfs/1/en/system_information" + }, + { + "name": "station_information", + "url": "https://www.example.com/gbfs/1/en/station_information" + } + ] + }, + "fr" : { + "feeds": [ + { + "name": "system_information", + "url": "https://www.example.com/gbfs/1/fr/system_information" + }, + { + "name": "station_information", + "url": "https://www.example.com/gbfs/1/fr/station_information" + } + ] + } + } +} diff --git a/models/rust/src/2.3/files/examples/specification/gbfs_versions.json b/models/rust/src/2.3/files/examples/specification/gbfs_versions.json new file mode 100644 index 0000000..99d2ecd --- /dev/null +++ b/models/rust/src/2.3/files/examples/specification/gbfs_versions.json @@ -0,0 +1,17 @@ +{ + "last_updated": 1640887163, + "ttl": 0, + "version": "2.3", + "data": { + "versions": [ + { + "version": "2.0", + "url": "https://www.example.com/gbfs/2/gbfs" + }, + { + "version": "2.3", + "url": "https://www.example.com/gbfs/3/gbfs" + } + ] + } +} diff --git a/models/rust/src/2.3/files/examples/specification/geofencing_zones.json b/models/rust/src/2.3/files/examples/specification/geofencing_zones.json new file mode 100644 index 0000000..35abb26 --- /dev/null +++ b/models/rust/src/2.3/files/examples/specification/geofencing_zones.json @@ -0,0 +1,85 @@ +{ + "last_updated": 1640887163, + "ttl": 60, + "version": "2.3", + "data": { + "geofencing_zones": { + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "geometry": { + "type": "MultiPolygon", + "coordinates": [ + [ + [ + [ + -122.578067, + 45.562982 + ], + [ + -122.661838, + 45.562741 + ], + [ + -122.661151, + 45.504542 + ], + [ + -122.578926, + 45.5046625 + ], + [ + -122.578067, + 45.562982 + ] + ] + ], + [ + [ + [ + -122.650680, + 45.548197 + ], + [ + -122.650852, + 45.534731 + ], + [ + -122.630939, + 45.535212 + ], + [ + -122.630424, + 45.548197 + ], + [ + -122.650680, + 45.548197 + ] + ] + ] + ] + }, + "properties": { + "name": "NE 24th/NE Knott", + "start": 1593878400, + "end": 1593907260, + "rules": [ + { + "vehicle_type_id": [ + "moped1", + "car1" + ], + "ride_allowed": false, + "ride_through_allowed": true, + "maximum_speed_kph": 10, + "station_parking": true + } + ] + } + } + ] + } + } +} diff --git a/models/rust/src/2.3/files/examples/specification/station_information-1.json b/models/rust/src/2.3/files/examples/specification/station_information-1.json new file mode 100644 index 0000000..a815c90 --- /dev/null +++ b/models/rust/src/2.3/files/examples/specification/station_information-1.json @@ -0,0 +1,23 @@ +{ + "last_updated": 1640887163, + "ttl": 0, + "version": "2.3", + "data": { + "stations": [ + { + "station_id": "pga", + "name": "Parking garage A", + "lat": 12.345678, + "lon": 45.678901, + "parking_type": "underground_parking", + "parking_hoop": false, + "contact_phone": "+33109874321", + "is_charging_station": true, + "vehicle_type_capacity": { + "abc123": 7, + "def456": 9 + } + } + ] + } +} diff --git a/models/rust/src/2.3/files/examples/specification/station_information-2.json b/models/rust/src/2.3/files/examples/specification/station_information-2.json new file mode 100644 index 0000000..00fbea7 --- /dev/null +++ b/models/rust/src/2.3/files/examples/specification/station_information-2.json @@ -0,0 +1,53 @@ +{ + "last_updated": 1640887163, + "ttl": 0, + "version": "2.3", + "data": { + "stations": [ + { + "station_id": "station12", + "name": "SE Belmont & SE 10 th", + "lat": 45.516445, + "lon": -122.655775, + "is_valet_station": false, + "is_virtual_station": true, + "is_charging_station": false, + "station_area": { + "type": "MultiPolygon", + "coordinates": [ + [ + [ + [ + -122.655775, + 45.516445 + ], + [ + -122.655705, + 45.516445 + ], + [ + -122.655705, + 45.516495 + ], + [ + -122.655775, + 45.516495 + ], + [ + -122.655775, + 45.516445 + ] + ] + ] + ] + }, + "capacity": 16, + "vehicle_capacity": { + "abc123": 8, + "def456": 8, + "ghi789": 16 + } + } + ] + } +} diff --git a/models/rust/src/2.3/files/examples/specification/station_status.json b/models/rust/src/2.3/files/examples/specification/station_status.json new file mode 100644 index 0000000..25261fc --- /dev/null +++ b/models/rust/src/2.3/files/examples/specification/station_status.json @@ -0,0 +1,71 @@ +{ + "last_updated": 1640887163, + "ttl": 0, + "version": "2.3", + "data": { + "stations": [ + { + "station_id": "station1", + "is_installed": true, + "is_renting": true, + "is_returning": true, + "last_reported": 1609866125, + "num_docks_available": 3, + "num_docks_disabled" : 1, + "vehicle_docks_available": [ + { + "vehicle_type_ids": [ "abc123", "def456" ], + "count": 2 + }, + { + "vehicle_type_ids": [ "def456" ], + "count": 1 + } + ], + "num_bikes_available": 1, + "num_bikes_disabled": 2, + "vehicle_types_available": [ + { + "vehicle_type_id": "abc123", + "count": 1 + }, + { + "vehicle_type_id": "def456", + "count": 0 + } + ] + }, + { + "station_id": "station2", + "is_installed": true, + "is_renting": true, + "is_returning": true, + "last_reported": 1609866106, + "num_docks_available": 8, + "num_docks_disabled" : 1, + "vehicle_docks_available": [ + { + "vehicle_type_ids": [ "abc123" ], + "count": 6 + }, + { + "vehicle_type_ids": [ "def456" ], + "count": 2 + } + ], + "num_bikes_available": 6, + "num_bikes_disabled": 1, + "vehicle_types_available": [ + { + "vehicle_type_id": "abc123", + "count": 2 + }, + { + "vehicle_type_id": "def456", + "count": 4 + } + ] + } + ] + } +} diff --git a/models/rust/src/2.3/files/examples/specification/system_alerts.json b/models/rust/src/2.3/files/examples/specification/system_alerts.json new file mode 100644 index 0000000..21e0e01 --- /dev/null +++ b/models/rust/src/2.3/files/examples/specification/system_alerts.json @@ -0,0 +1,28 @@ +{ + "last_updated": 1604519393, + "ttl": 60, + "version": "2.3", + "data": { + "alerts": [ + { + "alert_id": "21", + "type": "station_closure", + "station_ids": [ + "123", + "456", + "789" + ], + "times": [ + { + "start": 1604448000, + "end": 1604674800 + } + ], + "url": "https://example.com/more-info", + "summary": "Disruption of Service", + "description": "The three stations on Broadway will be out of service from 12:00am Nov 3 to 3:00pm Nov 6th to accommodate road work", + "last_updated": 1604198100 + } + ] + } +} diff --git a/models/rust/src/2.3/files/examples/specification/system_calendar.json b/models/rust/src/2.3/files/examples/specification/system_calendar.json new file mode 100644 index 0000000..86da6f8 --- /dev/null +++ b/models/rust/src/2.3/files/examples/specification/system_calendar.json @@ -0,0 +1,17 @@ +{ + "last_updated": 1640887163, + "ttl": 86400, + "version": "2.3", + "data": { + "calendars": [ + { + "start_month": 4, + "start_day": 1, + "start_year": 2020, + "end_month": 11, + "end_day": 5, + "end_year": 2020 + } + ] + } +} diff --git a/models/rust/src/2.3/files/examples/specification/system_hours.json b/models/rust/src/2.3/files/examples/specification/system_hours.json new file mode 100644 index 0000000..5c761ef --- /dev/null +++ b/models/rust/src/2.3/files/examples/specification/system_hours.json @@ -0,0 +1,42 @@ +{ + "last_updated": 1640887163, + "ttl": 86400, + "version": "2.3", + "data": { + "rental_hours": [ + { + "user_types": [ "member" ], + "days": [ + "sat", + "sun" + ], + "start_time": "00:00:00", + "end_time": "23:59:59" + }, + { + "user_types": [ "nonmember" ], + "days": [ + "sat", + "sun" + ], + "start_time": "05:00:00", + "end_time": "23:59:59" + }, + { + "user_types": [ + "member", + "nonmember" + ], + "days": [ + "mon", + "tue", + "wed", + "thu", + "fri" + ], + "start_time": "00:00:00", + "end_time": "23:59:59" + } + ] + } +} diff --git a/models/rust/src/2.3/files/examples/specification/system_information.json b/models/rust/src/2.3/files/examples/specification/system_information.json new file mode 100644 index 0000000..6eaa45d --- /dev/null +++ b/models/rust/src/2.3/files/examples/specification/system_information.json @@ -0,0 +1,42 @@ +{ + "last_updated": 1640887163, + "ttl": 1800, + "version": "2.3", + "data": { + "system_id": "example_cityname", + "language": "en", + "name": "Example Bike Rental", + "short_name": "Example Bike", + "operator": "Example Sharing, Inc", + "url": "https://www.example.com", + "purchase_url": "https://www.example.com", + "start_date": "2010-06-10", + "phone_number": "1-800-555-1234", + "email": "customerservice@example.com", + "feed_contact_email": "datafeed@example.com", + "timezone": "America/Chicago", + "license_url": "https://www.example.com/data-license.html", + "terms_url": "https://www.example.com/terms", + "terms_last_updated": "2021-06-21", + "privacy_url": "https://www.example.com/privacy-policy", + "privacy_last_updated": "2019-01-13", + "rental_apps": { + "android": { + "discovery_uri": "com.example.android://", + "store_uri": "https://play.google.com/store/apps/details?id=com.example.android" + }, + "ios": { + "store_uri": "https://apps.apple.com/app/apple-store/id123456789", + "discovery_uri": "com.example.ios://" + } + }, + "brand_assets": { + "brand_last_modified": "2021-06-15", + "brand_image_url": "https://www.example.com/assets/brand_image.svg", + "brand_image_url_dark": "https://www.example.com/assets/brand_image_dark.svg", + "color": "#C2D32C", + "brand_terms_url": "https://www.example.com/assets/brand.pdf" + } + + } +} diff --git a/models/rust/src/2.3/files/examples/specification/system_pricing_plans-1.json b/models/rust/src/2.3/files/examples/specification/system_pricing_plans-1.json new file mode 100644 index 0000000..e3a5c06 --- /dev/null +++ b/models/rust/src/2.3/files/examples/specification/system_pricing_plans-1.json @@ -0,0 +1,35 @@ +{ + "last_updated": 1640887163, + "ttl": 0, + "version": "2.3", + "data": { + "plans": [ + { + "plan_id": "plan2", + "name": "One-Way", + "currency": "USD", + "price": 2.00, + "is_taxable": false, + "description": "Includes 10km, overage fees apply after 10km.", + "per_km_pricing": [ + { + "start": 10, + "rate": 1.00, + "interval": 1.0, + "end": 25 + }, + { + "start": 25, + "rate": 0.50, + "interval": 1.0 + }, + { + "start": 25, + "rate": 3.00, + "interval": 5.0 + } + ] + } + ] + } +} diff --git a/models/rust/src/2.3/files/examples/specification/system_pricing_plans-2.json b/models/rust/src/2.3/files/examples/specification/system_pricing_plans-2.json new file mode 100644 index 0000000..ac1a20f --- /dev/null +++ b/models/rust/src/2.3/files/examples/specification/system_pricing_plans-2.json @@ -0,0 +1,31 @@ +{ + "last_updated": 1640887163, + "ttl": 0, + "version": "2.3", + "data": { + "plans": [ + { + "plan_id": "plan3", + "name": "Simple Rate", + "currency": "CAD", + "price": 3.00, + "is_taxable": true, + "description": "$3 unlock fee, $0.25 per kilometer and 0.50 per minute.", + "per_km_pricing": [ + { + "start": 0, + "rate": 0.25, + "interval": 1.0 + } + ], + "per_min_pricing": [ + { + "start": 0, + "rate": 0.50, + "interval": 1.0 + } + ] + } + ] + } +} diff --git a/models/rust/src/2.3/files/examples/specification/system_regions.json b/models/rust/src/2.3/files/examples/specification/system_regions.json new file mode 100644 index 0000000..cdec80b --- /dev/null +++ b/models/rust/src/2.3/files/examples/specification/system_regions.json @@ -0,0 +1,25 @@ +{ + "last_updated": 1640887163, + "ttl": 86400, + "version": "2.3", + "data": { + "regions": [ + { + "name": "North", + "region_id": "3" + }, + { + "name": "East", + "region_id": "4" + }, + { + "name": "South", + "region_id": "5" + }, + { + "name": "West", + "region_id": "6" + } + ] + } +} diff --git a/models/rust/src/2.3/files/examples/specification/vehicle_types.json b/models/rust/src/2.3/files/examples/specification/vehicle_types.json new file mode 100644 index 0000000..05255c9 --- /dev/null +++ b/models/rust/src/2.3/files/examples/specification/vehicle_types.json @@ -0,0 +1,105 @@ +{ + "last_updated": 1640887163, + "ttl": 0, + "version": "2.3", + "data": { + "vehicle_types": [ + { + "vehicle_type_id": "abc123", + "form_factor": "bicycle", + "propulsion_type": "human", + "name": "Example Basic Bike", + "wheel_count": 2, + "default_reserve_time": 30, + "return_constraint": "any_station", + "vehicle_assets": { + "icon_url": "https://www.example.com/assets/icon_bicycle.svg", + "icon_url_dark": "https://www.example.com/assets/icon_bicycle_dark.svg", + "icon_last_modified": "2021-06-15" + }, + "default_pricing_plan_id": "bike_plan_1", + "pricing_plan_ids": [ + "bike_plan_1", + "bike_plan_2", + "bike_plan_3" + ] + }, + { + "vehicle_type_id": "cargo123", + "form_factor": "cargo_bicycle", + "propulsion_type": "human", + "name": "Example Cargo Bike", + "wheel_count": 3, + "default_reserve_time": 30, + "return_constraint": "roundtrip_station", + "vehicle_assets": { + "icon_url": "https://www.example.com/assets/icon_cargobicycle.svg", + "icon_url_dark": "https://www.example.com/assets/icon_cargobicycle_dark.svg", + "icon_last_modified": "2021-06-15" + }, + "default_pricing_plan_id": "cargo_plan_1", + "pricing_plan_ids": [ + "cargo_plan_1", + "cargo_plan_2", + "cargo_plan_3" + ] + }, + { + "vehicle_type_id": "def456", + "form_factor": "scooter_standing", + "propulsion_type": "electric", + "name": "Example E-scooter V2", + "wheel_count": 2, + "max_permitted_speed": 25, + "rated_power": 350, + "default_reserve_time": 30, + "max_range_meters": 12345.0, + "return_constraint": "free_floating", + "vehicle_assets": { + "icon_url": "https://www.example.com/assets/icon_escooter.svg", + "icon_url_dark": "https://www.example.com/assets/icon_escooter_dark.svg", + "icon_last_modified": "2021-06-15" + }, + "default_pricing_plan_id": "scooter_plan_1" + }, + { + "vehicle_type_id": "car1", + "form_factor": "car", + "rider_capacity": 5, + "cargo_volume_capacity": 200, + "propulsion_type": "combustion_diesel", + "eco_labels": [ + { + "country_code": "FR", + "eco_sticker": "critair_1" + }, + { + "country_code": "DE", + "eco_sticker": "euro_2" + } + ], + "name": "Four-door Sedan", + "wheel_count": 4, + "default_reserve_time": 0, + "max_range_meters": 523992.0, + "return_constraint": "roundtrip_station", + "vehicle_accessories": [ + "doors_4", + "automatic", + "cruise_control" + ], + "g_CO2_km": 120, + "vehicle_image": "https://www.example.com/assets/renault-clio.jpg", + "make": "Renault", + "model": "Clio", + "color": "white", + "vehicle_assets": { + "icon_url": "https://www.example.com/assets/icon_car.svg", + "icon_url_dark": "https://www.example.com/assets/icon_car_dark.svg", + "icon_last_modified": "2021-06-15" + }, + "default_pricing_plan_id": "car_plan_1" + } + ] + } +} diff --git a/models/rust/src/2.3/files/free_bike_status.rs b/models/rust/src/2.3/files/free_bike_status.rs new file mode 100644 index 0000000..77ebb18 --- /dev/null +++ b/models/rust/src/2.3/files/free_bike_status.rs @@ -0,0 +1,76 @@ +#[cfg(feature = "pyo3")] +use pyo3::prelude::*; + +#[cfg(feature = "napi")] +use napi_derive::napi; + +use serde::{Deserialize, Serialize}; + +use crate::v2_3::types::*; + +file_struct!(FreeBikeStatusFile, FreeBikeStatusData); + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +/// Contains one object per vehicle that is currently deployed in the field. +pub struct FreeBikeStatusData { + pub bikes: Vec, +} + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +/// Vehicle that is currently deployed in the field +pub struct Bike { + /// Identifier of a vehicle. + pub bike_id: BikeID, + /// Latitude of the vehicle in decimal degrees. + pub lat: Option, + /// Longitude of the vehicle in decimal degrees. + pub lon: Option, + /// Is the vehicle currently reserved? + pub is_reserved: bool, + /// Is the vehicle currently disabled? + pub is_disabled: bool, + /// Rental URIs for Android, iOS, and web + pub rental_uris: Option, + /// The [VehicleType](crate::v2_3::files::vehicle_types::VehicleType) of this vehicle. + pub vehicle_type_id: Option, + /// The last time this vehicle reported its status to the operator's backend. + pub last_reported: Option, + /// This value represents the furthest distance in meters that the vehicle can travel with the vehicle's current charge or fuel (without recharging or refueling). + /// Note that in the case of carsharing, the given range is indicative and can be different from the one displayed on the vehicle's dashboard. + pub current_range_meters: Option, + /// This value represents the current percentage, expressed from 0 to 1, of fuel or battery power remaining in the vehicle. + pub current_fuel_percent: Option, + /// If the vehicle is currently at a [Station](crate::v2_3::files::station_information::Station). + pub station_id: Option, + /// The [Station](crate::v2_3::files::station_information::Station) where this vehicle must be returned to. + pub home_station_id: Option, + /// The [PricingPlan](crate::v2_3::files::system_pricing_plans::SystemPricingPlan) this vehicle is eligible for. + /// + /// If this field is defined it supersedes [default_pricing_plan_id](crate::v2_3::files::system_information::SystemInformation::default_pricing_plan_id). + pub pricing_plan_id: Option, + /// List of vehicle equipment provided by the operator in addition to the accessories already provided in the vehicle (field vehicle_accessories of vehicle_types.json) but subject to more frequent updates. + pub vehicle_equipment: Option>, + /// The date and time when any rental of the vehicle must be completed. + /// The vehicle must be returned and made available for the next user by this time. + /// If this field is empty, it indicates that the vehicle is available indefinitely. + pub available_until: Option, +} + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct RentalUris { + /// URI that can be passed to an Android app with an `android.intent.action.VIEW` Android intent to support Android Deep Links (https://developer.android.com/training/app-links/deep-linking). + pub android: Option, + /// URI that can be used on iOS to launch the rental app for this vehicle. + pub ios: Option, + /// URL that can be used by a web browser to show more information about renting a vehicle at this vehicle. + pub web: Option, +} diff --git a/models/rust/src/2.3/files/gbfs.rs b/models/rust/src/2.3/files/gbfs.rs new file mode 100644 index 0000000..00e7c52 --- /dev/null +++ b/models/rust/src/2.3/files/gbfs.rs @@ -0,0 +1,212 @@ +#[cfg(feature = "pyo3")] +use pyo3::prelude::*; + +#[cfg(feature = "napi")] +use napi_derive::napi; + +use futures::try_join; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +use super::super::GbfsObjects; + +use super::*; +use crate::v2_3::types::*; +use crate::v2_3::urls::*; + +file_struct!(GbfsFile, GbfsData); + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct GbfsData { + #[serde(flatten)] + pub feeds: HashMap, +} + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct GbfsLanguageFeeds { + pub feeds: Vec, +} + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct GbfsDataFeed { + /// Key identifying the type of feed this is. The key MUST be the base file name defined in the spec for the corresponding feed type + pub name: FeedType, + /// URL for the feed. Note that the actual feed endpoints (urls) may not be defined in the `file_name.json` format. + /// For example, a valid feed endpoint could end with `station_info` instead of `station_information.json`. + pub url: URL, +} + +impl GbfsData { + fn get_feed_url(&self, name: &str, language: &str) -> Option { + self.feeds + .get(language)? + .feeds + .iter() + .find(|feed| feed.name == name) + .map(|feed| feed.url.clone()) + } + + pub async fn get_objects(&self, language: &str) -> Result { + let free_bike_status = self.get_free_bike_status(language); + let station_information = self.get_station_information(language); + let station_status = self.get_station_status(language); + let system_pricing_plans = self.get_system_pricing_plans(language); + let vehicle_types = self.get_vehicle_types(language); + + let ( + free_bike_status, + station_information, + station_status, + system_pricing_plans, + vehicle_types, + ) = try_join!( + free_bike_status, + station_information, + station_status, + system_pricing_plans, + vehicle_types + ) + .unwrap(); + + let free_bike_status = free_bike_status.map(|f| f.data); + let station_information = station_information.map(|f| f.data); + let station_status = station_status.map(|f| f.data); + let system_pricing_plans = system_pricing_plans.map(|f| f.data); + let vehicle_types = vehicle_types.map(|f| f.data); + + Ok(GbfsObjects { + free_bike_status, + station_information, + station_status, + system_pricing_plans, + vehicle_types, + }) + } +} + +macro_rules! add_file_type { + ($fn_url_name: ident, $url_ty: ident, $fn_name: ident, $ty: ident, $name: expr) => { + impl GbfsData { + pub fn $fn_url_name(&self, language: &str) -> Option<$url_ty> { + let url = self.get_feed_url($name, language)?; + + Some($url_ty::new(&url)) + } + + pub async fn $fn_name(&self, language: &str) -> Result, reqwest::Error> { + let url = self.$fn_url_name(language); + + if let Some(url) = url { + Ok(Some(url.fetch_async().await?)) + } else { + Ok(None) + } + } + } + }; +} + +add_file_type!( + get_gbfs_versions_url, + GbfsVersionsFileUrl, + get_gbfs_versions, + GbfsVersionsFile, + "gbfs_versions" +); + +add_file_type!( + get_geofencing_zones_url, + GeofencingZonesFileUrl, + get_geofencing_zones, + GeofencingZonesFile, + "geofencing_zones" +); + +add_file_type!( + get_station_information_url, + StationInformationFileUrl, + get_station_information, + StationInformationFile, + "station_information" +); + +add_file_type!( + get_system_alerts_url, + SystemAlertsFileUrl, + get_system_alerts, + SystemAlertsFile, + "system_alerts" +); + +add_file_type!( + get_station_status_url, + StationStatusFileUrl, + get_station_status, + StationStatusFile, + "station_status" +); + +add_file_type!( + get_system_information_url, + SystemInformationFileUrl, + get_system_information, + SystemInformationFile, + "system_information" +); + +add_file_type!( + get_system_pricing_plans_url, + SystemPricingPlansFileUrl, + get_system_pricing_plans, + SystemPricingPlansFile, + "system_pricing_plans" +); + +add_file_type!( + get_system_hours_url, + SystemHoursFileUrl, + get_system_hours, + SystemHoursFile, + "system_hours" +); + +add_file_type!( + get_system_calendar_url, + SystemCalendarFileUrl, + get_system_calendar, + SystemCalendarFile, + "system_calendar" +); + +add_file_type!( + get_system_regions_url, + SystemRegionsFileUrl, + get_system_regions, + SystemRegionsFile, + "system_regions" +); + +add_file_type!( + get_free_bike_status_url, + FreeBikeStatusFileUrl, + get_free_bike_status, + FreeBikeStatusFile, + "free_bike_status" +); + +add_file_type!( + get_vehicle_types_url, + VehicleTypesFileUrl, + get_vehicle_types, + VehicleTypesFile, + "vehicle_types" +); diff --git a/models/rust/src/2.3/files/gbfs_versions.rs b/models/rust/src/2.3/files/gbfs_versions.rs new file mode 100644 index 0000000..3c8b531 --- /dev/null +++ b/models/rust/src/2.3/files/gbfs_versions.rs @@ -0,0 +1,33 @@ +#[cfg(feature = "pyo3")] +use pyo3::prelude::*; + +#[cfg(feature = "napi")] +use napi_derive::napi; + +use serde::{Deserialize, Serialize}; + +use crate::v2_3::types::*; +use crate::v2_3::urls::*; + +file_struct!(GbfsVersionsFile, GbfsVersionsData); + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct GbfsVersionsData { + /// Contains one object, as defined below, for each of the available versions of a feed. + /// The array MUST be sorted by increasing MAJOR and MINOR version number. + pub versions: Vec, +} + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct GbfsVersion { + /// The semantic version of the feed in the form `X.Y`. + pub version: String, + /// URL of the corresponding `gbfs.json` endpoint. + pub url: GbfsFileUrl, +} diff --git a/models/rust/src/2.3/files/geofencing_zones.rs b/models/rust/src/2.3/files/geofencing_zones.rs new file mode 100644 index 0000000..45996b8 --- /dev/null +++ b/models/rust/src/2.3/files/geofencing_zones.rs @@ -0,0 +1,95 @@ +#[cfg(feature = "pyo3")] +use pyo3::prelude::*; + +#[cfg(feature = "napi")] +use napi_derive::napi; + +use serde::{Deserialize, Serialize}; + +file_struct!(GeofencingZonesFile, GeofencingZonesData); + +use crate::v2_3::types::*; + +#[cfg(doc)] +use crate::v2_3::files::geofencing_zones; +#[cfg(doc)] +use crate::v2_3::files::station_information; + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct GeofencingZonesData { + pub geofencing_zones: GeofencingZone, +} + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +/// geofenced zone and its associated rules and attributes +pub struct GeofencingZone { + /// `FeatureCollection` (as per IETF RFC 7946). + pub r#type: String, + pub features: Vec, +} + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Feature { + /// `Feature` (as per IETF RFC 7946). + pub r#type: String, + /// A polygon that describes where rides may or may not be able to start, end, go through, or have other limitations or affordances. + /// Rules may only apply to the interior of a polygon. + /// All geofencing zones contained in this list are public (meaning they can be displayed on a map for public use). + pub geometry: geo_json::MultiPolygon, + /// Properties describing travel allowances and limitations. + pub properties: Properties, +} + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Properties { + /// Public name of the geofencing zone. + pub name: Option, + /// Start time of the geofencing zone. If the geofencing zone is always active, this can be omitted. + pub start: Option, + /// End time of the geofencing zone. If the geofencing zone is always active, this can be omitted. + pub end: Option, + /// Restrictions that apply within the area of the polygon. + pub rules: Option>, +} + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +/// Geofencing Rule Precedence +/// +/// Geofencing Rule objects are specified within arrays for the rules and [global_rules](GeofencingZonesData::global_rules) fields of [geofencing_zones] to allow for different restrictions for different vehicle types. +/// When multiple rules in the same array apply to a particular vehicle type, per the semantics of the vehicle_type_ids field, then the earlier rule (in order of the JSON file) takes precedence for that vehicle type. +/// +/// When multiple overlapping polygons define rules that apply to a particular vehicle type, +/// then the rules from the earlier polygon (in order of the JSON file) takes precedence for that vehicle type in the overlapping area. +/// Polygons with inactive time ranges should be excluded from consideration when considering precedence. +/// +/// When a polygon and the [global_rules](GeofencingZonesData::global_rules) field define rules that apply to a particular vehicle type, then the rules from the polygon take precedence for that vehicle type in the area of the polygon. +pub struct Rule { + /// Array of IDs of vehicle types for which any restrictions SHOULD be applied. + /// If vehicle type IDs are not specified, then restrictions apply to all vehicle types. + pub vehicle_type_id: Option>, + /// Is the ride allowed to start and end in this zone? + pub ride_allowed: bool, + /// Is the ride allowed to travel through this zone? + pub ride_through_allowed: bool, + /// What is the maximum speed allowed, in kilometers per hour? + /// + /// If there is no maximum speed to observe, this can be omitted. + pub maximum_speed_kph: Option, + /// Can vehicles only be parked at stations defined in [station_information] within this geofence zone? + pub station_parking: Option, +} diff --git a/models/rust/src/2.3/files/mod.rs b/models/rust/src/2.3/files/mod.rs new file mode 100644 index 0000000..901281c --- /dev/null +++ b/models/rust/src/2.3/files/mod.rs @@ -0,0 +1,169 @@ +macro_rules! file_struct { + ( $ty: ident, $data: ty ) => { + #[cfg_attr(feature = "napi", napi(object))] + #[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] + #[serde_with::skip_serializing_none] + #[derive(Serialize, Deserialize, Debug, Clone)] + pub struct $ty { + /// Indicates the last time data in the feed was updated. This timestamp represents the publisher's knowledge of the current state of the system at this point in time. + pub last_updated: Timestamp, + /// Number of seconds before the data in the feed will be updated again (0 if the data should always be refreshed). + pub ttl: u32, + /// GBFS version number to which the feed conforms, according to the versioning framework. + pub version: String, + /// Response data. + pub data: $data, + } + }; +} + +pub mod free_bike_status; +pub mod gbfs; +pub mod gbfs_versions; +pub mod geofencing_zones; +pub mod station_information; +pub mod station_status; +pub mod system_alerts; +pub mod system_calendar; +pub mod system_hours; +pub mod system_information; +pub mod system_pricing_plans; +pub mod system_regions; +pub mod vehicle_types; + +pub use self::free_bike_status::{FreeBikeStatusData, FreeBikeStatusFile}; +pub use self::gbfs::{GbfsData, GbfsFile}; +pub use self::gbfs_versions::{GbfsVersionsData, GbfsVersionsFile}; +pub use self::geofencing_zones::{GeofencingZonesData, GeofencingZonesFile}; +pub use self::station_information::{StationInformationData, StationInformationFile}; +pub use self::station_status::{StationStatusData, StationStatusFile}; +pub use self::system_alerts::{SystemAlertsData, SystemAlertsFile}; +pub use self::system_calendar::{SystemCalendarData, SystemCalendarFile}; +pub use self::system_hours::{SystemHoursData, SystemHoursFile}; +pub use self::system_information::{SystemInformationData, SystemInformationFile}; +pub use self::system_pricing_plans::{SystemPricingPlansData, SystemPricingPlansFile}; +pub use self::system_regions::{SystemRegionsData, SystemRegionsFile}; +pub use self::vehicle_types::{VehicleTypesData, VehicleTypesFile}; + +#[cfg(test)] +mod tests { + use pretty_assertions::assert_eq; + + fn test_file(example: &str) { + let json_value: serde_json::Value = serde_json::from_str(example).unwrap(); + + let file: T = serde_json::from_str(example).unwrap(); + + let json_value2: serde_json::Value = + serde_json::from_str(&serde_json::to_string(&file).unwrap()).unwrap(); + + assert_eq!(json_value, json_value2); + } + + #[test] + fn gbfs() { + let gbfs = include_str!("./examples/specification/gbfs.json"); + + test_file::(gbfs); + } + + #[test] + fn gbfs_versions() { + let gbfs_versions = include_str!("./examples/specification/gbfs_versions.json"); + + test_file::(gbfs_versions); + } + + #[test] + fn system_information() { + let system_information = include_str!("./examples/specification/system_information.json"); + + test_file::(system_information); + } + + #[test] + fn station_information() { + let station_information = + include_str!("./examples/specification/station_information-1.json"); + + test_file::(station_information); + + let station_information = + include_str!("./examples/specification/station_information-2.json"); + + test_file::(station_information); + } + + #[test] + fn vehicle_types() { + let vehicle_types = include_str!("./examples/specification/vehicle_types.json"); + + test_file::(vehicle_types); + } + + #[test] + fn station_status() { + let station_status = include_str!("./examples/specification/station_status.json"); + + test_file::(station_status); + } + + #[test] + fn free_bike_status() { + let free_bike_status = include_str!("./examples/specification/free_bike_status-1.json"); + + test_file::(free_bike_status); + + let free_bike_status = include_str!("./examples/specification/free_bike_status-2.json"); + + test_file::(free_bike_status); + } + + #[test] + fn system_hours() { + let system_hours = include_str!("./examples/specification/system_hours.json"); + + test_file::(system_hours); + } + + #[test] + fn system_calendar() { + let system_calendar = include_str!("./examples/specification/system_calendar.json"); + + test_file::(system_calendar); + } + + #[test] + fn system_regions() { + let system_regions = include_str!("./examples/specification/system_regions.json"); + + test_file::(system_regions); + } + + #[test] + fn system_alerts() { + let system_alerts = include_str!("./examples/specification/system_alerts.json"); + + test_file::(system_alerts); + } + + #[test] + fn system_pricing_plans() { + let system_pricing_plans = + include_str!("./examples/specification/system_pricing_plans-1.json"); + + test_file::(system_pricing_plans); + + let system_pricing_plans = + include_str!("./examples/specification/system_pricing_plans-2.json"); + + test_file::(system_pricing_plans); + } + + #[test] + fn geofencing_zones() { + let geofencing_zones = include_str!("./examples/specification/geofencing_zones.json"); + + test_file::(geofencing_zones); + } +} diff --git a/models/rust/src/2.3/files/station_information.rs b/models/rust/src/2.3/files/station_information.rs new file mode 100644 index 0000000..a756bd1 --- /dev/null +++ b/models/rust/src/2.3/files/station_information.rs @@ -0,0 +1,106 @@ +#[cfg(feature = "pyo3")] +use pyo3::prelude::*; + +#[cfg(feature = "napi")] +use napi_derive::napi; + +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +use crate::v2_3::types::*; + +file_struct!(StationInformationFile, StationInformationData); + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +/// Contains one object per station in the system as defined below. +pub struct StationInformationData { + pub stations: Vec, +} + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Station { + /// Identifier of a station. + pub station_id: StationID, + /// The public name of the station for display in maps, digital signage, and other text applications. + /// Names SHOULD reflect the station location through the use of a cross street or local landmark. + /// Abbreviations SHOULD NOT be used for names and other text (for example, "St." for "Street") unless a location is called by its abbreviated name (for example, “JFK Airport”). + /// + /// Examples: + /// + /// - Broadway and East 22nd Street + /// - Convention Center + /// - Central Park South + pub name: Option, + /// Short name or other type of identifier. + pub short_name: Option, + /// Latitude of the station in decimal degrees. + /// This field SHOULD have a precision of 6 decimal places (0.000001). + pub lat: Latitude, + /// Longitude of the station in decimal degrees. + /// This field SHOULD have a precision of 6 decimal places (0.000001). + pub lon: Longitude, + /// Address (street number and name) where station is located. + /// This MUST be a valid address, not a free-form text description. + /// + /// Example: 1234 Main Street + pub address: Option, + /// Cross street or landmark where the station is located. + pub cross_street: Option, + /// Identifier of the region where station is located. + pub region_id: Option, + /// Postal code where station is located. + pub post_code: Option, + /// Payment methods accepted at this station. + pub rental_methods: Option>, + /// Is this station a location with or without smart dock technology? + /// + /// - `true`: The station is a location without smart docking infrastructure. the station may be defined by a point (lat/lon) and/or station_area (below). + /// - `false`: The station consists of smart docking infrastructure (docks). + pub is_virtual_station: Option, + /// A GeoJSON MultiPolygon that describes the area of a virtual station. + /// + /// If `station_area` is supplied, then the record describes a virtual station. + /// + /// If `lat`/`lon` and `station_area` are both defined, the `lat`/`lon` is the significant coordinate of the station (for example, parking facility or valet drop-off and pick up point). + /// The station_area takes precedence over any `ride_start_allowed` and `ride_end_allowed` rules in overlapping `geofencing_zones`. + pub station_area: Option, + /// Type of parking station. + pub parking_type: Option, + /// Are parking hoops present at this station? + pub parking_hoop: Option, + /// Contact phone of the station. + pub contact_phone: Option, + /// Number of total docking points installed at this station, both available and unavailable, regardless of what vehicle types are allowed at each dock. + pub capacity: Option, + /// Used to model the parking capacity of virtual stations (defined using the is_virtual_station field) for each vehicle type that can be returned to this station. + /// The total number of vehicles from each of these objects SHOULD add up to match the value specified in the `capacity` field. + pub vehicle_capacity: Option>, + /// These objects are used to model the total docking capacity of a station, both available and unavailable, for each type of vehicle that may dock at this station. + /// The total number of docks from each of these objects SHOULD add up to match the value specified in the `capacity` field. + pub vehicle_type_capacity: Option>, + /// Are valet services provided at this station? + pub is_valet_station: Option, + /// Does the station support charging of electric vehicles? + pub is_charging_station: Option, + /// Contains rental URIs for Android, iOS, and web + pub rental_uris: Option, +} + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct RentalUris { + /// URI that can be passed to an Android app with an `android.intent.action.VIEW` Android intent to support Android Deep Links (). + pub android: Option, + /// URI that can be used on iOS to launch the rental app for this station. + pub ios: Option, + /// URL that can be used by a web browser to show more information about renting a vehicle at this station. + pub web: Option, +} diff --git a/models/rust/src/2.3/files/station_status.rs b/models/rust/src/2.3/files/station_status.rs new file mode 100644 index 0000000..c313c29 --- /dev/null +++ b/models/rust/src/2.3/files/station_status.rs @@ -0,0 +1,81 @@ +#[cfg(feature = "pyo3")] +use pyo3::prelude::*; + +#[cfg(feature = "napi")] +use napi_derive::napi; + +use serde::{Deserialize, Serialize}; + +use crate::v2_3::types::*; + +file_struct!(StationStatusFile, StationStatusData); + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +/// Contains one object per station in the system as defined below. +pub struct StationStatusData { + pub stations: Vec, +} + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Station { + /// Identifier of a station. + pub station_id: StationID, + /// Number of functional vehicles physically at the station that may be offered for rental. To know if the vehicles are available for rental, see [is_renting](Station::is_renting). + pub num_bikes_available: u32, + /// Used to model the total number of each defined vehicle type available at a station. + /// The total number of vehicles from each of these objects SHOULD add up to match the value specified in the [num_vehicles_available](Station::num_vehicles_available) field. + pub vehicle_types_available: Option>, + /// Number of disabled vehicles of any type at the station. + pub num_bikes_disabled: Option, + /// Number of functional docks physically at the station that are able to accept vehicles for return. To know if the docks are accepting vehicle returns, see [is_returning](Station::is_returning). + pub num_docks_available: Option, + /// Used to model the number of docks available for certain vehicle types. + pub vehicle_docks_available: Option>, + /// Number of disabled dock points at the station. + pub num_docks_disabled: Option, + /// Is the station currently on the street? + /// + /// In seasonal systems where equipment is removed during winter, boolean SHOULD be set to `false` during the off season. + /// May also be set to `false` to indicate planned (future) stations which have not yet been installed. + pub is_installed: bool, + /// Is the station currently renting vehicles? + /// + /// If the station is temporarily taken out of service and not allowing rentals, this field MUST be set to `false`. + /// + /// If a station becomes inaccessible to users due to road construction or other factors this field SHOULD be set to `false`. + /// Field SHOULD be set to `false` during hours or days when the system is not offering vehicles for rent. + pub is_renting: bool, + /// Is the station accepting vehicle returns? + /// + /// If the station is temporarily taken out of service and not allowing vehicle returns, this field MUST be set to `false`. + /// If a station becomes inaccessible to users due to road construction or other factors, this field SHOULD be set to `false`. + pub is_returning: bool, + /// The last time this station reported its status to the operator's backend. + pub last_reported: Option, +} + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct VehicleTypeAvailable { + pub vehicle_type_id: VehicleTypeID, + /// A number representing the total number of available vehicles of this type at this station. + pub count: u32, +} + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct VehicleDockAvailable { + pub vehicle_type_ids: Vec, + /// A number representing the number of docks available for those vehicle types at this station. + pub count: u32, +} diff --git a/models/rust/src/2.3/files/system_alerts.rs b/models/rust/src/2.3/files/system_alerts.rs new file mode 100644 index 0000000..bc7bc27 --- /dev/null +++ b/models/rust/src/2.3/files/system_alerts.rs @@ -0,0 +1,55 @@ +#[cfg(feature = "pyo3")] +use pyo3::prelude::*; + +#[cfg(feature = "napi")] +use napi_derive::napi; + +use serde::{Deserialize, Serialize}; + +use crate::v2_3::types::*; + +file_struct!(SystemAlertsFile, SystemAlertsData); + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct SystemAlertsData { + pub alerts: Vec, +} + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct SystemAlert { + /// Identifier for this alert. + pub alert_id: AlertID, + pub r#type: String, + /// Indicating when the alert is in effect (for example, when the system or station is actually closed, or when a station is scheduled to be moved). + pub times: Option>, + /// If this is an alert that affects one or more stations, include their ID(s). Otherwise omit this field. + /// If both [station_ids][SystemAlert::station_ids] and [region_ids][SystemAlert::region_ids] are omitted, this alert affects the entire system. + pub station_ids: Option>, + /// If this system has regions, and if this alert only affects certain regions, include their ID(s). + pub region_ids: Option>, + /// URL where the customer can learn more information about this alert. + pub url: Option, + /// A short summary of this alert to be displayed to the customer. + pub summary: Option, + /// Detailed description of the alert. + pub description: Option, + /// Indicates the last time the info for the alert was updated. + pub last_updated: Option, +} + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct AlertTime { + /// Start time of the alert. + pub start: Timestamp, + /// End time of the alert. If there is currently no end time planned for the alert, this can be omitted. + pub end: Option, +} diff --git a/models/rust/src/2.3/files/system_calendar.rs b/models/rust/src/2.3/files/system_calendar.rs new file mode 100644 index 0000000..c0a1704 --- /dev/null +++ b/models/rust/src/2.3/files/system_calendar.rs @@ -0,0 +1,32 @@ +#[cfg(feature = "pyo3")] +use pyo3::prelude::*; + +#[cfg(feature = "napi")] +use napi_derive::napi; + +use serde::{Deserialize, Serialize}; + +use crate::v2_3::types::*; + +file_struct!(SystemCalendarFile, SystemCalendarData); + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct SystemCalendarData { + pub calendars: Vec, +} + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Calendar { + pub start_month: Option, + pub start_day: Option, + pub start_year: Option, + pub end_month: Option, + pub end_day: Option, + pub end_year: Option, +} diff --git a/models/rust/src/2.3/files/system_hours.rs b/models/rust/src/2.3/files/system_hours.rs new file mode 100644 index 0000000..089da0e --- /dev/null +++ b/models/rust/src/2.3/files/system_hours.rs @@ -0,0 +1,30 @@ +#[cfg(feature = "pyo3")] +use pyo3::prelude::*; + +#[cfg(feature = "napi")] +use napi_derive::napi; + +use serde::{Deserialize, Serialize}; + +use crate::v2_3::types::*; + +file_struct!(SystemHoursFile, SystemHoursData); + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct SystemHoursData { + pub rental_hours: Vec, +} + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct RentalHours { + pub user_types: Option>, + pub days: Option>, + pub start_time: Option