1use core::ffi::CStr;
7use core::str::FromStr;
8
9use crate::{GameSchemaAvailability, UnknownStringValue, game};
10
11const INITIAL: GameSchemaAvailability =
15 GameSchemaAvailability::new(Some(game::ets2::V1_00), Some(game::ats::V1_00));
16const ETS2_1_01_ATS_1_00: GameSchemaAvailability =
17 GameSchemaAvailability::new(Some(game::ets2::V1_01), Some(game::ats::V1_00));
18const ETS2_1_09_ATS_1_00: GameSchemaAvailability =
19 GameSchemaAvailability::new(Some(game::ets2::V1_09), Some(game::ats::V1_00));
20const ETS2_1_10_ATS_1_00: GameSchemaAvailability =
21 GameSchemaAvailability::new(Some(game::ets2::V1_10), Some(game::ats::V1_00));
22const ETS2_1_12_ATS_1_00: GameSchemaAvailability =
23 GameSchemaAvailability::new(Some(game::ets2::V1_12), Some(game::ats::V1_00));
24const ETS2_1_14_ATS_1_01: GameSchemaAvailability =
25 GameSchemaAvailability::new(Some(game::ets2::V1_14), Some(game::ats::V1_01));
26const ETS2_1_15_ATS_1_02: GameSchemaAvailability =
27 GameSchemaAvailability::new(Some(game::ets2::V1_15), Some(game::ats::V1_02));
28
29pub const MAX_TRAILERS: usize = crate::TrailerIndex::COUNT;
31
32pub mod ids {
34 use super::{ETS2_1_09_ATS_1_00, INITIAL};
35 use crate::ConfigurationId;
36
37 pub const COUNT: usize = 6;
39
40 pub const SUBSTANCES: ConfigurationId = ConfigurationId::new(c"substances", INITIAL);
50
51 pub const CONTROLS: ConfigurationId = ConfigurationId::new(c"controls", INITIAL);
55
56 pub const HSHIFTER: ConfigurationId = ConfigurationId::new(c"hshifter", INITIAL);
69
70 pub const TRUCK: ConfigurationId = ConfigurationId::new(c"truck", INITIAL);
101
102 pub const TRAILER: ConfigurationId = ConfigurationId::new(c"trailer", INITIAL);
128
129 pub const JOB: ConfigurationId = ConfigurationId::new(c"job", ETS2_1_09_ATS_1_00);
152
153 pub const ALL: [ConfigurationId; COUNT] = [SUBSTANCES, CONTROLS, HSHIFTER, TRUCK, TRAILER, JOB];
159}
160
161pub mod attributes {
163 use super::{
164 ETS2_1_01_ATS_1_00, ETS2_1_09_ATS_1_00, ETS2_1_10_ATS_1_00, ETS2_1_12_ATS_1_00,
165 ETS2_1_14_ATS_1_01, ETS2_1_15_ATS_1_02, INITIAL,
166 };
167 use crate::{AnyAttribute, Attribute};
168
169 pub const COUNT: usize = 60;
171
172 pub const BRAND_ID: Attribute<crate::StringValue> = Attribute::new(c"brand_id", INITIAL);
178
179 pub const BRAND: Attribute<crate::StringValue> = Attribute::new(c"brand", INITIAL);
185
186 pub const ID: Attribute<crate::StringValue> = Attribute::new(c"id", INITIAL);
192
193 pub const CARGO_ACCESSORY_ID: Attribute<crate::StringValue> =
199 Attribute::new(c"cargo.accessory.id", INITIAL);
200
201 pub const CHAIN_TYPE: Attribute<crate::StringValue> =
207 Attribute::new(c"chain.type", ETS2_1_14_ATS_1_01);
208
209 pub const BODY_TYPE: Attribute<crate::StringValue> =
215 Attribute::new(c"body.type", ETS2_1_14_ATS_1_01);
216
217 pub const LICENSE_PLATE: Attribute<crate::StringValue> =
221 Attribute::new(c"license.plate", ETS2_1_14_ATS_1_01);
222
223 pub const LICENSE_PLATE_COUNTRY_ID: Attribute<crate::StringValue> =
227 Attribute::new(c"license.plate.country.id", ETS2_1_14_ATS_1_01);
228
229 pub const LICENSE_PLATE_COUNTRY: Attribute<crate::StringValue> =
233 Attribute::new(c"license.plate.country", ETS2_1_14_ATS_1_01);
234
235 pub const NAME: Attribute<crate::StringValue> = Attribute::new(c"name", INITIAL);
241
242 pub const FUEL_CAPACITY: Attribute<f32> = Attribute::new(c"fuel.capacity", INITIAL);
246
247 pub const FUEL_WARNING_FACTOR: Attribute<f32> = Attribute::new(c"fuel.warning.factor", INITIAL);
252
253 pub const ADBLUE_CAPACITY: Attribute<f32> =
257 Attribute::new(c"adblue.capacity", ETS2_1_12_ATS_1_00);
258
259 pub const ADBLUE_WARNING_FACTOR: Attribute<f32> =
264 Attribute::new(c"adblue.warning.factor", ETS2_1_12_ATS_1_00);
265
266 pub const AIR_PRESSURE_WARNING: Attribute<f32> =
271 Attribute::new(c"brake.air.pressure.warning", INITIAL);
272
273 pub const AIR_PRESSURE_EMERGENCY: Attribute<f32> =
278 Attribute::new(c"brake.air.pressure.emergency", ETS2_1_01_ATS_1_00);
279
280 pub const OIL_PRESSURE_WARNING: Attribute<f32> =
284 Attribute::new(c"oil.pressure.warning", INITIAL);
285
286 pub const WATER_TEMPERATURE_WARNING: Attribute<f32> =
290 Attribute::new(c"water.temperature.warning", INITIAL);
291
292 pub const BATTERY_VOLTAGE_WARNING: Attribute<f32> =
296 Attribute::new(c"battery.voltage.warning", INITIAL);
297
298 pub const RPM_LIMIT: Attribute<f32> = Attribute::new(c"rpm.limit", INITIAL);
302
303 pub const FORWARD_GEAR_COUNT: Attribute<u32> = Attribute::new(c"gears.forward", INITIAL);
307
308 pub const REVERSE_GEAR_COUNT: Attribute<u32> = Attribute::new(c"gears.reverse", INITIAL);
312
313 pub const DIFFERENTIAL_RATIO: Attribute<f32> =
317 Attribute::new(c"differential.ratio", ETS2_1_12_ATS_1_00);
318
319 pub const RETARDER_STEP_COUNT: Attribute<u32> = Attribute::new(c"retarder.steps", INITIAL);
325
326 pub const FORWARD_RATIO: Attribute<f32> =
330 Attribute::indexed(c"forward.ratio", ETS2_1_12_ATS_1_00);
331
332 pub const REVERSE_RATIO: Attribute<f32> =
336 Attribute::indexed(c"reverse.ratio", ETS2_1_12_ATS_1_00);
337
338 pub const CABIN_POSITION: Attribute<crate::FVector> =
346 Attribute::new(c"cabin.position", INITIAL);
347
348 pub const HEAD_POSITION: Attribute<crate::FVector> = Attribute::new(c"head.position", INITIAL);
352
353 pub const HOOK_POSITION: Attribute<crate::FVector> = Attribute::new(c"hook.position", INITIAL);
358
359 pub const WHEEL_COUNT: Attribute<u32> = Attribute::new(c"wheels.count", INITIAL);
363
364 pub const WHEEL_POSITION: Attribute<crate::FVector> =
368 Attribute::indexed(c"wheel.position", INITIAL);
369
370 pub const WHEEL_STEERABLE: Attribute<bool> = Attribute::indexed(c"wheel.steerable", INITIAL);
374
375 pub const WHEEL_SIMULATED: Attribute<bool> = Attribute::indexed(c"wheel.simulated", INITIAL);
379
380 pub const WHEEL_RADIUS: Attribute<f32> = Attribute::indexed(c"wheel.radius", INITIAL);
384
385 pub const WHEEL_POWERED: Attribute<bool> =
389 Attribute::indexed(c"wheel.powered", ETS2_1_10_ATS_1_00);
390
391 pub const WHEEL_LIFTABLE: Attribute<bool> =
395 Attribute::indexed(c"wheel.liftable", ETS2_1_10_ATS_1_00);
396
397 pub const SELECTOR_COUNT: Attribute<u32> = Attribute::new(c"selector.count", INITIAL);
401
402 pub const SLOT_GEAR: Attribute<i32> = Attribute::indexed(c"slot.gear", INITIAL);
406
407 pub const SLOT_HANDLE_POSITION: Attribute<u32> =
414 Attribute::indexed(c"slot.handle.position", INITIAL);
415
416 pub const SLOT_SELECTORS: Attribute<u32> = Attribute::indexed(c"slot.selectors", INITIAL);
422
423 pub const SHIFTER_TYPE: Attribute<crate::StringValue> =
429 Attribute::new(c"shifter.type", INITIAL);
430
431 pub const CARGO_ID: Attribute<crate::StringValue> =
437 Attribute::new(c"cargo.id", ETS2_1_09_ATS_1_00);
438
439 pub const CARGO: Attribute<crate::StringValue> = Attribute::new(c"cargo", ETS2_1_09_ATS_1_00);
445
446 pub const CARGO_MASS: Attribute<f32> = Attribute::new(c"cargo.mass", ETS2_1_09_ATS_1_00);
450
451 pub const CARGO_UNIT_MASS: Attribute<f32> =
455 Attribute::new(c"cargo.unit.mass", ETS2_1_14_ATS_1_01);
456
457 pub const CARGO_UNIT_COUNT: Attribute<u32> =
461 Attribute::new(c"cargo.unit.count", ETS2_1_14_ATS_1_01);
462
463 pub const DESTINATION_CITY_ID: Attribute<crate::StringValue> =
469 Attribute::new(c"destination.city.id", ETS2_1_09_ATS_1_00);
470
471 pub const DESTINATION_CITY: Attribute<crate::StringValue> =
477 Attribute::new(c"destination.city", ETS2_1_09_ATS_1_00);
478
479 pub const DESTINATION_COMPANY_ID: Attribute<crate::StringValue> =
485 Attribute::new(c"destination.company.id", ETS2_1_09_ATS_1_00);
486
487 pub const DESTINATION_COMPANY: Attribute<crate::StringValue> =
493 Attribute::new(c"destination.company", ETS2_1_09_ATS_1_00);
494
495 pub const SOURCE_CITY_ID: Attribute<crate::StringValue> =
501 Attribute::new(c"source.city.id", ETS2_1_09_ATS_1_00);
502
503 pub const SOURCE_CITY: Attribute<crate::StringValue> =
509 Attribute::new(c"source.city", ETS2_1_09_ATS_1_00);
510
511 pub const SOURCE_COMPANY_ID: Attribute<crate::StringValue> =
517 Attribute::new(c"source.company.id", ETS2_1_09_ATS_1_00);
518
519 pub const SOURCE_COMPANY: Attribute<crate::StringValue> =
525 Attribute::new(c"source.company", ETS2_1_09_ATS_1_00);
526
527 pub const INCOME: Attribute<u64> = Attribute::new(c"income", ETS2_1_09_ATS_1_00);
534
535 pub const DELIVERY_TIME: Attribute<u32> = Attribute::new(c"delivery.time", ETS2_1_09_ATS_1_00);
544
545 pub const PLANNED_DISTANCE_KM: Attribute<u32> =
551 Attribute::new(c"planned_distance.km", ETS2_1_15_ATS_1_02);
552
553 pub const IS_CARGO_LOADED: Attribute<bool> =
559 Attribute::new(c"cargo.loaded", ETS2_1_14_ATS_1_01);
560
561 pub const JOB_MARKET: Attribute<crate::StringValue> =
573 Attribute::new(c"job.market", ETS2_1_14_ATS_1_01);
574
575 pub const SPECIAL_JOB: Attribute<bool> = Attribute::new(c"is.special.job", ETS2_1_14_ATS_1_01);
579
580 pub const ALL: [AnyAttribute; COUNT] = [
587 BRAND_ID.erase(),
588 BRAND.erase(),
589 ID.erase(),
590 CARGO_ACCESSORY_ID.erase(),
591 CHAIN_TYPE.erase(),
592 BODY_TYPE.erase(),
593 LICENSE_PLATE.erase(),
594 LICENSE_PLATE_COUNTRY_ID.erase(),
595 LICENSE_PLATE_COUNTRY.erase(),
596 NAME.erase(),
597 FUEL_CAPACITY.erase(),
598 FUEL_WARNING_FACTOR.erase(),
599 ADBLUE_CAPACITY.erase(),
600 ADBLUE_WARNING_FACTOR.erase(),
601 AIR_PRESSURE_WARNING.erase(),
602 AIR_PRESSURE_EMERGENCY.erase(),
603 OIL_PRESSURE_WARNING.erase(),
604 WATER_TEMPERATURE_WARNING.erase(),
605 BATTERY_VOLTAGE_WARNING.erase(),
606 RPM_LIMIT.erase(),
607 FORWARD_GEAR_COUNT.erase(),
608 REVERSE_GEAR_COUNT.erase(),
609 DIFFERENTIAL_RATIO.erase(),
610 RETARDER_STEP_COUNT.erase(),
611 FORWARD_RATIO.erase(),
612 REVERSE_RATIO.erase(),
613 CABIN_POSITION.erase(),
614 HEAD_POSITION.erase(),
615 HOOK_POSITION.erase(),
616 WHEEL_COUNT.erase(),
617 WHEEL_POSITION.erase(),
618 WHEEL_STEERABLE.erase(),
619 WHEEL_SIMULATED.erase(),
620 WHEEL_RADIUS.erase(),
621 WHEEL_POWERED.erase(),
622 WHEEL_LIFTABLE.erase(),
623 SELECTOR_COUNT.erase(),
624 SLOT_GEAR.erase(),
625 SLOT_HANDLE_POSITION.erase(),
626 SLOT_SELECTORS.erase(),
627 SHIFTER_TYPE.erase(),
628 CARGO_ID.erase(),
629 CARGO.erase(),
630 CARGO_MASS.erase(),
631 CARGO_UNIT_MASS.erase(),
632 CARGO_UNIT_COUNT.erase(),
633 DESTINATION_CITY_ID.erase(),
634 DESTINATION_CITY.erase(),
635 DESTINATION_COMPANY_ID.erase(),
636 DESTINATION_COMPANY.erase(),
637 SOURCE_CITY_ID.erase(),
638 SOURCE_CITY.erase(),
639 SOURCE_COMPANY_ID.erase(),
640 SOURCE_COMPANY.erase(),
641 INCOME.erase(),
642 DELIVERY_TIME.erase(),
643 PLANNED_DISTANCE_KM.erase(),
644 IS_CARGO_LOADED.erase(),
645 JOB_MARKET.erase(),
646 SPECIAL_JOB.erase(),
647 ];
648}
649
650pub mod associations {
659 use super::{
660 ETS2_1_01_ATS_1_00, ETS2_1_09_ATS_1_00, ETS2_1_10_ATS_1_00, ETS2_1_12_ATS_1_00,
661 ETS2_1_14_ATS_1_01, ETS2_1_15_ATS_1_02, INITIAL, attributes, ids,
662 };
663 use crate::{
664 Attribute, ConfigurationAttributeAssociation, ConfigurationId, GameSchemaAvailability,
665 SdkValue,
666 };
667
668 pub const COUNT: usize = 71;
674
675 const fn association<T: SdkValue>(
676 configuration: ConfigurationId,
677 attribute: Attribute<T>,
678 availability: GameSchemaAvailability,
679 ) -> ConfigurationAttributeAssociation {
680 ConfigurationAttributeAssociation::new(configuration, attribute, availability)
681 }
682
683 pub const SUBSTANCES: [ConfigurationAttributeAssociation; 1] =
685 [association(ids::SUBSTANCES, attributes::ID, INITIAL)];
686
687 pub const CONTROLS: [ConfigurationAttributeAssociation; 1] = [association(
689 ids::CONTROLS,
690 attributes::SHIFTER_TYPE,
691 INITIAL,
692 )];
693
694 pub const HSHIFTER: [ConfigurationAttributeAssociation; 4] = [
696 association(ids::HSHIFTER, attributes::SELECTOR_COUNT, INITIAL),
697 association(ids::HSHIFTER, attributes::SLOT_GEAR, INITIAL),
698 association(ids::HSHIFTER, attributes::SLOT_HANDLE_POSITION, INITIAL),
699 association(ids::HSHIFTER, attributes::SLOT_SELECTORS, INITIAL),
700 ];
701
702 pub const TRUCK: [ConfigurationAttributeAssociation; 33] = [
711 association(ids::TRUCK, attributes::BRAND_ID, INITIAL),
712 association(ids::TRUCK, attributes::BRAND, INITIAL),
713 association(ids::TRUCK, attributes::ID, INITIAL),
714 association(ids::TRUCK, attributes::NAME, INITIAL),
715 association(ids::TRUCK, attributes::FUEL_CAPACITY, INITIAL),
716 association(ids::TRUCK, attributes::FUEL_WARNING_FACTOR, INITIAL),
717 association(ids::TRUCK, attributes::ADBLUE_CAPACITY, ETS2_1_12_ATS_1_00),
718 association(
719 ids::TRUCK,
720 attributes::ADBLUE_WARNING_FACTOR,
721 ETS2_1_12_ATS_1_00,
722 ),
723 association(ids::TRUCK, attributes::AIR_PRESSURE_WARNING, INITIAL),
724 association(
725 ids::TRUCK,
726 attributes::AIR_PRESSURE_EMERGENCY,
727 ETS2_1_01_ATS_1_00,
728 ),
729 association(ids::TRUCK, attributes::OIL_PRESSURE_WARNING, INITIAL),
730 association(ids::TRUCK, attributes::WATER_TEMPERATURE_WARNING, INITIAL),
731 association(ids::TRUCK, attributes::BATTERY_VOLTAGE_WARNING, INITIAL),
732 association(ids::TRUCK, attributes::RPM_LIMIT, INITIAL),
733 association(ids::TRUCK, attributes::FORWARD_GEAR_COUNT, INITIAL),
734 association(ids::TRUCK, attributes::REVERSE_GEAR_COUNT, INITIAL),
735 association(
736 ids::TRUCK,
737 attributes::DIFFERENTIAL_RATIO,
738 ETS2_1_12_ATS_1_00,
739 ),
740 association(ids::TRUCK, attributes::RETARDER_STEP_COUNT, INITIAL),
741 association(ids::TRUCK, attributes::FORWARD_RATIO, ETS2_1_12_ATS_1_00),
742 association(ids::TRUCK, attributes::REVERSE_RATIO, ETS2_1_12_ATS_1_00),
743 association(ids::TRUCK, attributes::CABIN_POSITION, INITIAL),
744 association(ids::TRUCK, attributes::HEAD_POSITION, INITIAL),
745 association(ids::TRUCK, attributes::HOOK_POSITION, INITIAL),
746 association(ids::TRUCK, attributes::LICENSE_PLATE, ETS2_1_14_ATS_1_01),
747 association(
748 ids::TRUCK,
749 attributes::LICENSE_PLATE_COUNTRY,
750 ETS2_1_14_ATS_1_01,
751 ),
752 association(
753 ids::TRUCK,
754 attributes::LICENSE_PLATE_COUNTRY_ID,
755 ETS2_1_14_ATS_1_01,
756 ),
757 association(ids::TRUCK, attributes::WHEEL_COUNT, INITIAL),
758 association(ids::TRUCK, attributes::WHEEL_POSITION, INITIAL),
759 association(ids::TRUCK, attributes::WHEEL_STEERABLE, INITIAL),
760 association(ids::TRUCK, attributes::WHEEL_SIMULATED, INITIAL),
761 association(ids::TRUCK, attributes::WHEEL_RADIUS, INITIAL),
762 association(ids::TRUCK, attributes::WHEEL_POWERED, ETS2_1_10_ATS_1_00),
763 association(ids::TRUCK, attributes::WHEEL_LIFTABLE, ETS2_1_10_ATS_1_00),
764 ];
765
766 pub const TRAILER: [ConfigurationAttributeAssociation; 13] = [
774 association(ids::TRAILER, attributes::ID, INITIAL),
775 association(ids::TRAILER, attributes::CARGO_ACCESSORY_ID, INITIAL),
776 association(ids::TRAILER, attributes::HOOK_POSITION, INITIAL),
777 association(ids::TRAILER, attributes::BRAND_ID, ETS2_1_14_ATS_1_01),
778 association(ids::TRAILER, attributes::BRAND, ETS2_1_14_ATS_1_01),
779 association(ids::TRAILER, attributes::NAME, ETS2_1_14_ATS_1_01),
780 association(ids::TRAILER, attributes::CHAIN_TYPE, ETS2_1_14_ATS_1_01),
781 association(ids::TRAILER, attributes::BODY_TYPE, ETS2_1_14_ATS_1_01),
782 association(ids::TRAILER, attributes::LICENSE_PLATE, ETS2_1_14_ATS_1_01),
783 association(
784 ids::TRAILER,
785 attributes::LICENSE_PLATE_COUNTRY,
786 ETS2_1_14_ATS_1_01,
787 ),
788 association(
789 ids::TRAILER,
790 attributes::LICENSE_PLATE_COUNTRY_ID,
791 ETS2_1_14_ATS_1_01,
792 ),
793 association(ids::TRAILER, attributes::WHEEL_COUNT, INITIAL),
794 association(ids::TRAILER, attributes::WHEEL_POSITION, INITIAL),
795 ];
796
797 pub const JOB: [ConfigurationAttributeAssociation; 19] = [
799 association(ids::JOB, attributes::CARGO_ID, ETS2_1_09_ATS_1_00),
800 association(ids::JOB, attributes::CARGO, ETS2_1_09_ATS_1_00),
801 association(ids::JOB, attributes::CARGO_MASS, ETS2_1_09_ATS_1_00),
802 association(ids::JOB, attributes::CARGO_UNIT_MASS, ETS2_1_14_ATS_1_01),
803 association(ids::JOB, attributes::CARGO_UNIT_COUNT, ETS2_1_14_ATS_1_01),
804 association(
805 ids::JOB,
806 attributes::DESTINATION_CITY_ID,
807 ETS2_1_09_ATS_1_00,
808 ),
809 association(ids::JOB, attributes::DESTINATION_CITY, ETS2_1_09_ATS_1_00),
810 association(ids::JOB, attributes::SOURCE_CITY_ID, ETS2_1_09_ATS_1_00),
811 association(ids::JOB, attributes::SOURCE_CITY, ETS2_1_09_ATS_1_00),
812 association(
813 ids::JOB,
814 attributes::DESTINATION_COMPANY_ID,
815 ETS2_1_09_ATS_1_00,
816 ),
817 association(
818 ids::JOB,
819 attributes::DESTINATION_COMPANY,
820 ETS2_1_09_ATS_1_00,
821 ),
822 association(ids::JOB, attributes::SOURCE_COMPANY_ID, ETS2_1_09_ATS_1_00),
823 association(ids::JOB, attributes::SOURCE_COMPANY, ETS2_1_09_ATS_1_00),
824 association(ids::JOB, attributes::INCOME, ETS2_1_09_ATS_1_00),
825 association(ids::JOB, attributes::DELIVERY_TIME, ETS2_1_09_ATS_1_00),
826 association(ids::JOB, attributes::IS_CARGO_LOADED, ETS2_1_14_ATS_1_01),
827 association(ids::JOB, attributes::JOB_MARKET, ETS2_1_14_ATS_1_01),
828 association(ids::JOB, attributes::SPECIAL_JOB, ETS2_1_14_ATS_1_01),
829 association(
830 ids::JOB,
831 attributes::PLANNED_DISTANCE_KM,
832 ETS2_1_15_ATS_1_02,
833 ),
834 ];
835
836 pub const ALL: [ConfigurationAttributeAssociation; COUNT] = [
838 SUBSTANCES[0],
839 CONTROLS[0],
840 HSHIFTER[0],
841 HSHIFTER[1],
842 HSHIFTER[2],
843 HSHIFTER[3],
844 TRUCK[0],
845 TRUCK[1],
846 TRUCK[2],
847 TRUCK[3],
848 TRUCK[4],
849 TRUCK[5],
850 TRUCK[6],
851 TRUCK[7],
852 TRUCK[8],
853 TRUCK[9],
854 TRUCK[10],
855 TRUCK[11],
856 TRUCK[12],
857 TRUCK[13],
858 TRUCK[14],
859 TRUCK[15],
860 TRUCK[16],
861 TRUCK[17],
862 TRUCK[18],
863 TRUCK[19],
864 TRUCK[20],
865 TRUCK[21],
866 TRUCK[22],
867 TRUCK[23],
868 TRUCK[24],
869 TRUCK[25],
870 TRUCK[26],
871 TRUCK[27],
872 TRUCK[28],
873 TRUCK[29],
874 TRUCK[30],
875 TRUCK[31],
876 TRUCK[32],
877 TRAILER[0],
878 TRAILER[1],
879 TRAILER[2],
880 TRAILER[3],
881 TRAILER[4],
882 TRAILER[5],
883 TRAILER[6],
884 TRAILER[7],
885 TRAILER[8],
886 TRAILER[9],
887 TRAILER[10],
888 TRAILER[11],
889 TRAILER[12],
890 JOB[0],
891 JOB[1],
892 JOB[2],
893 JOB[3],
894 JOB[4],
895 JOB[5],
896 JOB[6],
897 JOB[7],
898 JOB[8],
899 JOB[9],
900 JOB[10],
901 JOB[11],
902 JOB[12],
903 JOB[13],
904 JOB[14],
905 JOB[15],
906 JOB[16],
907 JOB[17],
908 JOB[18],
909 ];
910}
911
912#[derive(Clone, Copy, Debug, PartialEq, Eq)]
914pub enum ShifterType {
915 Arcade,
917 Automatic,
919 Manual,
921 HShifter,
923}
924
925impl ShifterType {
926 pub const COUNT: usize = 4;
928
929 pub const ALL: [Self; Self::COUNT] =
931 [Self::Arcade, Self::Automatic, Self::Manual, Self::HShifter];
932
933 #[must_use]
935 pub const fn availability(self) -> GameSchemaAvailability {
936 let _ = self;
937 INITIAL
938 }
939
940 #[must_use]
942 pub const fn as_str(self) -> &'static str {
943 match self {
944 Self::Arcade => "arcade",
945 Self::Automatic => "automatic",
946 Self::Manual => "manual",
947 Self::HShifter => "hshifter",
948 }
949 }
950
951 #[must_use]
952 pub const fn as_c_str(self) -> &'static CStr {
953 match self {
954 Self::Arcade => c"arcade",
955 Self::Automatic => c"automatic",
956 Self::Manual => c"manual",
957 Self::HShifter => c"hshifter",
958 }
959 }
960
961 #[must_use]
962 pub fn from_c_str(value: &CStr) -> Option<Self> {
963 value.to_str().ok()?.parse().ok()
964 }
965}
966
967impl FromStr for ShifterType {
968 type Err = UnknownStringValue;
969
970 fn from_str(value: &str) -> Result<Self, Self::Err> {
971 match value {
972 "arcade" => Ok(Self::Arcade),
973 "automatic" => Ok(Self::Automatic),
974 "manual" => Ok(Self::Manual),
975 "hshifter" => Ok(Self::HShifter),
976 _ => Err(UnknownStringValue),
977 }
978 }
979}
980
981#[derive(Clone, Copy, Debug, PartialEq, Eq)]
988pub enum JobMarket {
989 CargoMarket,
990 QuickJob,
991 FreightMarket,
992 ExternalContracts,
993 ExternalMarket,
994}
995
996impl JobMarket {
997 pub const COUNT: usize = 5;
999
1000 pub const ALL: [Self; Self::COUNT] = [
1002 Self::CargoMarket,
1003 Self::QuickJob,
1004 Self::FreightMarket,
1005 Self::ExternalContracts,
1006 Self::ExternalMarket,
1007 ];
1008
1009 #[must_use]
1011 pub const fn availability(self) -> GameSchemaAvailability {
1012 let _ = self;
1013 ETS2_1_14_ATS_1_01
1014 }
1015
1016 #[must_use]
1018 pub const fn as_str(self) -> &'static str {
1019 match self {
1020 Self::CargoMarket => "cargo_market",
1021 Self::QuickJob => "quick_job",
1022 Self::FreightMarket => "freight_market",
1023 Self::ExternalContracts => "external_contracts",
1024 Self::ExternalMarket => "external_market",
1025 }
1026 }
1027
1028 #[must_use]
1030 pub const fn as_c_str(self) -> &'static CStr {
1031 match self {
1032 Self::CargoMarket => c"cargo_market",
1033 Self::QuickJob => c"quick_job",
1034 Self::FreightMarket => c"freight_market",
1035 Self::ExternalContracts => c"external_contracts",
1036 Self::ExternalMarket => c"external_market",
1037 }
1038 }
1039
1040 #[must_use]
1042 pub fn from_c_str(value: &CStr) -> Option<Self> {
1043 value.to_str().ok()?.parse().ok()
1044 }
1045}
1046
1047impl FromStr for JobMarket {
1048 type Err = UnknownStringValue;
1049
1050 fn from_str(value: &str) -> Result<Self, Self::Err> {
1051 match value {
1052 "cargo_market" => Ok(Self::CargoMarket),
1053 "quick_job" => Ok(Self::QuickJob),
1054 "freight_market" => Ok(Self::FreightMarket),
1055 "external_contracts" => Ok(Self::ExternalContracts),
1056 "external_market" => Ok(Self::ExternalMarket),
1057 _ => Err(UnknownStringValue),
1058 }
1059 }
1060}
1061
1062const _: [(); 6] = [(); ids::COUNT];
1063const _: [(); 60] = [(); attributes::COUNT];
1064const _: [(); 71] = [(); associations::COUNT];
1065const _: [(); 4] = [(); ShifterType::COUNT];
1066const _: [(); 5] = [(); JobMarket::COUNT];
1067
1068#[cfg(test)]
1069mod tests {
1070 use super::*;
1071 use crate::GameSchemaVersion;
1072
1073 fn assert_since<T: crate::SdkValue>(
1074 attribute: crate::Attribute<T>,
1075 ets2: GameSchemaVersion,
1076 ats: GameSchemaVersion,
1077 ) {
1078 assert_eq!(attribute.availability().available_since_ets2(), Some(ets2));
1079 assert_eq!(attribute.availability().available_since_ats(), Some(ats));
1080 }
1081
1082 #[test]
1083 fn typed_catalog_counts_match_the_raw_sdk_catalog() {
1084 assert_eq!(ids::COUNT, crate::sys::configuration::ids::COUNT);
1085 assert_eq!(
1086 attributes::COUNT,
1087 crate::sys::configuration::attributes::COUNT
1088 );
1089 assert_eq!(ids::COUNT, 6);
1090 assert_eq!(attributes::COUNT, 60);
1091 assert_eq!(MAX_TRAILERS, crate::sys::configuration::MAX_TRAILERS);
1092 assert_eq!(associations::COUNT, 71);
1093 assert_eq!(associations::SUBSTANCES.len(), 1);
1094 assert_eq!(associations::CONTROLS.len(), 1);
1095 assert_eq!(associations::HSHIFTER.len(), 4);
1096 assert_eq!(associations::TRUCK.len(), 33);
1097 assert_eq!(associations::TRAILER.len(), 13);
1098 assert_eq!(associations::JOB.len(), 19);
1099 }
1100
1101 #[test]
1102 fn association_catalog_has_complete_membership_without_duplicate_pairs() {
1103 for association in associations::ALL {
1104 assert!(
1105 ids::ALL.contains(&association.configuration()),
1106 "association uses unknown configuration: {:?}",
1107 association.configuration().name()
1108 );
1109 assert!(
1110 attributes::ALL.contains(&association.attribute()),
1111 "association uses unknown attribute: {:?}",
1112 association.attribute().name()
1113 );
1114 }
1115
1116 for attribute in attributes::ALL {
1117 assert!(
1118 associations::ALL
1119 .iter()
1120 .any(|association| association.attribute() == attribute),
1121 "attribute has no configuration membership: {:?}",
1122 attribute.name()
1123 );
1124 }
1125
1126 for (position, association) in associations::ALL.iter().enumerate() {
1127 assert!(
1128 associations::ALL[..position].iter().all(|earlier| {
1129 earlier.configuration() != association.configuration()
1130 || earlier.attribute() != association.attribute()
1131 }),
1132 "duplicate configuration association: {:?} -> {:?}",
1133 association.configuration().name(),
1134 association.attribute().name()
1135 );
1136 }
1137 }
1138
1139 #[test]
1140 fn association_catalog_preserves_configuration_group_order() {
1141 let groups = [
1142 (ids::SUBSTANCES, 0..1),
1143 (ids::CONTROLS, 1..2),
1144 (ids::HSHIFTER, 2..6),
1145 (ids::TRUCK, 6..39),
1146 (ids::TRAILER, 39..52),
1147 (ids::JOB, 52..71),
1148 ];
1149
1150 for (configuration, range) in groups {
1151 assert!(
1152 associations::ALL[range]
1153 .iter()
1154 .all(|association| association.configuration() == configuration)
1155 );
1156 }
1157
1158 assert_eq!(
1159 associations::SUBSTANCES[0].attribute(),
1160 attributes::ID.erase()
1161 );
1162 assert_eq!(
1163 associations::CONTROLS[0].attribute(),
1164 attributes::SHIFTER_TYPE.erase()
1165 );
1166 assert_eq!(
1167 associations::TRUCK[16].attribute(),
1168 attributes::DIFFERENTIAL_RATIO.erase()
1169 );
1170 assert_eq!(
1171 associations::TRAILER[3].attribute(),
1172 attributes::BRAND_ID.erase()
1173 );
1174 assert_eq!(
1175 associations::JOB[18].attribute(),
1176 attributes::PLANNED_DISTANCE_KM.erase()
1177 );
1178 }
1179
1180 #[test]
1181 fn association_availability_is_never_earlier_than_its_descriptors() {
1182 for association in associations::ALL {
1183 let availability = association.availability();
1184 let configuration = association.configuration().availability();
1185 let attribute = association.attribute().availability();
1186
1187 assert!(matches!(
1188 (
1189 availability.available_since_ets2(),
1190 configuration.available_since_ets2(),
1191 attribute.available_since_ets2()
1192 ),
1193 (Some(relation), Some(group), Some(value)) if relation >= group && relation >= value
1194 ));
1195 assert!(matches!(
1196 (
1197 availability.available_since_ats(),
1198 configuration.available_since_ats(),
1199 attribute.available_since_ats()
1200 ),
1201 (Some(relation), Some(group), Some(value)) if relation >= group && relation >= value
1202 ));
1203 }
1204 }
1205
1206 #[test]
1207 fn shared_attribute_relationships_keep_independent_history() {
1208 assert_eq!(
1209 associations::TRUCK[0].availability(),
1210 INITIAL,
1211 "truck brand_id belongs to the initial truck schema"
1212 );
1213 assert_eq!(
1214 associations::TRAILER[3].availability(),
1215 game::capabilities::MULTI_TRAILER,
1216 "trailer brand_id arrived with the multi-trailer schema"
1217 );
1218 assert_eq!(
1219 associations::TRAILER[0].availability(),
1220 INITIAL,
1221 "the legacy trailer id relationship predates numbered trailers"
1222 );
1223 assert_eq!(
1224 associations::JOB[3].availability(),
1225 game::capabilities::MULTI_TRAILER,
1226 "cargo unit mass first appears in the same historical archive"
1227 );
1228 assert_eq!(
1229 game::capabilities::MULTI_TRAILER.available_since_ets2(),
1230 Some(game::ets2::V1_14)
1231 );
1232 assert_eq!(
1233 game::capabilities::MULTI_TRAILER.available_since_ats(),
1234 Some(game::ats::V1_01)
1235 );
1236 }
1237
1238 #[test]
1239 fn every_typed_descriptor_matches_the_raw_header_catalog() {
1240 for (descriptor, raw_name) in ids::ALL.iter().zip(crate::sys::configuration::ids::ALL) {
1241 assert_eq!(descriptor.name().to_bytes_with_nul(), raw_name);
1242 }
1243 for (descriptor, raw_name) in attributes::ALL
1244 .iter()
1245 .zip(crate::sys::configuration::attributes::ALL)
1246 {
1247 assert_eq!(descriptor.name().to_bytes_with_nul(), raw_name);
1248 }
1249
1250 for (position, descriptor) in attributes::ALL.iter().enumerate() {
1251 assert!(
1252 attributes::ALL[..position]
1253 .iter()
1254 .all(|earlier| earlier.name() != descriptor.name()),
1255 "duplicate configuration attribute: {:?}",
1256 descriptor.name()
1257 );
1258 }
1259 assert_eq!(
1260 attributes::ALL
1261 .iter()
1262 .filter(|attribute| attribute.is_indexed())
1263 .count(),
1264 11
1265 );
1266 }
1267
1268 #[test]
1269 fn representative_descriptors_preserve_header_metadata() {
1270 assert_eq!(ids::JOB.name(), c"job");
1271 assert_eq!(attributes::CARGO_MASS.name(), c"cargo.mass");
1272 assert_eq!(
1273 attributes::CARGO_MASS.value_type(),
1274 crate::sys::SCS_VALUE_TYPE_FLOAT
1275 );
1276 assert!(!attributes::CARGO_MASS.is_indexed());
1277 assert_eq!(attributes::WHEEL_POSITION.name(), c"wheel.position");
1278 assert!(attributes::WHEEL_POSITION.is_indexed());
1279 }
1280
1281 #[test]
1282 fn configuration_availability_follows_official_game_schema_history() {
1283 assert_eq!(
1284 ids::JOB.availability().available_since_ets2(),
1285 Some(game::ets2::V1_09)
1286 );
1287 assert_eq!(
1288 ids::JOB.availability().available_since_ats(),
1289 Some(game::ats::V1_00)
1290 );
1291 assert_since(attributes::CARGO_ID, game::ets2::V1_09, game::ats::V1_00);
1292 assert_since(attributes::INCOME, game::ets2::V1_09, game::ats::V1_00);
1293
1294 assert_since(
1295 attributes::AIR_PRESSURE_EMERGENCY,
1296 game::ets2::V1_01,
1297 game::ats::V1_00,
1298 );
1299 for attribute in [attributes::WHEEL_POWERED, attributes::WHEEL_LIFTABLE] {
1300 assert_since(attribute, game::ets2::V1_10, game::ats::V1_00);
1301 }
1302 for attribute in [
1303 attributes::ADBLUE_CAPACITY,
1304 attributes::ADBLUE_WARNING_FACTOR,
1305 attributes::DIFFERENTIAL_RATIO,
1306 attributes::FORWARD_RATIO,
1307 attributes::REVERSE_RATIO,
1308 ] {
1309 assert_since(attribute, game::ets2::V1_12, game::ats::V1_00);
1310 }
1311 for attribute in [
1312 attributes::CHAIN_TYPE,
1313 attributes::BODY_TYPE,
1314 attributes::LICENSE_PLATE,
1315 attributes::LICENSE_PLATE_COUNTRY_ID,
1316 attributes::LICENSE_PLATE_COUNTRY,
1317 ] {
1318 assert_since(attribute, game::ets2::V1_14, game::ats::V1_01);
1319 }
1320 assert_since(
1321 attributes::CARGO_UNIT_MASS,
1322 game::ets2::V1_14,
1323 game::ats::V1_01,
1324 );
1325 assert_since(
1326 attributes::CARGO_UNIT_COUNT,
1327 game::ets2::V1_14,
1328 game::ats::V1_01,
1329 );
1330 assert_since(
1331 attributes::IS_CARGO_LOADED,
1332 game::ets2::V1_14,
1333 game::ats::V1_01,
1334 );
1335 assert_since(attributes::JOB_MARKET, game::ets2::V1_14, game::ats::V1_01);
1336 assert_since(attributes::SPECIAL_JOB, game::ets2::V1_14, game::ats::V1_01);
1337 assert_since(
1338 attributes::PLANNED_DISTANCE_KM,
1339 game::ets2::V1_15,
1340 game::ats::V1_02,
1341 );
1342
1343 assert!(ids::ALL.iter().all(|descriptor| {
1344 descriptor.availability().available_since_ets2().is_some()
1345 && descriptor.availability().available_since_ats().is_some()
1346 }));
1347 assert!(attributes::ALL.iter().all(|descriptor| {
1348 descriptor.availability().available_since_ets2().is_some()
1349 && descriptor.availability().available_since_ats().is_some()
1350 }));
1351 }
1352
1353 #[test]
1354 fn every_documented_shifter_type_round_trips() {
1355 assert_eq!(ShifterType::COUNT, 4);
1356 for value in ShifterType::ALL {
1357 assert_eq!(ShifterType::from_c_str(value.as_c_str()), Some(value));
1358 assert_eq!(value.as_str().parse(), Ok(value));
1359 assert_eq!(value.availability(), INITIAL);
1360 }
1361 assert_eq!(ShifterType::from_c_str(c"future-shifter"), None);
1362 assert_eq!(
1363 "future-shifter".parse::<ShifterType>(),
1364 Err(UnknownStringValue)
1365 );
1366 }
1367
1368 #[test]
1369 fn every_documented_job_market_round_trips() {
1370 assert_eq!(JobMarket::COUNT, 5);
1371 for value in JobMarket::ALL {
1372 assert_eq!(JobMarket::from_c_str(value.as_c_str()), Some(value));
1373 assert_eq!(value.as_str().parse(), Ok(value));
1374 assert_eq!(value.availability(), ETS2_1_14_ATS_1_01);
1375 }
1376 assert_eq!(JobMarket::from_c_str(c"future_market"), None);
1377 assert_eq!(
1378 "future_market".parse::<JobMarket>(),
1379 Err(UnknownStringValue)
1380 );
1381 }
1382}