THRIFT-4098 Namespace support for generated Rust code (#2348)

Client: rs
diff --git a/lib/rs/Makefile.am b/lib/rs/Makefile.am
index 4abdff8..7a9b30a 100644
--- a/lib/rs/Makefile.am
+++ b/lib/rs/Makefile.am
@@ -21,6 +21,7 @@
 
 if WITH_TESTS
 SUBDIRS += test
+SUBDIRS += test_recursive
 endif
 
 install:
diff --git a/lib/rs/test_recursive/Cargo.toml b/lib/rs/test_recursive/Cargo.toml
new file mode 100644
index 0000000..6b2aa85
--- /dev/null
+++ b/lib/rs/test_recursive/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "thrift_4098_custom_rust_namespace_support"
+description = "Test namespace support in generated thrift files using recursive Make generation"
+version = "0.1.0"
+authors = ["Allen George <allengeorge@apache.org>"]
+edition = "2018"
+
+[dependencies]
+thrift = { path = "../" }
diff --git a/lib/rs/test_recursive/Makefile.am b/lib/rs/test_recursive/Makefile.am
new file mode 100644
index 0000000..e676ccd
--- /dev/null
+++ b/lib/rs/test_recursive/Makefile.am
@@ -0,0 +1,33 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+SUBDIRS = src
+
+check:
+	$(CARGO) fmt --all -- --check
+	$(CARGO) clippy --all -- -D warnings
+	$(CARGO) build
+	$(CARGO) test
+
+clean-local:
+	$(CARGO) clean
+	-$(RM) Cargo.lock
+
+EXTRA_DIST = \
+	Cargo.toml
diff --git a/lib/rs/test_recursive/src/Makefile.am b/lib/rs/test_recursive/src/Makefile.am
new file mode 100644
index 0000000..c21a94c
--- /dev/null
+++ b/lib/rs/test_recursive/src/Makefile.am
@@ -0,0 +1,33 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+SUBDIRS = . transit maintenance
+
+THRIFT = $(top_builddir)/compiler/cpp/thrift
+
+stubs: Vehicles.thrift $(THRIFT)
+	$(THRIFT) -I . -out . --gen rs Vehicles.thrift
+
+check: stubs
+
+clean-local:
+	-$(RM) vehicles.rs
+
+EXTRA_DIST = \
+	Vehicles.thrift
diff --git a/lib/rs/test_recursive/src/Vehicles.thrift b/lib/rs/test_recursive/src/Vehicles.thrift
new file mode 100644
index 0000000..5cce21c
--- /dev/null
+++ b/lib/rs/test_recursive/src/Vehicles.thrift
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+typedef i16 Capacity
+
+enum Material {
+  Steel = 0
+  Aluminum = 1
+}
+
+struct VehicleIdentifier {
+  1: string manufacturer
+  2: string model
+  3: list<string> qualifiers
+}
diff --git a/lib/rs/test_recursive/src/lib.rs b/lib/rs/test_recursive/src/lib.rs
new file mode 100644
index 0000000..bac37b4
--- /dev/null
+++ b/lib/rs/test_recursive/src/lib.rs
@@ -0,0 +1,276 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#![allow(dead_code)]
+
+pub mod transit;
+pub mod vehicles;
+pub mod maintenance;
+
+mod server {
+    use crate::maintenance::maintenance_facility::{
+        BigBarnSyncHandler, MultimodalFacilitySyncHandler,
+    };
+    use crate::transit::buses::{Bus, GarageSyncHandler};
+    use crate::transit::buses::{Powertrain, Route as BusRoute};
+    use crate::transit::light::streetcars::{
+        BarnSyncHandler, Flexity, RollingStock, Route, RouteNumber, Streetcar,
+    };
+    use crate::transit::services::city_services::TransitImprovements;
+    use crate::transit::trains::Locomotive;
+    use crate::transit::transporters::{FlatcarConsist, SingleVehicleTransporter};
+    use crate::vehicles::Material;
+    use thrift::Result;
+
+    //
+    // implement a whole bunch of handler methods just to make sure I can, and that everything compiles
+    //
+
+    pub struct AllInOneHandler;
+
+    impl BigBarnSyncHandler for AllInOneHandler {
+        fn handle_add_streetcar(&self, route: Route) -> Result<Streetcar> {
+            if let Some(route_number) = route.id {
+                match route_number {
+                    RouteNumber::LAKESHORE => Ok(Streetcar {
+                        id: Some(4417),
+                        stock: Some(RollingStock::Flexity(Flexity {
+                            materials: Some(vec![Material::STEEL, Material::ALUMINUM]),
+                            locomotive: Some(Locomotive::ELECTRIC_PANTOGRAPH),
+                        })),
+                        route: Some(Route {
+                            id: Some(RouteNumber::LAKESHORE),
+                            improvements: None,
+                        }),
+                    }),
+                    _ => Err(thrift::Error::from(format!(
+                        "Cannot create streetcar for route number {}",
+                        route_number.0
+                    ))),
+                }
+            } else {
+                Err(thrift::Error::from("Can't add a streetcar"))
+            }
+        }
+    }
+
+    impl BarnSyncHandler for AllInOneHandler {
+        fn handle_upgrade_streetcar(&self, streetcar: Streetcar) -> Result<Streetcar> {
+            if let Some(rolling_stock) = streetcar.stock {
+                match rolling_stock {
+                    RollingStock::Clrv(_) => Ok(Streetcar {
+                        stock: Some(RollingStock::Flexity(Flexity {
+                            materials: Some(vec![Material::STEEL, Material::ALUMINUM]),
+                            locomotive: Some(Locomotive::ELECTRIC_PANTOGRAPH),
+                        })),
+                        ..streetcar
+                    }),
+                    RollingStock::Flexity(_) => {
+                        Err(thrift::Error::from("Streetcar already upgraded"))
+                    }
+                }
+            } else {
+                Err(thrift::Error::from("Can't upgrade streetcar"))
+            }
+        }
+    }
+
+    impl MultimodalFacilitySyncHandler for AllInOneHandler {
+        fn handle_build_transporter(
+            &self,
+            source: String,
+            destination: String,
+            consist: FlatcarConsist,
+        ) -> Result<SingleVehicleTransporter> {
+            Ok(SingleVehicleTransporter {
+                consist: Some(consist),
+                source: Some(source),
+                destination: Some(destination),
+            })
+        }
+    }
+
+    impl GarageSyncHandler for AllInOneHandler {
+        fn handle_upgrade_bus(&self, bus: Bus) -> Result<Bus> {
+            if let Some(p) = bus.powertrain {
+                match p {
+                    Powertrain::COMPRESSED_NATURAL_GAS => Ok(Bus {
+                        powertrain: Some(Powertrain::DIESEL),
+                        ..bus
+                    }),
+                    _ => Err(thrift::Error::from("Cannot upgrade from this powertrain")),
+                }
+            } else {
+                Err(thrift::Error::from("Cannot upgrade bus"))
+            }
+        }
+
+        fn handle_improvements_for_route(
+            &self,
+            route: BusRoute,
+        ) -> Result<Vec<TransitImprovements>> {
+            Ok(route
+                .improvements
+                .expect("Expecting a list of improvements"))
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+
+    //
+    // TODO: consider using the generated client/server and doing a round-trip
+    //
+
+    use crate::server::AllInOneHandler;
+    use crate::transit::buses::{Bus, Powertrain, Route as BusRoute, DEFAULT4WHEELCAPACITY};
+    use crate::transit::light::light_rail::Lrt;
+    use crate::transit::light::streetcars::{
+        BarnSyncHandler, Flexity, RollingStock, Route, RouteNumber, Streetcar, CLRV,
+    };
+    use crate::transit::services::city_services::TransitImprovements;
+    use crate::transit::trains::Locomotive;
+    use crate::transit::transporters::{FlatcarConsist, SingleVehicleTransporter};
+    use crate::vehicles::{Material, VehicleIdentifier};
+
+    use crate::maintenance::maintenance_facility::{
+        BigBarnSyncHandler, MultimodalFacilitySyncHandler,
+    };
+    use crate::transit::buses::GarageSyncHandler;
+
+    #[test]
+    fn handle_add_streetcar_compiles_and_returns_expected_value() {
+        let expected = Streetcar {
+            id: Some(4417),
+            stock: Some(RollingStock::Flexity(Flexity {
+                materials: Some(vec![Material::STEEL, Material::ALUMINUM]),
+                locomotive: Some(Locomotive::ELECTRIC_PANTOGRAPH),
+            })),
+            route: Some(Route {
+                id: Some(RouteNumber::LAKESHORE),
+                improvements: None,
+            }),
+        };
+
+        let handler = AllInOneHandler {};
+        let actual = handler
+            .handle_add_streetcar(Route {
+                id: Some(RouteNumber::LAKESHORE),
+                improvements: None,
+            })
+            .expect("Expected a result");
+
+        assert_eq!(expected, actual)
+    }
+
+    #[test]
+    fn handle_upgrade_streetcar_compiles_and_returns_expected_value() {
+        let input = Streetcar {
+            stock: Some(RollingStock::Clrv(CLRV {
+                materials: Some(vec![Material::STEEL, Material::ALUMINUM]),
+                locomotive: Some(Locomotive::ELECTRIC_POLE),
+            })),
+            id: Some(4415),
+            route: Some(Route {
+                id: Some(RouteNumber::SPADINA),
+                improvements: Some(vec![TransitImprovements::DEDICATED_RIGHT_OF_WAY]),
+            }),
+        };
+
+        let expected = Streetcar {
+            stock: Some(RollingStock::Flexity(Flexity {
+                materials: Some(vec![Material::STEEL, Material::ALUMINUM]),
+                locomotive: Some(Locomotive::ELECTRIC_PANTOGRAPH),
+            })),
+            id: Some(4415),
+            route: Some(Route {
+                id: Some(RouteNumber::SPADINA),
+                improvements: Some(vec![TransitImprovements::DEDICATED_RIGHT_OF_WAY]),
+            }),
+        };
+
+        let handler = AllInOneHandler {};
+        let actual = handler
+            .handle_upgrade_streetcar(input)
+            .expect("Expected an upgraded streetcar");
+
+        assert_eq!(expected, actual)
+    }
+
+    #[test]
+    fn handle_build_transporter_compiles_and_returns_expected_value() {
+        let consist = FlatcarConsist::Lrt(Lrt {
+            materials: Some(vec![Material::STEEL, Material::ALUMINUM]),
+            locomotive: Some(Locomotive::ELECTRIC_PANTOGRAPH),
+        });
+        let expected = SingleVehicleTransporter {
+            consist: Some(consist.clone()),
+            source: Some("905".to_owned()),
+            destination: Some("416".to_owned()),
+        };
+
+        let handler = AllInOneHandler {};
+        let actual = handler
+            .handle_build_transporter("905".to_owned(), "416".to_owned(), consist)
+            .expect("Expected a transporter");
+
+        assert_eq!(expected, actual)
+    }
+
+    #[test]
+    fn handle_upgrade_bus_compiles_and_returns_expected_value() {
+        let bus = Bus {
+            identifier: Some(VehicleIdentifier {
+                manufacturer: Some("Orion".to_owned()),
+                model: Some("Orion 07.501 NG HEV".to_owned()),
+                qualifiers: None,
+            }),
+            capacity: Some(DEFAULT4WHEELCAPACITY),
+            powertrain: Some(Powertrain::COMPRESSED_NATURAL_GAS),
+            materials: Some(vec![Material::STEEL, Material::ALUMINUM]),
+        };
+
+        let expected = Bus {
+            powertrain: Some(Powertrain::DIESEL),
+            ..(bus.clone())
+        };
+
+        let handler = AllInOneHandler {};
+        let actual = handler
+            .handle_upgrade_bus(bus)
+            .expect("Expected improved bus");
+
+        assert_eq!(expected, actual)
+    }
+
+    #[test]
+    fn handle_improvements_for_route_compiles_and_returns_expected_value() {
+        let expected = vec![TransitImprovements::TRANSIT_SIGNAL_PRIORITY];
+        let bus_route = BusRoute {
+            route_id: Some("320".to_owned()),
+            improvements: Some(expected.clone()),
+        };
+
+        let handler = AllInOneHandler {};
+        let actual = handler
+            .handle_improvements_for_route(bus_route)
+            .expect("Expected list of transit improvements");
+
+        assert_eq!(expected, actual)
+    }
+}
diff --git a/lib/rs/test_recursive/src/maintenance/MaintenanceFacility.thrift b/lib/rs/test_recursive/src/maintenance/MaintenanceFacility.thrift
new file mode 100644
index 0000000..bed0b8d
--- /dev/null
+++ b/lib/rs/test_recursive/src/maintenance/MaintenanceFacility.thrift
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+namespace rs maintenance
+
+include "Buses.thrift"
+include "LightRail.thrift"
+include "Streetcars.thrift"
+include "Transporters.thrift"
+
+service BigBarn extends Streetcars.Barn {
+    Streetcars.Streetcar addStreetcar(1: Streetcars.Route route)
+}
+
+service MultimodalFacility extends Buses.Garage {
+    Transporters.SingleVehicleTransporter buildTransporter(1: string source, 2: string destination, 3: Transporters.FlatcarConsist consist)
+}
\ No newline at end of file
diff --git a/lib/rs/test_recursive/src/maintenance/Makefile.am b/lib/rs/test_recursive/src/maintenance/Makefile.am
new file mode 100644
index 0000000..c24813a
--- /dev/null
+++ b/lib/rs/test_recursive/src/maintenance/Makefile.am
@@ -0,0 +1,33 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+SUBDIRS = .
+
+THRIFT = $(top_builddir)/compiler/cpp/thrift
+
+stubs: ../Vehicles.thrift ../transit/Buses.thrift ../transit/Trains.thrift ../transit/Transporters.thrift ../transit/services/CityServices.thrift ../transit/light/LightRail.thrift ../transit/light/Streetcars.thrift $(THRIFT)
+	$(THRIFT) -I . -I ../ -I ../transit -I ../transit/services -I ../transit/light -out . --gen rs MaintenanceFacility.thrift
+
+check: stubs
+
+clean-local:
+	-$(RM) maintenance_facility.rs
+
+EXTRA_DIST = \
+	MaintenanceFacility.thrift
diff --git a/lib/rs/test_recursive/src/maintenance/mod.rs b/lib/rs/test_recursive/src/maintenance/mod.rs
new file mode 100644
index 0000000..a75b6e1
--- /dev/null
+++ b/lib/rs/test_recursive/src/maintenance/mod.rs
@@ -0,0 +1,18 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+pub mod maintenance_facility;
diff --git a/lib/rs/test_recursive/src/transit/Buses.thrift b/lib/rs/test_recursive/src/transit/Buses.thrift
new file mode 100644
index 0000000..29dc5fe
--- /dev/null
+++ b/lib/rs/test_recursive/src/transit/Buses.thrift
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+namespace rs transit
+
+include "CityServices.thrift"
+include "Vehicles.thrift"
+
+const Vehicles.Capacity DEFAULT4WHEELCAPACITY = 30
+
+enum Powertrain {
+  DIESEL = 0
+  BIO_DIESEL = 1
+  COMPRESSED_NATURAL_GAS = 2
+  TROLLEY = 3
+  HYBRID = 4
+  BATTERY = 5
+}
+
+struct Bus {
+  1: Vehicles.VehicleIdentifier identifier
+  2: Vehicles.Capacity capacity
+  3: Powertrain powertrain
+  4: list<Vehicles.Material> materials
+}
+
+struct Route {
+  1: string routeId
+  2: list<CityServices.TransitImprovements> improvements
+}
+
+service Garage {
+    Bus upgradeBus(1: Bus bus)
+
+    list<CityServices.TransitImprovements> improvementsForRoute(1: Route route)
+}
\ No newline at end of file
diff --git a/lib/rs/test_recursive/src/transit/Makefile.am b/lib/rs/test_recursive/src/transit/Makefile.am
new file mode 100644
index 0000000..7318265
--- /dev/null
+++ b/lib/rs/test_recursive/src/transit/Makefile.am
@@ -0,0 +1,40 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# intentionally added a cyclic dependency between '.' and 'light'
+SUBDIRS = . light services
+
+THRIFT = $(top_builddir)/compiler/cpp/thrift
+
+stubs: ../Vehicles.thrift Buses.thrift Trains.thrift Transporters.thrift services/CityServices.thrift light/LightRail.thrift light/Streetcars.thrift $(THRIFT)
+	$(THRIFT) -I . -I ../ -I ./services -I ./light -out . --gen rs Buses.thrift
+	$(THRIFT) -I . -I ../ -I ./services -I ./light -out . --gen rs Trains.thrift
+	$(THRIFT) -I . -I ../ -I ./services -I ./light -out . --gen rs Transporters.thrift
+
+check: stubs
+
+clean-local:
+	-$(RM) buses.rs
+	-$(RM) trains.rs
+	-$(RM) transporters.rs
+
+EXTRA_DIST = \
+	Buses.thrift \
+	Trains.thrift \
+	Transporters.thrift
diff --git a/lib/rs/test_recursive/src/transit/Trains.thrift b/lib/rs/test_recursive/src/transit/Trains.thrift
new file mode 100644
index 0000000..6f97b53
--- /dev/null
+++ b/lib/rs/test_recursive/src/transit/Trains.thrift
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+namespace rs transit
+
+enum Locomotive {
+  Steam = 0
+  ElectricPole = 1
+  ElectricPantograph = 2
+  ElectricThirdRail = 3
+  DieselMechanical = 4
+  DieselElectric = 5
+}
diff --git a/lib/rs/test_recursive/src/transit/Transporters.thrift b/lib/rs/test_recursive/src/transit/Transporters.thrift
new file mode 100644
index 0000000..540f1bb
--- /dev/null
+++ b/lib/rs/test_recursive/src/transit/Transporters.thrift
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+namespace rs transit
+
+include "Buses.thrift"
+include "LightRail.thrift"
+include "Streetcars.thrift"
+
+union FlatcarConsist {
+  1: LightRail.Lrt lrt
+  2: Streetcars.Streetcar streetcar
+  3: Buses.Bus bus
+}
+
+struct SingleVehicleTransporter {
+  1: FlatcarConsist consist
+  2: string source
+  3: string destination
+}
diff --git a/lib/rs/test_recursive/src/transit/light/LightRail.thrift b/lib/rs/test_recursive/src/transit/light/LightRail.thrift
new file mode 100644
index 0000000..0d887ab
--- /dev/null
+++ b/lib/rs/test_recursive/src/transit/light/LightRail.thrift
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+include "CityServices.thrift"
+include "Trains.thrift"
+include "Vehicles.thrift"
+
+namespace rs transit.light
+
+struct Lrt {
+  1: list<Vehicles.Material> materials
+  2: Trains.Locomotive locomotive
+}
+
+enum Route {
+  EglintonCrosstown = 0
+  FinchWest = 1
+}
+
+struct Line {
+  1: Lrt lrt
+  2: Route route
+  3: list<CityServices.TransitImprovements> improvements = [] // ABSOLUTELY NONE BY DEFAULT!
+}
+
+service Msf {
+    Lrt fixLrt(1: Lrt lrt)
+}
\ No newline at end of file
diff --git a/lib/rs/test_recursive/src/transit/light/Makefile.am b/lib/rs/test_recursive/src/transit/light/Makefile.am
new file mode 100644
index 0000000..c09c39d
--- /dev/null
+++ b/lib/rs/test_recursive/src/transit/light/Makefile.am
@@ -0,0 +1,36 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+SUBDIRS = .
+
+THRIFT = $(top_builddir)/compiler/cpp/thrift
+
+stubs: ../../Vehicles.thrift ../Trains.thrift ../services/CityServices.thrift LightRail.thrift Streetcars.thrift $(THRIFT)
+	$(THRIFT) -I . -I ../../ -I ../ -I ../services -out . --gen rs LightRail.thrift
+	$(THRIFT) -I . -I ../../ -I ../ -I ../services -out . --gen rs Streetcars.thrift
+
+check: stubs
+
+clean-local:
+	-$(RM) light_rail.rs
+	-$(RM) streetcars.rs
+
+EXTRA_DIST = \
+	LightRail.thrift \
+	Streetcars.thrift
diff --git a/lib/rs/test_recursive/src/transit/light/Streetcars.thrift b/lib/rs/test_recursive/src/transit/light/Streetcars.thrift
new file mode 100644
index 0000000..31d3ad4
--- /dev/null
+++ b/lib/rs/test_recursive/src/transit/light/Streetcars.thrift
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+include "CityServices.thrift"
+include "Trains.thrift"
+include "Vehicles.thrift"
+
+namespace rs transit.light
+
+struct CLRV {
+  1: list<Vehicles.Material> materials
+  2: Trains.Locomotive locomotive
+}
+
+struct Flexity {
+  1: list<Vehicles.Material> materials
+  2: Trains.Locomotive locomotive
+}
+
+union RollingStock {
+  1: CLRV clrv
+  2: Flexity flexity
+}
+
+enum RouteNumber {
+  Queen = 501
+  Downtowner = 502
+  Kingston = 503
+  King = 504
+  Dundas = 505
+  Carlton = 506
+  Lakeshore = 508
+  Harbourfront = 509
+  Spadina = 510
+  Bathurst = 511
+  StClair = 512
+}
+
+struct Route {
+  1: RouteNumber id
+  2: list<CityServices.TransitImprovements> improvements = []  // ABSOLUTELY NONE!
+}
+
+struct Streetcar {
+  1: i16 id
+  2: RollingStock stock
+  3: Route route
+}
+
+service Barn {
+    Streetcar upgradeStreetcar(1: Streetcar streetcar)
+}
\ No newline at end of file
diff --git a/lib/rs/test_recursive/src/transit/light/mod.rs b/lib/rs/test_recursive/src/transit/light/mod.rs
new file mode 100644
index 0000000..f68d099
--- /dev/null
+++ b/lib/rs/test_recursive/src/transit/light/mod.rs
@@ -0,0 +1,19 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+pub mod light_rail;
+pub mod streetcars;
diff --git a/lib/rs/test_recursive/src/transit/mod.rs b/lib/rs/test_recursive/src/transit/mod.rs
new file mode 100644
index 0000000..e144b38
--- /dev/null
+++ b/lib/rs/test_recursive/src/transit/mod.rs
@@ -0,0 +1,22 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+pub mod buses;
+pub mod light;
+pub mod services;
+pub mod trains;
+pub mod transporters;
diff --git a/lib/rs/test_recursive/src/transit/services/CityServices.thrift b/lib/rs/test_recursive/src/transit/services/CityServices.thrift
new file mode 100644
index 0000000..2ca559a
--- /dev/null
+++ b/lib/rs/test_recursive/src/transit/services/CityServices.thrift
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+namespace rs transit.services
+
+enum TransitImprovements {
+  TransitSignalPriority = 1
+  DedicatedRightOfWay = 2
+}
diff --git a/lib/rs/test_recursive/src/transit/services/Makefile.am b/lib/rs/test_recursive/src/transit/services/Makefile.am
new file mode 100644
index 0000000..f70e919
--- /dev/null
+++ b/lib/rs/test_recursive/src/transit/services/Makefile.am
@@ -0,0 +1,33 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+SUBDIRS = .
+
+THRIFT = $(top_builddir)/compiler/cpp/thrift
+
+stubs: CityServices.thrift $(THRIFT)
+	$(THRIFT) -I . -out . --gen rs CityServices.thrift
+
+check: stubs
+
+clean-local:
+	-$(RM) city_services.rs
+
+EXTRA_DIST = \
+	CityServices.thrift
diff --git a/lib/rs/test_recursive/src/transit/services/mod.rs b/lib/rs/test_recursive/src/transit/services/mod.rs
new file mode 100644
index 0000000..2cb171a
--- /dev/null
+++ b/lib/rs/test_recursive/src/transit/services/mod.rs
@@ -0,0 +1,18 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+pub mod city_services;