Skip to content

Commit

Permalink
feat(ser): add wof serializer
Browse files Browse the repository at this point in the history
  • Loading branch information
Joxit committed Jan 11, 2020
1 parent 8e8acad commit 159864f
Show file tree
Hide file tree
Showing 5 changed files with 250 additions and 2 deletions.
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "wof-cli"
name = "wof"
version = "0.1.0"
authors = ["Jones Magloire @Joxit"]
description = "The Who's On First command line aggregator."
Expand All @@ -14,7 +14,7 @@ path = "src/main.rs"
[dependencies]
structopt = "0.3"
which = "3.1.0"
serde_json = "1.0"
json = { git = "https://github.com/Joxit/json-rust.git", branch = "feat/custom-generator" }
tar = "0.4.26"
flate2 = "1.0.13"
attohttpc = "0.8.0"
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod ser;
1 change: 1 addition & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use structopt::StructOpt;

mod commands;
mod git;
mod ser;
mod std;

#[derive(Debug, StructOpt)]
Expand Down
153 changes: 153 additions & 0 deletions src/ser.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
use json::codegen::Generator;
use json::object::Object;
use json::JsonValue;
use std::io::{self, Write};

pub struct WOFGenerator<'a, W> {
writer: &'a mut W,
dent: u16,
}

impl<'a, W> WOFGenerator<'a, W>
where
W: Write,
{
pub fn new(writer: &'a mut W) -> WOFGenerator<W> {
WOFGenerator {
writer: writer,
dent: 0,
}
}

pub fn write_object_value_by_key(&mut self, key: &str, value: &JsonValue) -> io::Result<()> {
match key {
"bbox" => {
if let JsonValue::Array(ref array) = value {
self.write_char(b'[')?;
let mut iter = array.iter();

if let Some(item) = iter.next() {
self.indent();
self.new_line()?;
self.write_json(item)?;
} else {
self.write_char(b']')?;
return Ok(());
}

for item in iter {
self.write_char(b',')?;
self.new_line()?;
self.write_json(item)?;
}

self.dedent();
self.write_char(b'\n')?;
self.write_char(b']')
} else {
self.write_json(value)
}
}
"geometry" => value.write(&mut self.writer),
_ => self.write_json(value),
}
}
}

impl<'a, W> Generator for WOFGenerator<'a, W>
where
W: Write,
{
type T = W;

#[inline(always)]
fn get_writer(&mut self) -> &mut W {
&mut self.writer
}

#[inline(always)]
fn write_min(&mut self, colon_space: &[u8], colon: u8) -> io::Result<()> {
if self.dent == 1 {
self.writer.write_all(colon_space)
} else {
self.writer.write_all(&[colon])
}
}

#[inline(always)]
fn write_object(&mut self, object: &Object) -> io::Result<()> {
self.write_char(b'{')?;
let mut entries: Vec<(&str, &JsonValue)> = Vec::new();
for (k, v) in object.iter() {
entries.push((k, v));
}
if self.dent == 0 {
entries.sort_by(wof_first_level_ordering);
} else {
entries.sort_by(|(k1, _), (k2, _)| k1.partial_cmp(k2).unwrap());
}
let mut iter = entries.iter();
if let Some((key, value)) = iter.next() {
self.indent();
self.new_line()?;
self.write_string(key)?;
self.write_min(b": ", b':')?;
self.write_object_value_by_key(key, value)?;
} else {
self.write_char(b'}')?;
return Ok(());
}

for (key, value) in iter {
self.write_char(b',')?;
self.new_line()?;
self.write_string(key)?;
self.write_min(b": ", b':')?;
self.write_object_value_by_key(key, value)?;
}

self.dedent();
self.new_line()?;
self.write_char(b'}')
}

fn indent(&mut self) {
self.dent += 1;
}

fn dedent(&mut self) {
self.dent -= 1;
}

fn new_line(&mut self) -> io::Result<()> {
self.write_char(b'\n')?;
for _ in 0..(self.dent * 2) {
self.write_char(b' ')?;
}
Ok(())
}
}

fn wof_first_level_classify(key: &str) -> i32 {
match key {
"id" => 0,
"type" => 1,
"properties" => 2,
"bbox" => 4,
"geometry" => 5,
_ => 3,
}
}

fn wof_first_level_ordering(
(k1, _): &(&str, &JsonValue),
(k2, _): &(&str, &JsonValue),
) -> std::cmp::Ordering {
if wof_first_level_classify(k1) < wof_first_level_classify(k2) {
std::cmp::Ordering::Less
} else if wof_first_level_classify(k1) > wof_first_level_classify(k2) {
std::cmp::Ordering::Greater
} else {
k1.partial_cmp(k2).unwrap()
}
}
93 changes: 93 additions & 0 deletions tests/test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#[macro_use]
extern crate json;

use json::codegen::Generator;
use json::Null;
use wof::ser::WOFGenerator;

#[test]
pub fn serialize_first_level_wof_geojson_with_null() {
let t = object! {
"type"=> Null,
"properties"=> Null,
"geometry"=> Null,
"bbox"=> Null,
"id"=> Null,
};
let mut vec: Vec<u8> = Vec::new();
assert!(WOFGenerator::new(&mut vec).write_json(&t).is_ok());
assert_eq!(
String::from_utf8(vec).unwrap(),
r#"{
"id": null,
"type": null,
"properties": null,
"bbox": null,
"geometry": null
}"#
);
}

#[test]
pub fn serialize_first_level_wof_geojson_with_content() {
let t = object! {
"type"=>"Feature",
"properties"=> object!{
"name:fra_x_preferred" => vec![
"Ajaccio"
],
"wof:id"=>101748927,
"wof:lang" => vec![
"fre"
],
"name:eng_x_preferred" => vec![
"Ajaccio"
],
},
"geometry"=> object!{
"coordinates" => vec![vec![
vec![8.585396,41.873571],
vec![8.826011,41.873571],
vec![8.826011,41.971536],
vec![8.585396,41.968222],
vec![8.585396,41.873571]
]],
"type" => "Polygon"
},
"bbox"=> vec![
8.585396,
41.873571,
8.826011,
41.971536
],
"id"=> 101748927,
};
let mut vec: Vec<u8> = Vec::new();
assert!(WOFGenerator::new(&mut vec).write_json(&t).is_ok());
assert_eq!(
String::from_utf8(vec).unwrap(),
r#"{
"id": 101748927,
"type": "Feature",
"properties": {
"name:eng_x_preferred":[
"Ajaccio"
],
"name:fra_x_preferred":[
"Ajaccio"
],
"wof:id":101748927,
"wof:lang":[
"fre"
]
},
"bbox": [
8.585396,
41.873571,
8.826011,
41.971536
],
"geometry": {"coordinates":[[[8.585396,41.873571],[8.826011,41.873571],[8.826011,41.971536],[8.585396,41.968222],[8.585396,41.873571]]],"type":"Polygon"}
}"#
);
}

0 comments on commit 159864f

Please sign in to comment.