Skip to content

Commit

Permalink
feat: remove null properties encoder and decoder
Browse files Browse the repository at this point in the history
Signed-off-by: Aleksei Gurianov <[email protected]>
  • Loading branch information
Guria committed Oct 28, 2024
1 parent 46499da commit 79babba
Show file tree
Hide file tree
Showing 6 changed files with 199 additions and 32 deletions.
90 changes: 89 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,99 @@ pub fn main() {
)
// Encode the Feature to GeoJSON
|> gleojson.GeoFeature
|> gleojson.encode_geojson(gleojson.properties_null_encoder)
|> gleojson.encode_geojson(fn(_) { json.null() })
|> json.to_string
}
```

Decoding GeoJSON objects is also straightforward:

```gleam:./test/examples/decode.gleam
import decode/zero
import examples/to_dynamic_decoder.{to_dynamic_decoder}
import gleam/bool
import gleam/float
import gleam/int
import gleam/io
import gleam/json
import gleam/option
import gleojson
// Define custom properties type for your features
pub type ParkProperties {
ParkProperties(
name: String,
area_sq_km: Float,
year_established: Int,
is_protected: Bool,
)
}
// Define decoder for your custom properties
fn park_properties_decoder() {
use name <- zero.field("name", zero.string)
use area_sq_km <- zero.field("area_sq_km", zero.float)
use year_established <- zero.field("year_established", zero.int)
use is_protected <- zero.field("is_protected", zero.bool)
ParkProperties(name:, area_sq_km:, year_established:, is_protected:)
|> zero.success
}
pub fn main() {
// Example GeoJSON string representing a national park
let json_string =
"{
\"type\": \"Feature\",
\"geometry\": {
\"type\": \"Point\",
\"coordinates\": [-119.5383, 37.8651]
},
\"properties\": {
\"name\": \"Yosemite National Park\",
\"area_sq_km\": 3029.87,
\"year_established\": 1890,
\"is_protected\": true
},
\"id\": \"yosemite\"
}"
// Decode the JSON string into a GeoJSON object
let decoded =
json.decode(
from: json_string,
using: to_dynamic_decoder(
gleojson.geojson_decoder(park_properties_decoder()),
),
)
// Handle the decoded result
case decoded {
Ok(geojson) -> {
case geojson {
gleojson.GeoFeature(feature) -> {
case feature.properties {
option.Some(ParkProperties(name, area, year, is_protected)) -> {
io.println(
"Decoded " <> name <> ", established in " <> int.to_string(year),
)
io.println(
"Area: "
<> float.to_string(area)
<> " sq km, Protected: "
<> bool.to_string(is_protected),
)
}
option.None -> io.println("Feature has no properties")
}
}
_ -> io.println("Decoded a different type of GeoJSON object")
}
}
Error(_error) -> io.println("Failed to decode")
}
}
```

For more advanced usage, including custom properties and decoding, see the [documentation](https://hexdocs.pm/gleojson).

## Development
Expand Down
20 changes: 2 additions & 18 deletions src/gleojson.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -350,8 +350,8 @@ fn featurecollection_decoder(properties_decoder: zero.Decoder(properties)) {
/// }
/// ```
///
/// Note: This function expects a valid GeoJSON structure. Invalid or incomplete
/// GeoJSON data will result in a decode error.
/// Note: This function expects a valid GeoJSON structure.
/// Invalid or incomplete GeoJSON data will result in a decode error.
pub fn geojson_decoder(properties_decoder: zero.Decoder(properties)) {
use type_str <- zero.then(type_decoder())
case type_str {
Expand All @@ -363,22 +363,6 @@ pub fn geojson_decoder(properties_decoder: zero.Decoder(properties)) {
}
}

/// Encodes null properties for Features and FeatureCollections.
///
/// This is a utility function that can be used as the `properties_encoder`
/// argument for `encode_geojson` when you don't need to encode any properties.
pub fn properties_null_encoder(_props) {
json.null()
}

/// Decodes null properties for Features and FeatureCollections.
///
/// This is a utility function that can be used as the `properties_decoder`
/// argument for `geojson_decoder` when you don't need to decode any properties.
pub fn properties_null_decoder() {
zero.success(Ok(Nil))
}

/// Creates a 2D Position object from longitude and latitude values.
///
/// This function is a convenience helper for creating a Position object
Expand Down
83 changes: 83 additions & 0 deletions test/examples/decode.gleam
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import decode/zero
import examples/to_dynamic_decoder.{to_dynamic_decoder}
import gleam/bool
import gleam/float
import gleam/int
import gleam/io
import gleam/json
import gleam/option
import gleojson

// Define custom properties type for your features
pub type ParkProperties {
ParkProperties(
name: String,
area_sq_km: Float,
year_established: Int,
is_protected: Bool,
)
}

// Define decoder for your custom properties
fn park_properties_decoder() {
use name <- zero.field("name", zero.string)
use area_sq_km <- zero.field("area_sq_km", zero.float)
use year_established <- zero.field("year_established", zero.int)
use is_protected <- zero.field("is_protected", zero.bool)
ParkProperties(name:, area_sq_km:, year_established:, is_protected:)
|> zero.success
}

pub fn main() {
// Example GeoJSON string representing a national park
let json_string =
"{
\"type\": \"Feature\",
\"geometry\": {
\"type\": \"Point\",
\"coordinates\": [-119.5383, 37.8651]
},
\"properties\": {
\"name\": \"Yosemite National Park\",
\"area_sq_km\": 3029.87,
\"year_established\": 1890,
\"is_protected\": true
},
\"id\": \"yosemite\"
}"

// Decode the JSON string into a GeoJSON object
let decoded =
json.decode(
from: json_string,
using: to_dynamic_decoder(
gleojson.geojson_decoder(park_properties_decoder()),
),
)

// Handle the decoded result
case decoded {
Ok(geojson) -> {
case geojson {
gleojson.GeoFeature(feature) -> {
case feature.properties {
option.Some(ParkProperties(name, area, year, is_protected)) -> {
io.println(
"Decoded " <> name <> ", established in " <> int.to_string(year),
)
io.println(
"Area: "
<> float.to_string(area)
<> " sq km, Protected: "
<> bool.to_string(is_protected),
)
}
option.None -> io.println("Feature has no properties")
}
}
_ -> io.println("Decoded a different type of GeoJSON object")
}
}
Error(_error) -> io.println("Failed to decode")
}
}
2 changes: 1 addition & 1 deletion test/examples/encode.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ pub fn main() {
)
// Encode the Feature to GeoJSON
|> gleojson.GeoFeature
|> gleojson.encode_geojson(gleojson.properties_null_encoder)
|> gleojson.encode_geojson(fn(_) { json.null() })
|> json.to_string
}
6 changes: 6 additions & 0 deletions test/examples/to_dynamic_decoder.gleam
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import decode/zero.{type Decoder}
import gleam/dynamic.{type Dynamic}

pub fn to_dynamic_decoder(zero_decoder: Decoder(a)) {
fn(dynamic_value: Dynamic) { zero.run(dynamic_value, zero_decoder) }
}
30 changes: 18 additions & 12 deletions test/gleojson_test.gleam
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import birdie
import decode/zero
import examples/decode
import examples/encode
import gleam/dynamic
import gleam/json
Expand Down Expand Up @@ -134,8 +135,8 @@ pub fn point_encode_decode_test() {

assert_encode_decode(
geojson,
gleojson.properties_null_encoder,
gleojson.properties_null_decoder(),
fn(_) { json.null() },
zero.success(Nil),
"point_encode_decode",
)
}
Expand All @@ -151,8 +152,8 @@ pub fn multipoint_encode_decode_test() {

assert_encode_decode(
geojson,
gleojson.properties_null_encoder,
gleojson.properties_null_decoder(),
fn(_) { json.null() },
zero.success(Nil),
"multipoint_encode_decode",
)
}
Expand All @@ -168,8 +169,8 @@ pub fn linestring_encode_decode_test() {

assert_encode_decode(
geojson,
gleojson.properties_null_encoder,
gleojson.properties_null_decoder(),
fn(_) { json.null() },
zero.success(Nil),
"linestring_encode_decode",
)
}
Expand All @@ -189,8 +190,8 @@ pub fn polygon_encode_decode_test() {

assert_encode_decode(
geojson,
gleojson.properties_null_encoder,
gleojson.properties_null_decoder(),
fn(_) { json.null() },
zero.success(Nil),
"polygon_encode_decode",
)
}
Expand Down Expand Up @@ -220,8 +221,8 @@ pub fn multipolygon_encode_decode_test() {

assert_encode_decode(
geojson,
gleojson.properties_null_encoder,
gleojson.properties_null_decoder(),
fn(_) { json.null() },
zero.success(Nil),
"multipolygon_encode_decode",
)
}
Expand All @@ -240,8 +241,8 @@ pub fn geometrycollection_encode_decode_test() {

assert_encode_decode(
geojson,
gleojson.properties_null_encoder,
gleojson.properties_null_decoder(),
fn(_) { json.null() },
zero.success(Nil),
"geometrycollection_encode_decode",
)
}
Expand Down Expand Up @@ -344,3 +345,8 @@ pub fn example_test() {
|> dynamic.classify()
|> should.equal("String")
}

pub fn example_decode_test() {
decode.main()
|> should.equal(Nil)
}

0 comments on commit 79babba

Please sign in to comment.