#[cfg(feature = "cli")]
use clap::ValueEnum;
use std::ffi::OsStr;
use std::fmt;
use std::path::PathBuf;
use crate::SkywayError;
#[derive(Clone, Debug)]
#[cfg_attr(feature = "cli", derive(ValueEnum))]
#[non_exhaustive]
pub enum OsmFormat {
#[cfg_attr(feature = "cli", value(name = "json"))]
Json,
#[cfg_attr(feature = "cli", value(name = "o5m"))]
O5m,
#[cfg_attr(feature = "cli", value(name = "opl"))]
Opl,
#[cfg_attr(feature = "cli", value(name = "overpass"))]
Overpass,
#[cfg_attr(feature = "cli", value(name = "xml", alias = "osm"))]
Xml,
#[cfg_attr(feature = "cli", value(name = "pbf"))]
Pbf,
}
impl OsmFormat {
fn format_error(ext: &str) -> SkywayError {
SkywayError::UnknownFormat(ext.to_string())
}
pub fn parse(
cli_format: Option<Self>,
file_path: &Option<PathBuf>,
) -> Result<Self, SkywayError> {
if let Some(format) = cli_format {
Ok(format)
} else {
let path = file_path.as_ref().ok_or(Self::format_error(
"no file path given or format specified.",
))?;
let extension = path.extension().ok_or(Self::format_error(
format!(
"unable to extract extension from path \"{}\"",
path.display()
)
.as_str(),
))?;
let ext_str = extension.to_str().ok_or(Self::format_error(
format!(
"extension found but could not be converted to a string in path \"{}\"",
path.display()
)
.as_str(),
))?;
Self::from_extension(ext_str).ok_or(Self::format_error(
format!("File extension not recognized: {}", ext_str).as_str(),
))
}
}
pub const fn can_read(&self) -> bool {
match self {
#[cfg(feature = "json")]
OsmFormat::Json => true,
#[cfg(feature = "json")]
OsmFormat::Overpass => true,
#[cfg(feature = "opl")]
OsmFormat::Opl => true,
#[cfg(feature = "xml")]
OsmFormat::Xml => true,
#[cfg(feature = "pbf")]
OsmFormat::Pbf => true,
_ => false,
}
}
pub const fn can_write(&self) -> bool {
match self {
#[cfg(feature = "json")]
OsmFormat::Json => true,
#[cfg(feature = "json")]
OsmFormat::Overpass => true,
#[cfg(feature = "o5m")]
OsmFormat::O5m => true,
#[cfg(feature = "opl")]
OsmFormat::Opl => true,
#[cfg(feature = "xml")]
OsmFormat::Xml => true,
_ => false,
}
}
#[inline]
#[must_use]
pub fn reading_enabled(&self) -> bool {
match self {
#[cfg(feature = "json")]
OsmFormat::Json => true,
#[cfg(feature = "json")]
OsmFormat::Overpass => true,
#[cfg(feature = "opl")]
OsmFormat::Opl => true,
#[cfg(feature = "xml")]
OsmFormat::Xml => true,
#[cfg(feature = "pbf")]
OsmFormat::Pbf => true,
_ => false,
}
}
#[inline]
#[must_use]
pub fn writing_enabled(&self) -> bool {
match self {
#[cfg(feature = "json")]
OsmFormat::Json => true,
#[cfg(feature = "json")]
OsmFormat::Overpass => true,
#[cfg(feature = "o5m")]
OsmFormat::O5m => true,
#[cfg(feature = "opl")]
OsmFormat::Opl => true,
#[cfg(feature = "xml")]
OsmFormat::Xml => true,
_ => false,
}
}
#[inline]
pub fn validate_capabilities(input: &OsmFormat, output: &OsmFormat) -> Result<(), SkywayError> {
if !input.can_read() {
return Err(SkywayError::UnsupportedRead(format!(
"format {:?} does not support reading",
input
)));
}
if !output.can_write() {
return Err(SkywayError::UnsupportedWrite(format!(
"format {:?} does not support writing",
output
)));
}
Ok(())
}
#[inline]
pub fn validate_enabled(input: &OsmFormat, output: &OsmFormat) -> Result<(), SkywayError> {
if !input.reading_enabled() {
return Err(SkywayError::UnsupportedRead(format!(
"reading support not enabled for format {:?}",
input
)));
}
if !output.writing_enabled() {
return Err(SkywayError::UnsupportedWrite(format!(
"writing support not enabled for format {:?}",
output
)));
}
Ok(())
}
pub fn validate_conversion(input: &OsmFormat, output: &OsmFormat) -> Result<(), SkywayError> {
Self::validate_capabilities(input, output)?;
Self::validate_enabled(input, output)?;
Ok(())
}
#[inline]
pub fn from_extension<S>(ext: S) -> Option<Self>
where
S: AsRef<OsStr>,
{
fn inner(ext: &OsStr) -> Option<OsmFormat> {
let ext = ext.to_str()?.to_ascii_lowercase();
match ext.as_str() {
"json" => Some(OsmFormat::Json),
"o5m" => Some(OsmFormat::O5m),
"opl" => Some(OsmFormat::Opl),
"osm" | "xml" => Some(OsmFormat::Xml),
"pbf" => Some(OsmFormat::Pbf),
_ => None,
}
}
inner(ext.as_ref())
}
}
impl fmt::Display for OsmFormat {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
OsmFormat::Json => write!(f, "json")?,
OsmFormat::Overpass => write!(f, "overpass")?,
OsmFormat::O5m => write!(f, "o5m")?,
OsmFormat::Opl => write!(f, "opl")?,
OsmFormat::Xml => write!(f, "xml")?,
OsmFormat::Pbf => write!(f, "pbf")?,
}
Ok(())
}
}