From fb48e44ae6bb700b419d703ca45489f1ae2ac08f Mon Sep 17 00:00:00 2001 From: Taku Fukada Date: Wed, 14 Feb 2024 13:40:22 +0900 Subject: [PATCH] =?UTF-8?q?CLI:=20=E5=90=84=20SinkProvider=20=E3=81=AE?= =?UTF-8?q?=E6=83=85=E5=A0=B1=E3=82=92=E4=BD=BF=E3=81=A3=E3=81=A6=20Sink?= =?UTF-8?q?=E3=81=AE=E4=B8=80=E8=A6=A7=EF=BC=88ID=E3=80=81=E5=90=8D?= =?UTF-8?q?=E5=89=8D=EF=BC=89=E3=82=92=E7=B5=84=E3=82=80=20(#275)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nusamai CLI でのSinkの選択において、SinkProviderが提供する情報を使ってSinkの一覧をうまく作るように変更する。いままではenum のVariantsとして手書きで列挙していた。 ## Summary by CodeRabbit - **新機能** - `GpkgSinkProvider` に複数のシンクプロバイダを統合した `SinkChoice` 構造体を追加しました。 - `Args` 構造体をコメントを明確化して更新しました。 - 列挙型 `SinkChoice` を構造体に置き換え、動的なシンク選択処理のために `clap::ValueEnum` を実装しました。 - より拡張性の高いために、`SinkChoice` 作成メソッドをループを使用してリファクタリングしました。 --- nusamai/Cargo.toml | 2 +- nusamai/src/lib.rs | 15 ++++ nusamai/src/main.rs | 79 +++++++++---------- nusamai/src/sink/cesiumtiles/mod.rs | 3 +- nusamai/src/sink/czml/mod.rs | 1 + nusamai/src/sink/geojson/mod.rs | 1 + nusamai/src/sink/geojson_transform_exp/mod.rs | 1 + nusamai/src/sink/gltf_poc/mod.rs | 1 + nusamai/src/sink/gpkg/mod.rs | 1 + nusamai/src/sink/kml/mod.rs | 3 +- nusamai/src/sink/mod.rs | 5 +- nusamai/src/sink/mvt/mod.rs | 3 +- nusamai/src/sink/noop/mod.rs | 3 +- nusamai/src/sink/ply/mod.rs | 1 + nusamai/src/sink/serde/mod.rs | 1 + nusamai/src/sink/shapefile/mod.rs | 1 + nusamai/tests/pipeline.rs | 1 + 17 files changed, 74 insertions(+), 48 deletions(-) diff --git a/nusamai/Cargo.toml b/nusamai/Cargo.toml index 9e3aac7b1..2bb57c583 100644 --- a/nusamai/Cargo.toml +++ b/nusamai/Cargo.toml @@ -10,7 +10,7 @@ serde = { version = "1.0.196", features = ["derive"] } nusamai-plateau = { path = "../nusamai-plateau" } nusamai-citygml = { path = "../nusamai-citygml" } quick-xml = "0.31.0" -clap = { version = "4.5.0", features = ["derive"] } +clap = { version = "4.5.0", features = ["derive", "string"] } thiserror = "1.0.57" ctrlc = "3.4.2" bincode = "1.3.3" diff --git a/nusamai/src/lib.rs b/nusamai/src/lib.rs index 0bcc19018..c2335381f 100644 --- a/nusamai/src/lib.rs +++ b/nusamai/src/lib.rs @@ -3,3 +3,18 @@ pub mod pipeline; pub mod sink; pub mod source; pub mod transformer; + +pub static BUILTIN_SINKS: &[&dyn sink::DataSinkProvider] = &[ + &sink::cesiumtiles::CesiumTilesSinkProvider {}, + &sink::gpkg::GpkgSinkProvider {}, + &sink::mvt::MVTSinkProvider {}, + &sink::geojson::GeoJsonSinkProvider {}, + &sink::geojson_transform_exp::GeoJsonTransformExpSinkProvider {}, + &sink::czml::CzmlSinkProvider {}, + &sink::gltf_poc::GltfPocSinkProvider {}, + &sink::kml::KmlSinkProvider {}, + &sink::ply::StanfordPlySinkProvider {}, + &sink::serde::SerdeSinkProvider {}, + &sink::shapefile::ShapefileSinkProvider {}, + &sink::noop::NoopSinkProvider {}, +]; diff --git a/nusamai/src/main.rs b/nusamai/src/main.rs index 7bb6d6ccc..4a459722d 100644 --- a/nusamai/src/main.rs +++ b/nusamai/src/main.rs @@ -1,43 +1,39 @@ use std::env; -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, Mutex, OnceLock}; use clap::Parser; use nusamai::pipeline::Canceller; -use nusamai::sink::{ - cesiumtiles::CesiumTilesSinkProvider, czml::CzmlSinkProvider, geojson::GeoJsonSinkProvider, - geojson_transform_exp::GeoJsonTransformExpSinkProvider, gltf_poc::GltfPocSinkProvider, - gpkg::GpkgSinkProvider, kml::KmlSinkProvider, mvt::MVTSinkProvider, noop::NoopSinkProvider, - ply::StanfordPlySinkProvider, serde::SerdeSinkProvider, shapefile::ShapefileSinkProvider, -}; use nusamai::sink::{DataSink, DataSinkProvider}; use nusamai::source::citygml::CityGmlSourceProvider; use nusamai::source::{DataSource, DataSourceProvider}; use nusamai::transformer::MultiThreadTransformer; use nusamai::transformer::{NusamaiTransformBuilder, TransformBuilder}; +use nusamai::BUILTIN_SINKS; use nusamai_citygml::CityGmlElement; use nusamai_plateau::models::TopLevelCityObject; #[derive(clap::Parser)] #[command(author, version, about, long_about = None)] struct Args { + /// Specify path patterns to the input CityGML files #[arg()] file_patterns: Vec, - /// Sink choice + /// Select the output format #[arg(value_enum, long)] sink: SinkChoice, - /// Output path + /// Specify the output path #[arg(long)] output: String, - /// Options for the source + /// Add an option for the input (CityGML) #[arg(short = 'i', value_parser = parse_key_val)] sourceopt: Vec<(String, String)>, - /// Options for the sink + /// Add an option for the output format #[arg(short = 'o', value_parser = parse_key_val)] sinkopt: Vec<(String, String)>, } @@ -49,39 +45,41 @@ fn parse_key_val(s: &str) -> Result<(String, String), String> { Ok((s[..pos].into(), s[pos + 1..].into())) } -#[derive(clap::ValueEnum, Clone)] -enum SinkChoice { - Noop, - Serde, - Geojson, - Gpkg, - Mvt, - GeojsonTransformExp, - #[clap(name = "3dtiles")] - CesiumTiles, - Shapefile, - Czml, - Ply, - KML, - GltfPoc, +#[derive(Clone)] +struct SinkChoice(String); + +static SINK_CHOICE_VARIANTS: OnceLock> = OnceLock::new(); + +impl clap::ValueEnum for SinkChoice { + fn value_variants<'a>() -> &'a [Self] { + SINK_CHOICE_VARIANTS.get_or_init(|| { + BUILTIN_SINKS + .iter() + .map(|provider| Self(provider.info().id_name)) + .collect() + }); + SINK_CHOICE_VARIANTS.get().unwrap() + } + + fn to_possible_value(&self) -> Option { + BUILTIN_SINKS + .iter() + .find(|provider| provider.info().id_name == self.0) + .map(|provider| { + let info = provider.info(); + clap::builder::PossibleValue::new(info.id_name).help(info.name) + }) + } } impl SinkChoice { - fn create(&self) -> Box { - match self { - SinkChoice::Noop => Box::new(NoopSinkProvider {}), - SinkChoice::Serde => Box::new(SerdeSinkProvider {}), - SinkChoice::Geojson => Box::new(GeoJsonSinkProvider {}), - SinkChoice::GeojsonTransformExp => Box::new(GeoJsonTransformExpSinkProvider {}), - SinkChoice::Gpkg => Box::new(GpkgSinkProvider {}), - SinkChoice::Mvt => Box::new(MVTSinkProvider {}), - SinkChoice::CesiumTiles => Box::new(CesiumTilesSinkProvider {}), - SinkChoice::Shapefile => Box::new(ShapefileSinkProvider {}), - SinkChoice::Czml => Box::new(CzmlSinkProvider {}), - SinkChoice::Ply => Box::new(StanfordPlySinkProvider {}), - SinkChoice::KML => Box::new(KmlSinkProvider {}), - SinkChoice::GltfPoc => Box::new(GltfPocSinkProvider {}), + fn create(&self) -> &dyn DataSinkProvider { + for &provider in nusamai::BUILTIN_SINKS { + if self.0 == provider.info().id_name { + return provider; + } } + panic!("Unknown sink choice: {:?}", self.0); } } @@ -95,7 +93,6 @@ fn main() { // output path let mut args = Args::parse(); args.sinkopt.push(("@output".into(), args.output.clone())); - args }; diff --git a/nusamai/src/sink/cesiumtiles/mod.rs b/nusamai/src/sink/cesiumtiles/mod.rs index a603d7f2d..233b48312 100644 --- a/nusamai/src/sink/cesiumtiles/mod.rs +++ b/nusamai/src/sink/cesiumtiles/mod.rs @@ -40,7 +40,8 @@ pub struct CesiumTilesSinkProvider {} impl DataSinkProvider for CesiumTilesSinkProvider { fn info(&self) -> SinkInfo { SinkInfo { - name: "Vector Tiles (MVT)".to_string(), + id_name: "3dtiles".to_string(), + name: "Cesium 3D Tiles".to_string(), } } diff --git a/nusamai/src/sink/czml/mod.rs b/nusamai/src/sink/czml/mod.rs index f3d95169d..010832db1 100644 --- a/nusamai/src/sink/czml/mod.rs +++ b/nusamai/src/sink/czml/mod.rs @@ -25,6 +25,7 @@ pub struct CzmlSinkProvider {} impl DataSinkProvider for CzmlSinkProvider { fn info(&self) -> SinkInfo { SinkInfo { + id_name: "czml".to_string(), name: "CZML".to_string(), } } diff --git a/nusamai/src/sink/geojson/mod.rs b/nusamai/src/sink/geojson/mod.rs index 1a44cd542..4cb2e83c4 100644 --- a/nusamai/src/sink/geojson/mod.rs +++ b/nusamai/src/sink/geojson/mod.rs @@ -24,6 +24,7 @@ pub struct GeoJsonSinkProvider {} impl DataSinkProvider for GeoJsonSinkProvider { fn info(&self) -> SinkInfo { SinkInfo { + id_name: "geojson".to_string(), name: "GeoJSON".to_string(), } } diff --git a/nusamai/src/sink/geojson_transform_exp/mod.rs b/nusamai/src/sink/geojson_transform_exp/mod.rs index 8f2222956..374da3e11 100644 --- a/nusamai/src/sink/geojson_transform_exp/mod.rs +++ b/nusamai/src/sink/geojson_transform_exp/mod.rs @@ -25,6 +25,7 @@ pub struct GeoJsonTransformExpSinkProvider {} impl DataSinkProvider for GeoJsonTransformExpSinkProvider { fn info(&self) -> SinkInfo { SinkInfo { + id_name: "geojson-tf-exp".to_string(), name: "GeoJSON".to_string(), } } diff --git a/nusamai/src/sink/gltf_poc/mod.rs b/nusamai/src/sink/gltf_poc/mod.rs index eb755cf37..f26075475 100644 --- a/nusamai/src/sink/gltf_poc/mod.rs +++ b/nusamai/src/sink/gltf_poc/mod.rs @@ -44,6 +44,7 @@ pub struct GltfPocSinkProvider {} impl DataSinkProvider for GltfPocSinkProvider { fn info(&self) -> SinkInfo { SinkInfo { + id_name: "gltf-poc".to_string(), name: "glTF".to_string(), } } diff --git a/nusamai/src/sink/gpkg/mod.rs b/nusamai/src/sink/gpkg/mod.rs index 9739847b8..507bc6736 100644 --- a/nusamai/src/sink/gpkg/mod.rs +++ b/nusamai/src/sink/gpkg/mod.rs @@ -28,6 +28,7 @@ pub struct GpkgSinkProvider {} impl DataSinkProvider for GpkgSinkProvider { fn info(&self) -> SinkInfo { SinkInfo { + id_name: "gpkg".to_string(), name: "GeoPackage".to_string(), } } diff --git a/nusamai/src/sink/kml/mod.rs b/nusamai/src/sink/kml/mod.rs index b47e2b690..d432ed183 100644 --- a/nusamai/src/sink/kml/mod.rs +++ b/nusamai/src/sink/kml/mod.rs @@ -25,7 +25,8 @@ pub struct KmlSinkProvider {} impl DataSinkProvider for KmlSinkProvider { fn info(&self) -> SinkInfo { SinkInfo { - name: "kml".to_string(), + id_name: "kml".to_string(), + name: "KML".to_string(), } } diff --git a/nusamai/src/sink/mod.rs b/nusamai/src/sink/mod.rs index 43190132f..e088bcadc 100644 --- a/nusamai/src/sink/mod.rs +++ b/nusamai/src/sink/mod.rs @@ -6,12 +6,12 @@ pub mod geojson; pub mod geojson_transform_exp; pub mod gltf_poc; pub mod gpkg; +pub mod kml; pub mod mvt; pub mod noop; pub mod ply; pub mod serde; pub mod shapefile; -pub mod kml; use nusamai_citygml::schema::Schema; @@ -20,10 +20,11 @@ use crate::pipeline::{Feedback, PipelineError, Receiver}; use crate::transformer; pub struct SinkInfo { + pub id_name: String, pub name: String, } -pub trait DataSinkProvider { +pub trait DataSinkProvider: Sync { /// Gets basic information about the sink. fn info(&self) -> SinkInfo; diff --git a/nusamai/src/sink/mvt/mod.rs b/nusamai/src/sink/mvt/mod.rs index 4db05b786..5c94f806a 100644 --- a/nusamai/src/sink/mvt/mod.rs +++ b/nusamai/src/sink/mvt/mod.rs @@ -36,7 +36,8 @@ pub struct MVTSinkProvider {} impl DataSinkProvider for MVTSinkProvider { fn info(&self) -> SinkInfo { SinkInfo { - name: "Vector Tiles (MVT)".to_string(), + id_name: "mvt".to_string(), + name: "Mapbox Vector Tiles (MVT)".to_string(), } } diff --git a/nusamai/src/sink/noop/mod.rs b/nusamai/src/sink/noop/mod.rs index 637943e5e..86756084e 100644 --- a/nusamai/src/sink/noop/mod.rs +++ b/nusamai/src/sink/noop/mod.rs @@ -28,7 +28,8 @@ impl DataSinkProvider for NoopSinkProvider { fn info(&self) -> SinkInfo { SinkInfo { - name: "No-op".to_string(), + id_name: "noop".to_string(), + name: "No Output".to_string(), } } diff --git a/nusamai/src/sink/ply/mod.rs b/nusamai/src/sink/ply/mod.rs index c71f8473a..e73e857a8 100644 --- a/nusamai/src/sink/ply/mod.rs +++ b/nusamai/src/sink/ply/mod.rs @@ -37,6 +37,7 @@ pub struct StanfordPlySinkProvider {} impl DataSinkProvider for StanfordPlySinkProvider { fn info(&self) -> SinkInfo { SinkInfo { + id_name: "ply".to_string(), name: "Stanford PLY".to_string(), } } diff --git a/nusamai/src/sink/serde/mod.rs b/nusamai/src/sink/serde/mod.rs index de18ae64f..0e06d583c 100644 --- a/nusamai/src/sink/serde/mod.rs +++ b/nusamai/src/sink/serde/mod.rs @@ -20,6 +20,7 @@ pub struct SerdeSinkProvider {} impl DataSinkProvider for SerdeSinkProvider { fn info(&self) -> SinkInfo { SinkInfo { + id_name: "serde".to_string(), name: "Serde (bincode)".to_string(), } } diff --git a/nusamai/src/sink/shapefile/mod.rs b/nusamai/src/sink/shapefile/mod.rs index bb26bff8d..0fae609a2 100644 --- a/nusamai/src/sink/shapefile/mod.rs +++ b/nusamai/src/sink/shapefile/mod.rs @@ -21,6 +21,7 @@ pub struct ShapefileSinkProvider {} impl DataSinkProvider for ShapefileSinkProvider { fn info(&self) -> SinkInfo { SinkInfo { + id_name: "shapefile".to_string(), name: "Shapefile".to_string(), } } diff --git a/nusamai/tests/pipeline.rs b/nusamai/tests/pipeline.rs index 9bf25d5b1..c0fcd2692 100644 --- a/nusamai/tests/pipeline.rs +++ b/nusamai/tests/pipeline.rs @@ -81,6 +81,7 @@ impl DataSinkProvider for DummySinkProvider { fn info(&self) -> SinkInfo { SinkInfo { + id_name: "dummy".to_string(), name: "Dummy Sink".to_string(), } }