1use clap::{Args, Parser, Subcommand};
2use std::path::PathBuf;
3use strum_macros::AsRefStr;
4
5use utiles_core::{
6 BBox, LngLat, TileStringFormatter, VERSION, ZoomSet, geobbox_merge,
7 parsing::parse_bbox_ext, zoom,
8};
9
10use crate::cli::commands::dev::DevArgs;
11use crate::cli::commands::serve::ServeArgs;
12use crate::cli::commands::shapes::ShapesArgs;
13use crate::cli::commands::{analyze_main, header_main, vacuum_main};
14use crate::copy::CopyConfig;
15use crate::errors::UtilesResult;
16use crate::hash_types::HashType;
17use crate::mbt::{MbtType, TilesFilter};
18use crate::sqlite::InsertStrategy;
19
20fn about() -> String {
29 let is_debug: bool = cfg!(debug_assertions);
30 if is_debug {
31 format!("utiles cli (rust) ~ v{VERSION} ~ DEBUG")
32 } else {
33 format!("utiles cli (rust) ~ v{VERSION}")
34 }
35}
36
37#[derive(Debug, Parser)]
38pub struct AboutArgs {
39 #[arg(required = false, long, short, action = clap::ArgAction::SetTrue,)]
41 pub json: bool,
42}
43
44#[derive(Debug, Parser)]
45#[command(name = "ut", about = about(), version = VERSION, author, max_term_width = 120)]
46pub struct Cli {
47 #[arg(long, global = true, default_value = "false", action = clap::ArgAction::SetTrue)]
49 pub debug: bool,
50
51 #[arg(long, global = true, default_value = "false", action = clap::ArgAction::SetTrue)]
53 pub trace: bool,
54
55 #[arg(long, global = true, default_value = "false", action = clap::ArgAction::SetTrue)]
57 pub log_json: bool,
58
59 #[command(subcommand)]
61 pub command: Commands,
62}
63
64#[derive(Debug, Parser)]
65pub struct TileInputStreamArgs {
66 #[arg(required = false)]
67 pub input: Option<String>,
68}
69
70fn tile_fmt_string_long_help() -> String {
71 r#"Format string for tiles (default: `{json_arr}`)
72
73Example:
74 > utiles tiles 1 * --fmt "http://thingy.com/{z}/{x}/{y}.png"
75 http://thingy.com/1/0/0.png
76 http://thingy.com/1/0/1.png
77 http://thingy.com/1/1/0.png
78 http://thingy.com/1/1/1.png
79 > utiles tiles 1 * --fmt "SELECT * FROM tiles WHERE zoom_level = {z} AND tile_column = {x} AND tile_row = {-y};"
80 SELECT * FROM tiles WHERE zoom_level = 1 AND tile_column = 0 AND tile_row = 1;
81 SELECT * FROM tiles WHERE zoom_level = 1 AND tile_column = 0 AND tile_row = 0;
82 SELECT * FROM tiles WHERE zoom_level = 1 AND tile_column = 1 AND tile_row = 1;
83 SELECT * FROM tiles WHERE zoom_level = 1 AND tile_column = 1 AND tile_row = 0;
84
85fmt-tokens:
86 `{json_arr}`/`{json}` -> [x, y, z]
87 `{json_obj}`/`{obj}` -> {x: x, y: y, z: z}
88 `{quadkey}`/`{qk}` -> quadkey string
89 `{pmtileid}`/`{pmid}` -> pmtile-id
90 `{x}` -> x tile coord
91 `{y}` -> y tile coord
92 `{z}` -> z/zoom level
93 `{-y}`/`{yup}` -> y tile coord flipped/tms
94 `{zxy}` -> z/x/y
95 `{bbox}` -> [w, s, e, n] bbox lnglat (wgs84)
96 `{projwin}` -> ulx,uly,lrx,lry projwin 4 gdal (wgs84)
97 `{bbox_web}` -> [w, s, e, n] bbox web-mercator (epsg:3857)
98 `{projwin_web}` -> ulx,uly,lrx,lry projwin 4 gdal (epsg:3857)
99 "#
100 .to_string()
101}
102#[derive(Debug, Parser)]
103pub struct TileFmtOptions {
104 #[arg(required = false, long, action = clap::ArgAction::SetTrue)]
106 pub seq: bool,
107
108 #[arg(required = false, long, action = clap::ArgAction::SetTrue)]
110 pub obj: bool,
111
112 #[arg(
114 required = false,
115 long,
116 alias = "fmt",
117 short = 'F',
118 conflicts_with = "obj",
119 long_help = tile_fmt_string_long_help()
120 )]
121 pub fmt: Option<String>,
122}
123
124impl TileFmtOptions {
125 #[must_use]
126 pub fn formatter(&self) -> TileStringFormatter {
127 if let Some(fmt) = &self.fmt {
128 TileStringFormatter::new(fmt)
129 } else if self.obj {
130 TileStringFormatter::new("{json_obj}")
131 } else {
132 TileStringFormatter::default()
133 }
134 }
135}
136impl From<&TileFmtOptions> for TileStringFormatter {
137 fn from(opts: &TileFmtOptions) -> Self {
138 opts.formatter()
139 }
140}
141
142#[derive(Debug, Parser)]
143pub struct TilesArgs {
144 #[arg(required = true, value_parser = clap::value_parser ! (u8).range(0..=30))]
146 pub zoom: u8,
147
148 #[command(flatten)]
149 pub inargs: TileInputStreamArgs,
150
151 #[command(flatten)]
152 pub fmtopts: TileFmtOptions,
153}
154
155#[derive(Debug, Parser)]
156pub struct TileFmtArgs {
157 #[command(flatten)]
158 pub inargs: TileInputStreamArgs,
159
160 #[command(flatten)]
161 pub fmtopts: TileFmtOptions,
162}
163#[derive(Debug, Parser)]
164pub struct EdgesArgs {
165 #[arg(required = false, long, action = clap::ArgAction::SetTrue,)]
167 pub wrapx: bool,
168
169 #[command(flatten)]
170 pub inargs: TileInputStreamArgs,
171
172 #[command(flatten)]
173 pub fmtopts: TileFmtOptions,
174}
175
176#[derive(Debug, Parser)]
177pub struct BurnArgs {
178 #[arg(required = true, value_parser = clap::value_parser ! (u8).range(0..=30))]
180 pub zoom: u8,
181
182 #[command(flatten)]
183 pub inargs: TileInputStreamArgs,
184
185 #[command(flatten)]
186 pub fmtopts: TileFmtOptions,
187}
188
189#[derive(Debug, Parser)]
190pub struct MergeArgs {
191 #[arg(long, short = 'Z', default_value = "0")]
193 pub minzoom: u8,
194
195 #[arg(required = false, short, long, action = clap::ArgAction::SetTrue)]
196 pub sort: bool,
197
198 #[command(flatten)]
199 pub inargs: TileInputStreamArgs,
200
201 #[command(flatten)]
202 pub fmtopts: TileFmtOptions,
203}
204
205#[derive(Debug, Parser)]
206pub struct FmtStrArgs {
207 #[command(flatten)]
208 pub inargs: TileInputStreamArgs,
209
210 #[command(flatten)]
211 pub fmtopts: TileFmtOptions,
212}
213
214#[derive(Debug, Parser)]
215pub struct ParentChildrenArgs {
216 #[command(flatten)]
217 pub inargs: TileInputStreamArgs,
218
219 #[command(flatten)]
220 pub fmtopts: TileFmtOptions,
221
222 #[arg(required = false, long, default_value = "1")]
223 pub depth: u8,
224}
225
226#[derive(Debug, Parser)]
227pub struct SqliteDbCommonArgs {
228 #[arg(required = true)]
230 pub filepath: String,
231
232 #[arg(required = false, short, long, action = clap::ArgAction::SetTrue)]
234 pub min: bool,
235}
236
237#[derive(Debug, Parser)]
238pub struct TilesFilterArgs {
239 #[arg(required = false, long, value_parser = parse_bbox_ext, allow_hyphen_values = true)]
241 pub bbox: Option<Vec<BBox>>,
242
243 #[command(flatten)]
244 pub zoom: Option<ZoomArgGroup>,
245}
246
247impl TilesFilterArgs {
248 #[must_use]
249 pub fn zooms(&self) -> Option<Vec<u8>> {
250 match &self.zoom {
251 Some(zoom) => zoom.zooms(),
252 None => None,
253 }
254 }
255
256 #[must_use]
257 pub fn bboxes(&self) -> Option<Vec<BBox>> {
258 self.bbox.clone()
259 }
260
261 #[must_use]
262 pub fn tiles_filter_maybe(&self) -> Option<TilesFilter> {
263 if self.bbox.is_none() && self.zoom.is_none() {
264 None
265 } else {
266 Some(TilesFilter::new(self.bboxes(), self.zooms()))
267 }
268 }
269}
270
271#[derive(Debug, Parser, Clone, clap::ValueEnum)]
272pub enum DbtypeOption {
273 Flat,
274 Hash,
275 Norm,
276}
277
278impl From<&DbtypeOption> for MbtType {
279 fn from(opt: &DbtypeOption) -> Self {
280 match opt {
281 DbtypeOption::Flat => Self::Flat,
282 DbtypeOption::Hash => Self::Hash,
283 DbtypeOption::Norm => Self::Norm,
284 }
285 }
286}
287
288#[derive(Debug, Parser, Clone)]
289pub struct TouchArgs {
290 #[arg(required = true)]
292 pub filepath: String,
293
294 #[arg(required = false, long)]
296 pub page_size: Option<i64>,
297
298 #[arg(
300 required = false, long = "dbtype", aliases = ["db-type", "mbtype", "mbt-type"], default_value = "flat"
301 )]
302 pub dbtype: Option<DbtypeOption>,
303}
304
305impl TouchArgs {
306 #[must_use]
307 pub fn mbtype(&self) -> MbtType {
308 self.dbtype.as_ref().map_or(MbtType::Flat, |opt| opt.into())
309 }
310}
311
312#[derive(Debug, Subcommand)]
313pub enum SqliteCommands {
315 Analyze(AnalyzeArgs),
316 Header(SqliteHeaderArgs),
317 Vacuum(VacuumArgs),
318}
319
320impl SqliteCommands {
321 pub async fn run(&self) -> UtilesResult<()> {
322 match self {
323 Self::Analyze(args) => analyze_main(args).await,
324 Self::Header(args) => header_main(args).await,
325 Self::Vacuum(args) => vacuum_main(args).await,
326 }
327 }
328}
329
330#[derive(Debug, Parser)]
337pub struct AnalyzeArgs {
339 #[command(flatten)]
340 pub common: SqliteDbCommonArgs,
341
342 #[arg(required = false, long)]
343 pub analysis_limit: Option<usize>,
344}
345
346#[derive(Debug, Parser)]
347pub struct SqliteHeaderArgs {
349 #[command(flatten)]
350 pub common: SqliteDbCommonArgs,
351}
352
353#[derive(Debug, Parser)]
354pub struct VacuumArgs {
356 #[command(flatten)]
357 pub common: SqliteDbCommonArgs,
358
359 #[arg(required = false)]
361 pub into: Option<String>,
362
363 #[arg(required = false, long, short, action = clap::ArgAction::SetTrue)]
365 pub analyze: bool,
366
367 #[arg(required = false, long)]
369 pub page_size: Option<i64>,
370}
371
372#[derive(Debug, Parser)]
373pub struct MetadataArgs {
374 #[command(flatten)]
375 pub common: SqliteDbCommonArgs,
376
377 #[arg(required = false, long, action = clap::ArgAction::SetTrue)]
379 pub obj: bool,
380
381 #[arg(required = false, long, action = clap::ArgAction::SetTrue, default_value = "false")]
383 pub raw: bool,
384}
385
386#[derive(Debug, Parser)]
387pub struct MetadataSetArgs {
388 #[command(flatten)]
389 pub common: SqliteDbCommonArgs,
390
391 #[arg(required = true, value_name = "KEY/FSPATH")]
393 pub key: String,
394
395 #[arg(required = false)]
397 pub value: Option<String>,
398
399 #[arg(
401 required = false, long, aliases = ["dry-run"], short = 'n', action = clap::ArgAction::SetTrue
402 )]
403 pub dryrun: bool,
404}
405
406#[derive(Debug, Parser)]
407pub struct TilejsonArgs {
408 #[command(flatten)]
409 pub common: SqliteDbCommonArgs,
410
411 #[arg(required = false, short, long, action = clap::ArgAction::SetTrue)]
413 pub tilestats: bool,
414}
415
416#[derive(Debug, Parser)]
417pub struct LintArgs {
418 #[arg(required = true, num_args(1..))]
420 pub(crate) fspaths: Vec<String>,
421
422 #[arg(
424 required = false, long, action = clap::ArgAction::SetTrue,
425 default_value = "false", hide = true
426 )]
427 pub(crate) fix: bool,
428}
429
430#[derive(Debug, Parser)]
431pub struct InfoArgs {
432 #[command(flatten)]
433 pub common: SqliteDbCommonArgs,
434
435 #[arg(required = false, long, action = clap::ArgAction::SetTrue)]
436 pub(crate) full: bool,
437
438 #[arg(required = false, long, short, visible_aliases = ["stats"], action = clap::ArgAction::SetTrue)]
439 pub(crate) statistics: bool,
440}
441
442#[derive(Debug, Parser)]
443pub struct AggHashArgs {
444 #[command(flatten)]
445 pub common: SqliteDbCommonArgs,
446
447 #[command(flatten)]
448 pub filter_args: TilesFilterArgs,
449 #[arg(required = false, long)]
454 pub hash: Option<HashType>,
455}
456
457#[derive(Debug, Parser)]
458pub struct UpdateArgs {
459 #[command(flatten)]
460 pub common: SqliteDbCommonArgs,
461
462 #[arg(required = false, long, short = 'n', action = clap::ArgAction::SetTrue)]
464 pub(crate) dryrun: bool,
465}
466#[derive(Debug, Parser)]
467pub struct ZxyifyArgs {
468 #[command(flatten)]
469 pub common: SqliteDbCommonArgs,
470
471 #[arg(required = false, long, action = clap::ArgAction::SetTrue)]
473 pub(crate) rm: bool,
474}
475#[derive(Debug, Parser)]
476#[command(
477 name = "enumerate",
478 about = "enumerate db tiles like `tippecanoe-enumerate`"
479)]
480pub struct EnumerateArgs {
481 #[arg(required = true)]
482 pub(crate) fspaths: Vec<String>,
483
484 #[command(flatten)]
485 pub filter_args: TilesFilterArgs,
486
487 #[arg(required = false, long, short = 't', action = clap::ArgAction::SetTrue)]
489 pub(crate) tippecanoe: bool,
490}
491
492#[derive(Debug, Parser)]
493pub struct OptimizeArgs {
495 #[command(flatten)]
496 pub common: SqliteDbCommonArgs,
497
498 #[arg(required = true)]
500 pub dst: String,
501}
502
503#[derive(Debug, Parser)]
526pub struct WebpifyArgs {
527 #[command(flatten)]
528 pub common: SqliteDbCommonArgs,
529
530 #[arg(required = true)]
532 pub dst: String,
533
534 #[arg(required = false, long, short)]
536 pub jobs: Option<u8>,
537
538 #[arg(required = false, long, short, action = clap::ArgAction::SetTrue)]
540 pub(crate) quiet: bool,
541}
542
543#[derive(Debug, Parser)]
544pub struct CommandsArgs {
545 #[arg(required = false, short, long, action = clap::ArgAction::SetTrue)]
546 pub full: bool,
547
548 #[arg(required = false, short, long, action = clap::ArgAction::SetTrue)]
549 pub(crate) table: bool,
550 }
554
555#[derive(Debug, Subcommand)]
556pub enum Commands {
557 #[command(name = "about", visible_alias = "aboot")]
562 About(AboutArgs),
563
564 #[command(name = "commands", visible_alias = "cmds")]
566 Commands(CommandsArgs),
567
568 #[command(subcommand, visible_alias = "db")]
569 Sqlite(SqliteCommands),
570
571 #[command(name = "tilejson", visible_alias = "tj", alias = "trader-joes")]
573 Tilejson(TilejsonArgs),
574
575 #[command(name = "touch")]
577 Touch(TouchArgs),
578
579 #[command(name = "copy", visible_alias = "cp")]
581 Copy(CopyArgs),
582
583 #[command(name = "lint")]
585 Lint(LintArgs),
586
587 #[command(name = "agg-hash")]
589 AggHash(AggHashArgs),
590
591 #[command(name = "metadata", visible_aliases = ["meta", "md"])]
593 Metadata(MetadataArgs),
594
595 #[command(name = "metadata-set", visible_aliases = ["meta-set", "mds"])]
597 MetadataSet(MetadataSetArgs),
598
599 #[command(name = "update", visible_aliases = ["up"])]
601 Update(UpdateArgs),
602
603 #[command(name = "enumerate", visible_aliases = ["enum"])]
605 Enumerate(EnumerateArgs),
606
607 #[command(name = "rimraf", visible_alias = "rmrf", hide = true)]
609 Rimraf(RimrafArgs),
610
611 #[command(name = "info")]
613 Info(InfoArgs),
614
615 #[command(name = "vacuum", visible_alias = "vac")]
616 Vacuum(VacuumArgs),
617
618 #[command(name = "dbcontains")]
620 Contains {
621 #[arg(required = true)]
623 filepath: String,
624
625 #[arg(required = true)]
627 lnglat: LngLat,
628 },
629
630 #[command(name = "zxyify", aliases = ["xyzify"])]
634 Zxyify(ZxyifyArgs),
635
636 #[command(
666 name = "fmtstr",
667 aliases = & ["fmt", "xt"],
668 verbatim_doc_comment,
669 )]
670 Fmt(TileFmtArgs),
671
672 #[command(
685 name = "bounding-tile",
686 verbatim_doc_comment,
687 about = "Echo bounding tile at zoom for bbox / geojson"
688 )]
689 BoundingTile(TileFmtArgs),
690
691 #[command(name = "quadkey", verbatim_doc_comment, visible_alias = "qk")]
707 Quadkey(TileFmtArgs),
708
709 #[command(name = "pmtileid", verbatim_doc_comment, visible_alias = "pmid")]
725 Pmtileid(TileFmtArgs),
726
727 #[command(
762 name = "tiles",
763 verbatim_doc_comment,
764 about = "Echo tiles at zoom intersecting geojson bbox / feature / collection"
765 )]
766 Tiles(TilesArgs),
767
768 #[command(name = "neighbors")]
775 Neighbors(TileFmtArgs),
776
777 #[command(name = "children", verbatim_doc_comment)]
789 Children(ParentChildrenArgs),
790
791 #[command(name = "parent")]
793 Parent(ParentChildrenArgs),
794
795 #[command(name = "shapes")]
807 Shapes(ShapesArgs),
808
809 #[command(name = "burn", visible_alias = "cover")]
811 Burn(BurnArgs),
812
813 #[command(name = "merge")]
815 Merge(MergeArgs),
816
817 #[command(name = "edges")]
819 Edges(EdgesArgs),
820
821 #[command(
823 name = "webpify",
824 about = "Convert raster mbtiles to webp format",
825 hide = true
826 )]
827 Webpify(WebpifyArgs),
828
829 #[command(
831 name = "optimize",
832 about = "Optimize tiles-db",
833 aliases = ["opt"],
834 hide = true
835 )]
836 Optimize(OptimizeArgs),
837
838 #[command(name = "serve", hide = true)]
840 Serve(ServeArgs),
841
842 #[command(name = "dev", hide = true)]
844 Dev(DevArgs),
845
846 #[command(name = "addo", hide = true)]
851 Addo,
852
853 #[command(name = "translate", hide = true)]
855 Translate,
856}
857
858#[derive(Debug, Parser, Clone)]
859#[command(name = "rimraf", about = "rm-rf dirpath")]
860pub struct RimrafArgs {
861 #[arg(required = true)]
863 pub dirpath: String,
864
865 #[arg(required = false, long, action = clap::ArgAction::SetTrue)]
867 pub(crate) size: bool,
868
869 #[arg(required = false, short = 'n', long, action = clap::ArgAction::SetTrue)]
871 pub(crate) dryrun: bool,
872
873 #[arg(required = false, short, long, action = clap::ArgAction::SetTrue)]
874 pub(crate) verbose: bool,
875}
876
877#[derive(Args, Debug)]
878#[group(required = false, multiple = false, id = "minmaxzoom")]
879pub struct MinMaxZoom {
880 #[arg(long)]
882 minzoom: Option<u8>,
883
884 #[arg(long)]
886 maxzoom: Option<u8>,
887}
888
889#[derive(Debug, Parser)]
890pub struct ZoomArgGroup {
891 #[arg(short, long, required = false, value_delimiter = ',', value_parser = zoom::parse_zooms)]
893 pub zoom: Option<Vec<Vec<u8>>>,
894
895 #[arg(long, conflicts_with = "zoom", aliases = ["min-zoom", "min-z"])]
897 pub minzoom: Option<u8>,
898
899 #[arg(long, conflicts_with = "zoom", aliases = ["max-zoom", "max-z"])]
901 pub maxzoom: Option<u8>,
902}
903
904impl ZoomArgGroup {
905 #[must_use]
906 pub fn zooms(&self) -> Option<Vec<u8>> {
907 match &self.zoom {
908 Some(zooms) => Some(zooms.iter().flatten().copied().collect()),
909 None => match (self.minzoom, self.maxzoom) {
910 (Some(minzoom), Some(maxzoom)) => Some((minzoom..=maxzoom).collect()),
911 (Some(minzoom), None) => Some((minzoom..=31).collect()),
912 (None, Some(maxzoom)) => Some((0..=maxzoom).collect()),
913 (None, None) => None,
914 },
915 }
916 }
917}
918
919#[derive(
920 Debug, Copy, Parser, Clone, clap::ValueEnum, strum::EnumString, AsRefStr, Default,
921)]
922#[strum(serialize_all = "kebab-case")]
923pub enum ConflictStrategy {
924 #[default]
925 Undefined,
926 Ignore,
927 Replace,
928 Abort,
929 Fail,
930}
931
932impl From<ConflictStrategy> for InsertStrategy {
933 fn from(cs: ConflictStrategy) -> Self {
934 match cs {
935 ConflictStrategy::Undefined => Self::None,
936 ConflictStrategy::Ignore => Self::Ignore,
937 ConflictStrategy::Replace => Self::Replace,
938 ConflictStrategy::Abort => Self::Abort,
939 ConflictStrategy::Fail => Self::Fail,
940 }
941 }
942}
943
944#[derive(Debug, Parser)]
945#[command(name = "copy", about = "Copy tiles from src -> dst")]
946pub struct CopyArgs {
947 #[arg(required = true)]
949 pub src: String,
950
951 #[arg(required = true)]
953 pub dst: String,
954
955 #[arg(required = false, long, short = 'n', action = clap::ArgAction::SetTrue)]
957 pub dryrun: bool,
958
959 #[arg(required = false, long, short, action = clap::ArgAction::SetTrue)]
961 pub force: bool,
962
963 #[command(flatten)]
964 pub zoom: Option<ZoomArgGroup>,
965
966 #[arg(required = false, long, value_parser = parse_bbox_ext, allow_hyphen_values = true)]
968 pub bbox: Option<BBox>,
969
970 #[arg(required = false, long, short, default_value = "undefined")]
972 pub conflict: ConflictStrategy,
973
974 #[arg(
976 required = false,
977 long = "dst-type",
978 aliases = ["dbtype", "dsttype", "mbtype", "mbt-type", "mbtiles-type"]
979 )]
980 pub dst_type: Option<DbtypeOption>,
981
982 #[arg(required = false, long)]
984 pub hash: Option<HashType>,
985
986 #[arg(required = false, long, short)]
988 pub jobs: Option<u8>,
989
990 #[arg(required = false, long, hide = true, action = clap::ArgAction::SetTrue)]
992 pub stream: bool,
993}
994
995impl CopyArgs {
996 #[must_use]
997 pub fn zooms(&self) -> Option<Vec<u8>> {
998 match &self.zoom {
999 Some(zoom) => zoom.zooms(),
1000 None => None,
1001 }
1002 }
1003
1004 #[must_use]
1005 pub fn zoom_set(&self) -> Option<ZoomSet> {
1006 self.zooms().map(|zooms| ZoomSet::from_zooms(&zooms))
1007 }
1008
1009 #[must_use]
1010 pub fn bboxes(&self) -> Option<Vec<BBox>> {
1011 self.bbox.as_ref().map(|bbox| vec![*bbox])
1012 }
1013
1014 #[must_use]
1015 pub fn bounds(&self) -> Option<String> {
1016 if let Some(bboxes) = self.bboxes() {
1017 let new_bbox = geobbox_merge(&bboxes);
1018 Some(new_bbox.mbt_bounds())
1019 } else {
1020 None
1021 }
1022 }
1023}
1024
1025impl From<&CopyArgs> for CopyConfig {
1026 fn from(args: &CopyArgs) -> Self {
1027 let dbtype = args.dst_type.as_ref().map(|dbtype| dbtype.into());
1028 Self {
1029 src: PathBuf::from(&args.src),
1030 dst: PathBuf::from(&args.dst),
1031 zset: args.zoom_set(),
1032 zooms: args.zooms(),
1033 verbose: true,
1034 bboxes: args.bboxes(),
1035 bounds_string: args.bounds(),
1036 force: false,
1037 dryrun: false,
1038 jobs: args.jobs,
1039 istrat: InsertStrategy::from(args.conflict),
1040 hash: args.hash,
1041 dst_type: dbtype,
1042 stream: args.stream,
1043 }
1044 }
1045}