Skip to content

Commit

Permalink
Split Clap code from MbtilesCopier into CopyArgs (#1072)
Browse files Browse the repository at this point in the history
This allows easier reuse of the copy functionality without affecting CLI
commands.

Closes #1071
  • Loading branch information
nyurik authored Dec 16, 2023
1 parent bb802c5 commit 16fbfa5
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 93 deletions.
3 changes: 3 additions & 0 deletions .github/files/markdown.links.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
{
"pattern": "^http://localhost"
},
{
"pattern": "^https://ghcr.io/maplibre/martin($|/|\\?)"
},
{
"pattern": "^http://opensource.org"
}
Expand Down
153 changes: 90 additions & 63 deletions mbtiles/src/bin/mbtiles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ use std::path::{Path, PathBuf};

use clap::{Parser, Subcommand};
use log::error;
use mbtiles::{apply_patch, AggHashType, IntegrityCheckType, MbtResult, Mbtiles, MbtilesCopier};
use mbtiles::{
apply_patch, AggHashType, CopyDuplicateMode, IntegrityCheckType, MbtResult, MbtTypeCli,
Mbtiles, MbtilesCopier,
};
use tilejson::Bounds;

#[derive(Parser, PartialEq, Debug)]
#[command(
Expand Down Expand Up @@ -31,15 +35,15 @@ enum Commands {
file: PathBuf,
},
/// Gets a single value from the MBTiles metadata table.
#[command(name = "meta-get")]
#[command(name = "meta-get", alias = "get-meta")]
MetaGetValue {
/// MBTiles file to read a value from
file: PathBuf,
/// Value to read
key: String,
},
/// Sets a single value in the MBTiles metadata table or deletes it if no value.
#[command(name = "meta-set")]
#[command(name = "meta-set", alias = "set-meta")]
MetaSetValue {
/// MBTiles file to modify
file: PathBuf,
Expand All @@ -50,7 +54,7 @@ enum Commands {
},
/// Copy tiles from one mbtiles file to another.
#[command(name = "copy", alias = "cp")]
Copy(MbtilesCopier),
Copy(CopyArgs),
/// Apply diff file generated from 'copy' command
#[command(name = "apply-patch", alias = "apply-diff")]
ApplyPatch {
Expand All @@ -76,6 +80,43 @@ enum Commands {
},
}

#[derive(Clone, Default, PartialEq, Debug, clap::Args)]
pub struct CopyArgs {
/// MBTiles file to read from
pub src_file: PathBuf,
/// MBTiles file to write to
pub dst_file: PathBuf,
/// Output format of the destination file, ignored if the file exists. If not specified, defaults to the type of source
#[arg(long, alias = "dst-type", alias = "dst_type", value_name = "SCHEMA")]
pub mbtiles_type: Option<MbtTypeCli>,
/// Allow copying to existing files, and indicate what to do if a tile with the same Z/X/Y already exists
#[arg(long, value_enum)]
pub on_duplicate: Option<CopyDuplicateMode>,
/// Minimum zoom level to copy
#[arg(long, conflicts_with("zoom_levels"))]
pub min_zoom: Option<u8>,
/// Maximum zoom level to copy
#[arg(long, conflicts_with("zoom_levels"))]
pub max_zoom: Option<u8>,
/// List of zoom levels to copy
#[arg(long, value_delimiter = ',')]
pub zoom_levels: Vec<u8>,
/// Bounding box to copy, in the format `min_lon,min_lat,max_lon,max_lat`. Can be used multiple times.
#[arg(long)]
pub bbox: Vec<Bounds>,
/// Compare source file with this file, and only copy non-identical tiles to destination.
/// It should be later possible to run `mbtiles apply-diff SRC_FILE DST_FILE` to get the same DIFF file.
#[arg(long, conflicts_with("apply_patch"))]
pub diff_with_file: Option<PathBuf>,
/// Compare source file with this file, and only copy non-identical tiles to destination.
/// It should be later possible to run `mbtiles apply-diff SRC_FILE DST_FILE` to get the same DIFF file.
#[arg(long, conflicts_with("diff_with_file"))]
pub apply_patch: Option<PathBuf>,
/// Skip generating a global hash for mbtiles validation. By default, `mbtiles` will compute `agg_tiles_hash` metadata value.
#[arg(long)]
pub skip_agg_tiles_hash: bool,
}

#[tokio::main]
async fn main() {
let env = env_logger::Env::default().default_filter_or("mbtiles=info");
Expand Down Expand Up @@ -105,6 +146,20 @@ async fn main_int() -> anyhow::Result<()> {
meta_set_value(file.as_path(), &key, value.as_deref()).await?;
}
Commands::Copy(opts) => {
let opts = MbtilesCopier {
src_file: opts.src_file,
dst_file: opts.dst_file,
dst_type_cli: opts.mbtiles_type,
dst_type: None,
on_duplicate: opts.on_duplicate,
min_zoom: opts.min_zoom,
max_zoom: opts.max_zoom,
zoom_levels: opts.zoom_levels,
bbox: opts.bbox,
diff_with_file: opts.diff_with_file,
apply_patch: opts.apply_patch,
skip_agg_tiles_hash: opts.skip_agg_tiles_hash,
};
opts.run().await?;
}
Commands::ApplyPatch {
Expand Down Expand Up @@ -176,7 +231,7 @@ mod tests {

use clap::error::ErrorKind;
use clap::Parser;
use mbtiles::{CopyDuplicateMode, MbtilesCopier};
use mbtiles::CopyDuplicateMode;

use super::*;
use crate::Commands::{ApplyPatch, Copy, MetaGetValue, MetaSetValue, Validate};
Expand All @@ -198,20 +253,17 @@ mod tests {
Args::parse_from(["mbtiles", "copy", "src_file", "dst_file"]),
Args {
verbose: false,
command: Copy(MbtilesCopier::new(
PathBuf::from("src_file"),
PathBuf::from("dst_file")
))
command: Copy(CopyArgs {
src_file: PathBuf::from("src_file"),
dst_file: PathBuf::from("dst_file"),
..Default::default()
})
}
);
}

#[test]
fn test_copy_min_max_zoom_arguments() {
let mut opt = MbtilesCopier::new(PathBuf::from("src_file"), PathBuf::from("dst_file"));
opt.min_zoom = Some(1);
opt.max_zoom = Some(100);

let args = Args::parse_from([
"mbtiles",
"copy",
Expand All @@ -226,7 +278,13 @@ mod tests {
args,
Args {
verbose: false,
command: Copy(opt)
command: Copy(CopyArgs {
src_file: PathBuf::from("src_file"),
dst_file: PathBuf::from("dst_file"),
min_zoom: Some(1),
max_zoom: Some(100),
..Default::default()
})
}
);
}
Expand Down Expand Up @@ -271,8 +329,6 @@ mod tests {

#[test]
fn test_copy_zoom_levels_arguments() {
let mut opt = MbtilesCopier::new(PathBuf::from("src_file"), PathBuf::from("dst_file"));
opt.zoom_levels.extend(&[3, 7, 1]);
assert_eq!(
Args::parse_from([
"mbtiles",
Expand All @@ -284,15 +340,18 @@ mod tests {
]),
Args {
verbose: false,
command: Copy(opt)
command: Copy(CopyArgs {
src_file: PathBuf::from("src_file"),
dst_file: PathBuf::from("dst_file"),
zoom_levels: vec![3, 7, 1],
..Default::default()
})
}
);
}

#[test]
fn test_copy_diff_with_file_arguments() {
let mut opt = MbtilesCopier::new(PathBuf::from("src_file"), PathBuf::from("dst_file"));
opt.diff_with_file = Some(PathBuf::from("no_file"));
assert_eq!(
Args::parse_from([
"mbtiles",
Expand All @@ -304,15 +363,18 @@ mod tests {
]),
Args {
verbose: false,
command: Copy(opt)
command: Copy(CopyArgs {
src_file: PathBuf::from("src_file"),
dst_file: PathBuf::from("dst_file"),
diff_with_file: Some(PathBuf::from("no_file")),
..Default::default()
})
}
);
}

#[test]
fn test_copy_diff_with_override_copy_duplicate_mode() {
let mut opt = MbtilesCopier::new(PathBuf::from("src_file"), PathBuf::from("dst_file"));
opt.on_duplicate = Some(CopyDuplicateMode::Override);
assert_eq!(
Args::parse_from([
"mbtiles",
Expand All @@ -324,47 +386,12 @@ mod tests {
]),
Args {
verbose: false,
command: Copy(opt)
}
);
}

#[test]
fn test_copy_diff_with_ignore_copy_duplicate_mode() {
let mut opt = MbtilesCopier::new(PathBuf::from("src_file"), PathBuf::from("dst_file"));
opt.on_duplicate = Some(CopyDuplicateMode::Ignore);
assert_eq!(
Args::parse_from([
"mbtiles",
"copy",
"src_file",
"dst_file",
"--on-duplicate",
"ignore"
]),
Args {
verbose: false,
command: Copy(opt)
}
);
}

#[test]
fn test_copy_diff_with_abort_copy_duplicate_mode() {
let mut opt = MbtilesCopier::new(PathBuf::from("src_file"), PathBuf::from("dst_file"));
opt.on_duplicate = Some(CopyDuplicateMode::Abort);
assert_eq!(
Args::parse_from([
"mbtiles",
"copy",
"src_file",
"dst_file",
"--on-duplicate",
"abort"
]),
Args {
verbose: false,
command: Copy(opt)
command: Copy(CopyArgs {
src_file: PathBuf::from("src_file"),
dst_file: PathBuf::from("dst_file"),
on_duplicate: Some(CopyDuplicateMode::Override),
..Default::default()
})
}
);
}
Expand Down
24 changes: 1 addition & 23 deletions mbtiles/src/copier.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
use std::fmt::Write as _;
use std::path::PathBuf;

#[cfg(feature = "cli")]
use clap::{Args, ValueEnum};
use enum_display::EnumDisplay;
use itertools::Itertools as _;
use log::{debug, info, trace};
Expand All @@ -24,7 +22,7 @@ use crate::{

#[derive(PartialEq, Eq, Debug, Clone, Copy, EnumDisplay, Serialize, Deserialize)]
#[enum_display(case = "Kebab")]
#[cfg_attr(feature = "cli", derive(ValueEnum))]
#[cfg_attr(feature = "cli", derive(clap::ValueEnum))]
pub enum CopyDuplicateMode {
Override,
Ignore,
Expand All @@ -43,52 +41,32 @@ impl CopyDuplicateMode {
}

#[derive(Clone, Default, PartialEq, Debug)]
#[cfg_attr(feature = "cli", derive(Args))]
pub struct MbtilesCopier {
/// MBTiles file to read from
pub src_file: PathBuf,
/// MBTiles file to write to
pub dst_file: PathBuf,
/// Output format of the destination file, ignored if the file exists. If not specified, defaults to the type of source
#[cfg_attr(
feature = "cli",
arg(
long = "mbtiles-type",
alias = "dst-type",
alias = "dst_type",
value_name = "SCHEMA",
value_enum
)
)]
pub dst_type_cli: Option<MbtTypeCli>,
/// Destination type with options
#[cfg_attr(feature = "cli", arg(skip))]
pub dst_type: Option<MbtType>,
/// Allow copying to existing files, and indicate what to do if a tile with the same Z/X/Y already exists
#[cfg_attr(feature = "cli", arg(long, value_enum))]
pub on_duplicate: Option<CopyDuplicateMode>,
/// Minimum zoom level to copy
#[cfg_attr(feature = "cli", arg(long, conflicts_with("zoom_levels")))]
pub min_zoom: Option<u8>,
/// Maximum zoom level to copy
#[cfg_attr(feature = "cli", arg(long, conflicts_with("zoom_levels")))]
pub max_zoom: Option<u8>,
/// List of zoom levels to copy
#[cfg_attr(feature = "cli", arg(long, value_delimiter = ','))]
pub zoom_levels: Vec<u8>,
/// Bounding box to copy, in the format `min_lon,min_lat,max_lon,max_lat`. Can be used multiple times.
#[cfg_attr(feature = "cli", arg(long))]
pub bbox: Vec<Bounds>,
/// Compare source file with this file, and only copy non-identical tiles to destination.
/// It should be later possible to run `mbtiles apply-diff SRC_FILE DST_FILE` to get the same DIFF file.
#[cfg_attr(feature = "cli", arg(long, conflicts_with("apply_patch")))]
pub diff_with_file: Option<PathBuf>,
/// Compare source file with this file, and only copy non-identical tiles to destination.
/// It should be later possible to run `mbtiles apply-diff SRC_FILE DST_FILE` to get the same DIFF file.
#[cfg_attr(feature = "cli", arg(long, conflicts_with("diff_with_file")))]
pub apply_patch: Option<PathBuf>,
/// Skip generating a global hash for mbtiles validation. By default, `mbtiles` will compute `agg_tiles_hash` metadata value.
#[cfg_attr(feature = "cli", arg(long))]
pub skip_agg_tiles_hash: bool,
}

Expand Down
4 changes: 1 addition & 3 deletions mbtiles/src/mbtiles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ use std::ffi::OsStr;
use std::fmt::{Display, Formatter};
use std::path::Path;

#[cfg(feature = "cli")]
use clap::ValueEnum;
use enum_display::EnumDisplay;
use log::debug;
use serde::{Deserialize, Serialize};
Expand All @@ -16,7 +14,7 @@ use crate::{invert_y_value, CopyDuplicateMode, MbtType};

#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize, EnumDisplay)]
#[enum_display(case = "Kebab")]
#[cfg_attr(feature = "cli", derive(ValueEnum))]
#[cfg_attr(feature = "cli", derive(clap::ValueEnum))]
pub enum MbtTypeCli {
Flat,
FlatWithHash,
Expand Down
6 changes: 2 additions & 4 deletions mbtiles/src/validation.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
use std::collections::HashSet;
use std::str::from_utf8;

#[cfg(feature = "cli")]
use clap::ValueEnum;
use enum_display::EnumDisplay;
use log::{debug, info, warn};
use martin_tile_utils::{Format, TileInfo, MAX_ZOOM};
Expand Down Expand Up @@ -52,7 +50,7 @@ impl MbtType {

#[derive(PartialEq, Eq, Default, Debug, Clone, EnumDisplay)]
#[enum_display(case = "Kebab")]
#[cfg_attr(feature = "cli", derive(ValueEnum))]
#[cfg_attr(feature = "cli", derive(clap::ValueEnum))]
pub enum IntegrityCheckType {
#[default]
Quick,
Expand All @@ -62,7 +60,7 @@ pub enum IntegrityCheckType {

#[derive(PartialEq, Eq, Default, Debug, Clone, EnumDisplay)]
#[enum_display(case = "Kebab")]
#[cfg_attr(feature = "cli", derive(ValueEnum))]
#[cfg_attr(feature = "cli", derive(clap::ValueEnum))]
pub enum AggHashType {
/// Verify that the aggregate tiles hash value in the metadata table matches the computed value. Used by default.
#[default]
Expand Down

0 comments on commit 16fbfa5

Please sign in to comment.