skyway 0.0.1

A command-line OpenStreetMap file converter
use std::sync::mpsc::Receiver;
use serde_json::to_writer;
use std::io::Write;
use std::collections::HashMap;
use serde::{Serialize, Serializer};

use crate::elements::{Element, Metadata, Member, ElementType};

#[derive(Serialize)]
#[serde(remote = "Member")]
pub struct MemberDef {
    #[serde(rename = "type")]
    pub t: Option<String>,
    #[serde(rename = "ref")]
    pub id: i64,
    pub role: Option<String>,
}

#[derive(Serialize)]
#[serde(remote = "ElementType", tag = "type", rename_all = "lowercase")]
pub enum ElementTypeDef {
    Node {
        lat: f64,
        lon: f64,
    },
    Way {
        nodes: Vec<i64>,
    },
    Relation {
        #[serde(serialize_with = "serialize_member_vec")]
        members: Vec<Member>,
    },
}

fn serialize_member_vec<S: Serializer>(v: &[Member], serializer: S) -> Result<S::Ok, S::Error> {
    #[derive(Serialize)]
    struct Wrapper<'a>(#[serde(with = "MemberDef")] &'a Member);

    v.iter()
        .map(Wrapper)
        .collect::<Vec<_>>()
        .serialize(serializer)
}


fn _skip_visibility(visibility: &Option<bool>) -> bool {
    visibility.unwrap_or(true)
}

#[derive(Serialize)]
#[serde(remote = "Element")]
pub struct ElementDef {
    pub changeset: Option<i64>,
    pub user: Option<String>,
    pub version: Option<i32>,
    pub uid: Option<i32>,
    pub id: i64,
    pub timestamp: Option<String>,
    #[serde(skip_serializing_if = "_skip_visibility")]
    pub visible: Option<bool>,
    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
    pub tags: HashMap<String, String>,
    #[serde(flatten, with = "ElementTypeDef")]
    pub element_type: ElementType,
}

#[derive(Serialize)]
#[serde(remote = "Metadata")]
pub struct MetadataDef {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub version: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub generator: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub copyright: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub license: Option<String>,
}

#[derive(Serialize)]
pub struct OsmDocument {
    #[serde(flatten, with = "MetadataDef")]
    pub metadata: Metadata,
    #[serde(serialize_with = "serialize_element_vec")]
    pub elements: Vec<Element>,
}

fn serialize_element_vec<S: Serializer>(v: &[Element], serializer: S) -> Result<S::Ok, S::Error> {
    #[derive(Serialize)]
    struct Wrapper<'a>(#[serde(with = "ElementDef")] &'a Element);

    v.iter()
        .map(Wrapper)
        .collect::<Vec<_>>()
        .serialize(serializer)
}

pub fn write_json<D: Write>(receiver: Receiver<Element>, metadata: Metadata, dest: D) {
    let mut received_elements = Vec::new();
    for e in receiver {
        received_elements.push(e);
    }

    let osm_document = OsmDocument {
        metadata,
        elements: received_elements,
    };

    match to_writer(dest, &osm_document) {
        Ok(_) => {
            eprintln!("Successfully wrote output.");
        },
        Err(e) => {
            panic!("JSON serialization error: {e:?}");
        },
    }
}