pub mod stages;
use clap::Subcommand;
use super::extract::{
osm_admin_levels, osm_house_numbers, osm_pbf_blob_chunks, osm_pbf_data, osm_pbf_header,
};
use super::index::{addresses, admin_levels_hierarchy, coordinates};
use super::optimize::{
delete_intermediary_data, delete_isolated_admin_levels, merge_admin_levels, sqlite_file,
};
use crate::admin_level::level;
use crate::database::store::{self, base_files};
use stages::stage;
#[derive(Subcommand)]
pub enum exec_commands {
#[command(name = stage::extract_osm_pbf_blob_chunks.name())]
extract_osm_pbf_blob_chunks {
#[arg(num_args(1..))]
inputs: Vec<String>,
#[arg(long, default_value_t = false)]
recreate: bool,
},
#[command(name = stage::extract_osm_pbf_header.name())]
extract_osm_pbf_header {
#[arg(num_args(1..))]
inputs: Vec<String>,
},
#[command(name = stage::extract_osm_pbf_data.name())]
extract_osm_pbf_data {
#[arg(num_args(1..))]
inputs: Vec<String>,
#[arg(long, action = clap::ArgAction::Set, default_value_t = true)]
include_relations: bool,
#[arg(long, action = clap::ArgAction::Set, default_value_t = true)]
include_ways: bool,
#[arg(long, action = clap::ArgAction::Set, default_value_t = true)]
include_nodes: bool,
#[arg(long, default_value_t = true)]
ignore_info: bool,
#[arg(long)]
tags_include_list: Option<String>,
#[arg(long)]
tags_ignore_list: Option<String>,
#[arg(long, default_value_t = false)]
recreate: bool,
#[arg(long, value_name = "MB")]
buffer_limit_in_mb: Option<u64>,
},
#[command(name = stage::extract_osm_admin_levels.name())]
extract_osm_admin_levels {
#[arg(long)]
admin_level: Option<String>,
#[arg(long, default_value_t = false)]
recreate: bool,
#[arg(long, value_name = "TAGS")]
name_priority: Option<String>,
},
#[command(name = stage::extract_osm_house_numbers.name())]
extract_osm_house_numbers {
#[arg(long, default_value_t = false)]
recreate: bool,
},
#[command(name = stage::index_admin_levels_hierarchy.name())]
index_admin_levels_hierarchy {
#[arg(value_name = "country", num_args(0..))]
countries: Vec<String>,
},
#[command(name = stage::optimize_delete_isolated_admin_levels.name())]
optimize_delete_isolated_admin_levels {
#[arg(value_name = "country", num_args(0..))]
countries: Vec<String>,
},
#[command(name = stage::optimize_merge_admin_levels.name())]
optimize_merge_admin_levels {
#[arg(value_name = "country", num_args(0..))]
countries: Vec<String>,
},
#[command(name = stage::index_addresses.name())]
index_addresses {
#[arg(value_name = "country", num_args(0..))]
countries: Vec<String>,
},
#[command(name = stage::index_coordinates.name())]
index_coordinates {
#[arg(value_name = "country", num_args(0..))]
countries: Vec<String>,
},
#[command(name = stage::optimize_delete_intermediary_data.name())]
optimize_delete_intermediary_data,
#[command(name = stage::optimize_sqlite_file.name())]
optimize_sqlite_file {
#[arg(value_name = "country", num_args(0..))]
countries: Vec<String>,
},
}
fn bases_for(data_path: &str, countries: &[String]) -> Vec<base_files> {
let all = store::bases(data_path);
if countries.is_empty() {
if all.is_empty() {
eprintln!(
"\x1b[1;31merror\x1b[0m: no base in {data_path} — first you need to build the data using geolite build"
);
std::process::exit(1);
}
return all;
}
countries
.iter()
.map(|iso| {
all
.iter()
.find(|base| base.iso.eq_ignore_ascii_case(iso))
.cloned()
.unwrap_or_else(|| {
eprintln!("\x1b[1;31merror\x1b[0m: no base for {iso} in {data_path}");
std::process::exit(1);
})
})
.collect()
}
fn writable_bases_for(data_path: &str, countries: &[String], stage: stage) -> Vec<base_files> {
let bases = bases_for(data_path, countries);
if !countries.is_empty()
&& let Some(base) = bases.iter().find(|base| base.is_sealed())
{
eprintln!(
"\x1b[1;31merror\x1b[0m: {} is sealed — a base built once is final; remove it to run {} again",
base.label(),
stage.name()
);
std::process::exit(1);
}
let (sealed, writable): (Vec<base_files>, Vec<base_files>) =
bases.into_iter().partition(|base| base.is_sealed());
for base in &sealed {
println!("\x1b[1;33msealed\x1b[0m {} — left as it is", base.label());
}
if writable.is_empty() {
eprintln!("\x1b[1;31merror\x1b[0m: every base in {data_path} is sealed — a base built once is final");
std::process::exit(1);
}
writable
}
fn per_base(bases: &[base_files], stage: impl Fn(&base_files)) {
for (i, base) in bases.iter().enumerate() {
if i > 0 {
println!();
}
println!("\x1b[2mbase {}\x1b[0m", base.label());
stage(base);
}
}
fn with_stale_indexes(
data_path: &str,
bases: &[base_files],
handler: fn(&str, &str) -> bool,
) {
super::optimize::with_sizes(data_path, || {
per_base(bases, |base| {
if handler(&base.sqlite, &base.index) {
println!(
"\x1b[1;33mnext\x1b[0m run `geolite exec {} {}` and `geolite exec {} {}`",
stage::index_addresses.name(),
base.iso,
stage::index_coordinates.name(),
base.iso
);
}
})
})
}
pub fn command_handler_exec(data_path: &str, threads: &u8, command: exec_commands) {
match command {
exec_commands::extract_osm_pbf_blob_chunks { inputs, recreate } => {
osm_pbf_blob_chunks::command_handler_extract_osm_pbf_blob_chunks(data_path, &inputs, recreate);
}
exec_commands::extract_osm_pbf_header { inputs } => {
osm_pbf_header::command_handler_extract_osm_pbf_header(data_path, &inputs);
}
exec_commands::extract_osm_pbf_data {
inputs,
include_relations,
include_ways,
include_nodes,
ignore_info,
tags_include_list,
tags_ignore_list,
recreate,
buffer_limit_in_mb,
} => {
osm_pbf_data::command_handler_extract_osm_pbf_data(
data_path,
threads,
&inputs,
include_relations,
include_ways,
include_nodes,
ignore_info,
tags_include_list,
tags_ignore_list,
recreate,
buffer_limit_in_mb,
);
}
exec_commands::extract_osm_admin_levels {
admin_level,
recreate,
name_priority,
} => {
let levels: Vec<level> = match admin_level.as_deref() {
Some(raw) => match super::extract::parse_admin_levels(raw) {
Ok(v) => v,
Err(e) => {
eprintln!("\x1b[1;31merror\x1b[0m: invalid --admin-level: {e}");
std::process::exit(1);
}
},
None => crate::presets::DEFAULT.extract_osm_admin_levels.admin_levels.to_vec(),
};
let names: Option<Vec<&str>> = match name_priority.as_deref() {
Some(raw) => match super::extract::parse_name_priority(raw) {
Ok(v) => Some(v),
Err(e) => {
eprintln!("\x1b[1;31merror\x1b[0m: invalid --name-priority: {e}");
std::process::exit(1);
}
},
None => None,
};
osm_admin_levels::command_handler_extract_osm_admin_levels(
data_path,
&levels,
threads,
recreate,
names.as_deref(),
);
}
exec_commands::extract_osm_house_numbers { recreate } => {
osm_house_numbers::command_handler_extract_osm_house_numbers(data_path, recreate);
}
exec_commands::index_admin_levels_hierarchy { countries } => {
per_base(&writable_bases_for(data_path, &countries, stage::index_admin_levels_hierarchy), |base| {
admin_levels_hierarchy::command_handler_index_admin_levels_hierarchy(&base.sqlite)
})
}
exec_commands::index_addresses { countries } => {
per_base(&writable_bases_for(data_path, &countries, stage::index_addresses), |base| {
addresses::command_handler_index_addresses(&base.sqlite, &base.index)
})
}
exec_commands::index_coordinates { countries } => {
per_base(&writable_bases_for(data_path, &countries, stage::index_coordinates), |base| {
coordinates::command_handler_index_coordinates(&base.sqlite)
})
}
exec_commands::optimize_delete_isolated_admin_levels { countries } => with_stale_indexes(
data_path,
&writable_bases_for(data_path, &countries, stage::optimize_delete_isolated_admin_levels),
delete_isolated_admin_levels::command_handler_optimize_delete_isolated_admin_levels,
),
exec_commands::optimize_merge_admin_levels { countries } => with_stale_indexes(
data_path,
&writable_bases_for(data_path, &countries, stage::optimize_merge_admin_levels),
merge_admin_levels::command_handler_optimize_merge_admin_levels,
),
exec_commands::optimize_delete_intermediary_data => {
super::optimize::with_sizes(data_path, || {
delete_intermediary_data::command_handler_optimize_delete_intermediary_data(data_path);
})
}
exec_commands::optimize_sqlite_file { countries } => {
let bases = writable_bases_for(data_path, &countries, stage::optimize_sqlite_file);
super::optimize::with_sizes(data_path, || {
per_base(&bases, |base| {
sqlite_file::command_handler_optimize_sqlite_file(&base.sqlite)
})
})
}
}
}