pub mod osm_admin_levels;
pub mod osm_house_numbers;
pub mod osm_pbf_blob_chunks;
pub mod osm_pbf_data;
pub mod osm_pbf_header;
use std::io::Write;
use clap::Subcommand;
use rusqlite::Connection;
use crate::domain::admin_level::level;
#[derive(Subcommand)]
pub enum extract_commands {
#[command(name = "osm-pbf-blob-chunks")]
osm_pbf_blob_chunks {
#[arg(num_args(1..))]
inputs: Vec<String>,
#[arg(long, default_value_t = false)]
recreate: bool,
},
#[command(name = "osm-pbf-header")]
osm_pbf_header {
#[arg(num_args(1..))]
inputs: Vec<String>,
},
#[command(name = "osm-pbf-data")]
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 = "osm-admin-levels")]
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 = "osm-house-numbers")]
osm_house_numbers {
#[arg(long, default_value_t = false)]
recreate: bool,
},
}
fn parse_name_priority(raw: &str) -> Result<Vec<&str>, String> {
let tags: Vec<&str> = raw
.split(',')
.map(str::trim)
.filter(|s| !s.is_empty())
.collect();
if tags.is_empty() {
return Err("at least one tag required".to_string());
}
for tag in &tags {
if !crate::domain::osm_tag::key::is_valid_key(tag) {
return Err(format!("invalid characters in tag '{tag}'"));
}
}
Ok(tags)
}
fn parse_admin_levels(raw: &str) -> Result<Vec<level>, String> {
let levels = raw
.split(',')
.map(str::trim)
.filter(|s| !s.is_empty())
.map(|part| level::parse(part).map_err(|e| e.to_string()))
.collect::<Result<Vec<level>, String>>()?;
if levels.is_empty() {
return Err("at least one level required".to_string());
}
Ok(levels)
}
pub(super) struct resolved_input {
pub path: String,
pub name: String,
pub id: u32,
}
pub(super) fn resolve_input(
conn: &Connection,
data_path: &str,
ordinal: usize,
input: &str,
) -> Option<resolved_input> {
if ordinal > 0 {
println!();
}
let file = crate::domain::osm_pbf_file::osm_pbf_file::open(Some(conn), data_path);
let is_path = file.local_file(input).is_some();
if !is_path {
print!("\x1b[1;32mresolving\x1b[0m '{input}'...");
let _ = std::io::stdout().flush();
}
let Some(path) = file.resolve(input) else {
if !is_path {
println!();
}
eprintln!("\x1b[1;31merror\x1b[0m: could not resolve '{input}'");
return None;
};
if !is_path {
println!(" done");
}
let name = std::path::Path::new(&path)
.file_name()
.unwrap_or_default()
.to_string_lossy()
.into_owned();
let id = crate::domain::osm_pbf_file::repository::ensure_by_file_path(conn, &path);
Some(resolved_input { path, name, id })
}
pub fn command_handler_extract(
data_path: &str,
threads: &u8,
sqlite_path: &str,
command: extract_commands,
_recreate: &bool,
preset: &crate::presets::preset,
) {
match command {
extract_commands::osm_pbf_blob_chunks { inputs, recreate } => {
osm_pbf_blob_chunks::command_handler_extract_osm_pbf_blob_chunks(
data_path,
sqlite_path,
&inputs,
recreate,
);
}
extract_commands::osm_pbf_header { inputs } => {
osm_pbf_header::command_handler_extract_osm_pbf_header(data_path, sqlite_path, &inputs);
}
extract_commands::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,
sqlite_path,
threads,
&inputs,
include_relations,
include_ways,
include_nodes,
ignore_info,
tags_include_list,
tags_ignore_list,
recreate,
buffer_limit_in_mb,
);
}
extract_commands::osm_admin_levels {
admin_level,
recreate,
name_priority,
} => {
let levels: Vec<level> = match admin_level.as_deref() {
Some(raw) => match parse_admin_levels(raw) {
Ok(v) => v,
Err(e) => {
eprintln!("\x1b[1;31merror\x1b[0m: invalid --admin-level: {e}");
std::process::exit(1);
}
},
None => preset.extract_osm_admin_levels.admin_levels.to_vec(),
};
let names: Vec<&str> = match name_priority.as_deref() {
Some(raw) => match parse_name_priority(raw) {
Ok(v) => v,
Err(e) => {
eprintln!("\x1b[1;31merror\x1b[0m: invalid --name-priority: {e}");
std::process::exit(1);
}
},
None => preset.extract_osm_admin_levels.name_priority.to_vec(),
};
osm_admin_levels::command_handler_extract_osm_admin_levels(
sqlite_path,
&levels,
threads,
recreate,
&names,
preset.extract_osm_admin_levels.admin_levels_rules,
);
}
extract_commands::osm_house_numbers { recreate } => {
osm_house_numbers::command_handler_extract_osm_house_numbers(
sqlite_path,
recreate,
preset.house_numbers,
);
}
}
}