From 96fda0bcc70fab6303b1135e14dc2dcd87779f1e Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Sat, 13 Jan 2024 19:26:15 -0500 Subject: [PATCH 01/12] export projection as projjson --- src/proj.rs | 50 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/src/proj.rs b/src/proj.rs index a24e64b3..6fdcc6f8 100644 --- a/src/proj.rs +++ b/src/proj.rs @@ -2,8 +2,8 @@ use libc::c_int; use libc::{c_char, c_double}; use num_traits::Float; use proj_sys::{ - proj_area_create, proj_area_destroy, proj_area_set_bbox, proj_cleanup, proj_context_create, - proj_context_destroy, proj_context_errno, proj_context_get_url_endpoint, + proj_area_create, proj_area_destroy, proj_area_set_bbox, proj_as_projjson, proj_cleanup, + proj_context_create, proj_context_destroy, proj_context_errno, proj_context_get_url_endpoint, proj_context_is_network_enabled, proj_context_set_search_paths, proj_context_set_url_endpoint, proj_create, proj_create_crs_to_crs, proj_destroy, proj_errno_string, proj_get_area_of_use, proj_grid_cache_set_enable, proj_info, proj_normalize_for_visualization, proj_pj_info, @@ -1072,6 +1072,52 @@ impl Proj { Err(ProjError::Projection(error_message(err)?)) } } + + /// ```rust + /// # use approx::assert_relative_eq; + /// use proj::{Proj, Coord}; + /// + /// let from = "EPSG:2230"; + /// let to = "EPSG:26946"; + /// let ft_to_m = Proj::new_known_crs(&from, &to, None).unwrap(); + /// let result = ft_to_m.to_projjson(None, None).unwrap(); + /// dbg!(result); + /// ``` + /// + /// # Safety + /// This method contains unsafe code. + pub fn to_projjson( + &self, + multiline: Option, + indentation_width: Option, + ) -> Result { + let mut opts_c = vec![]; + if let Some(multiline) = multiline { + if multiline { + opts_c.push(CString::new("MULTILINE=YES")?); + } else { + opts_c.push(CString::new("MULTILINE=NO")?); + } + } + if let Some(indentation_width) = indentation_width { + opts_c.push(CString::new(format!( + "INDENTATION_WIDTH={}", + indentation_width + ))?); + } + + let opts_p: Vec<_> = opts_c.iter().map(|cstr| cstr.as_ptr()).collect(); + + unsafe { + let out_ptr = proj_as_projjson(self.ctx, self.c_proj, opts_p.as_ptr()); + if out_ptr.is_null() { + // Not sure the best way to retrieve and return the error + todo!() + } else { + Ok(_string(out_ptr)?) + } + } + } } impl convert::TryFrom<&str> for Proj { From 016e890b2f573ae6c6322858b77cb438547169d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20H=C3=BCgel?= Date: Wed, 17 Jan 2024 13:59:47 +0000 Subject: [PATCH 02/12] Fix projjson input option passing --- src/proj.rs | 45 +++++++++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/src/proj.rs b/src/proj.rs index 6fdcc6f8..7309c24d 100644 --- a/src/proj.rs +++ b/src/proj.rs @@ -10,6 +10,7 @@ use proj_sys::{ proj_trans, proj_trans_array, proj_trans_bounds, PJconsts, PJ_AREA, PJ_CONTEXT, PJ_COORD, PJ_DIRECTION_PJ_FWD, PJ_DIRECTION_PJ_INV, PJ_INFO, PJ_LPZT, PJ_XYZT, }; +use std::ptr; use std::{ convert, ffi, fmt::{self, Debug}, @@ -1073,16 +1074,7 @@ impl Proj { } } - /// ```rust - /// # use approx::assert_relative_eq; - /// use proj::{Proj, Coord}; - /// - /// let from = "EPSG:2230"; - /// let to = "EPSG:26946"; - /// let ft_to_m = Proj::new_known_crs(&from, &to, None).unwrap(); - /// let result = ft_to_m.to_projjson(None, None).unwrap(); - /// dbg!(result); - /// ``` + /// Return the projjson representation of a transformation /// /// # Safety /// This method contains unsafe code. @@ -1091,23 +1083,27 @@ impl Proj { multiline: Option, indentation_width: Option, ) -> Result { - let mut opts_c = vec![]; + let mut opts = vec![]; if let Some(multiline) = multiline { if multiline { - opts_c.push(CString::new("MULTILINE=YES")?); + opts.push(String::from("MULTILINE=YES")); } else { - opts_c.push(CString::new("MULTILINE=NO")?); + opts.push(String::from("MULTILINE=NO")); } } if let Some(indentation_width) = indentation_width { - opts_c.push(CString::new(format!( - "INDENTATION_WIDTH={}", - indentation_width - ))?); + opts.push(format!("INDENTATION_WIDTH={}", indentation_width)); } - - let opts_p: Vec<_> = opts_c.iter().map(|cstr| cstr.as_ptr()).collect(); - + // Do we have input options? Join them into a single string + let sep = if opts.len() > 1 { "," } else { "" }; + let joined = opts.join(sep); + let opts_c = CString::new(joined)?; + let opts_p = if opts_c.is_empty() { + // You cannot pass an empty string as an input option: it must be a null pointer + vec![ptr::null()] + } else { + vec![opts_c.as_ptr()] + }; unsafe { let out_ptr = proj_as_projjson(self.ctx, self.c_proj, opts_p.as_ptr()); if out_ptr.is_null() { @@ -1530,4 +1526,13 @@ mod test { assert_eq!(area.north, 84.73); assert!(name.contains("Europe")); } + + #[test] + fn test_projjson() { + let from = "EPSG:2230"; + let to = "EPSG:26946"; + let ft_to_m = Proj::new_known_crs(&from, &to, None).unwrap(); + let result = ft_to_m.to_projjson(None, None).unwrap(); + dbg!(&result); + } } From c57b2cc8838f5624d0f9e1b703478c21993a8d17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20H=C3=BCgel?= Date: Thu, 18 Jan 2024 11:20:32 +0000 Subject: [PATCH 03/12] Add schema option to projjson output --- src/proj.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/proj.rs b/src/proj.rs index 7309c24d..7a9c82d4 100644 --- a/src/proj.rs +++ b/src/proj.rs @@ -1082,6 +1082,7 @@ impl Proj { &self, multiline: Option, indentation_width: Option, + schema: Option<&str>, ) -> Result { let mut opts = vec![]; if let Some(multiline) = multiline { @@ -1094,6 +1095,9 @@ impl Proj { if let Some(indentation_width) = indentation_width { opts.push(format!("INDENTATION_WIDTH={}", indentation_width)); } + if let Some(schema) = schema { + opts.push(format!("SCHEMA={}", schema)); + } // Do we have input options? Join them into a single string let sep = if opts.len() > 1 { "," } else { "" }; let joined = opts.join(sep); @@ -1532,7 +1536,7 @@ mod test { let from = "EPSG:2230"; let to = "EPSG:26946"; let ft_to_m = Proj::new_known_crs(&from, &to, None).unwrap(); - let result = ft_to_m.to_projjson(None, None).unwrap(); + let result = ft_to_m.to_projjson(None, None, None).unwrap(); dbg!(&result); } } From 51de11758207025e24b9aee3488613895c3402b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20H=C3=BCgel?= Date: Thu, 18 Jan 2024 11:48:17 +0000 Subject: [PATCH 04/12] More compact check for no projjson options, and test for calling with no options --- src/proj.rs | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/proj.rs b/src/proj.rs index 7a9c82d4..6cd3aed6 100644 --- a/src/proj.rs +++ b/src/proj.rs @@ -1102,11 +1102,11 @@ impl Proj { let sep = if opts.len() > 1 { "," } else { "" }; let joined = opts.join(sep); let opts_c = CString::new(joined)?; - let opts_p = if opts_c.is_empty() { - // You cannot pass an empty string as an input option: it must be a null pointer - vec![ptr::null()] - } else { - vec![opts_c.as_ptr()] + // it seems wasteful to leave the None check until now, but building the option pointers inside + // an if statement consistently causes libproj to segfault with a memory error due to bad input + let opts_p = match (multiline, indentation_width, schema) { + (None, None, None) => vec![ptr::null()], + _ => vec![opts_c.as_ptr()], }; unsafe { let out_ptr = proj_as_projjson(self.ctx, self.c_proj, opts_p.as_ptr()); @@ -1536,7 +1536,15 @@ mod test { let from = "EPSG:2230"; let to = "EPSG:26946"; let ft_to_m = Proj::new_known_crs(&from, &to, None).unwrap(); - let result = ft_to_m.to_projjson(None, None, None).unwrap(); - dbg!(&result); + // Because libproj has been fussy about passing empty options strings we're testing both + let _ = ft_to_m + .to_projjson( + Some(true), + None, + Some("https://proj.org/schemas/v0.7/projjson.schema.json"), + ) + .unwrap(); + let _ = ft_to_m.to_projjson(None, None, None).unwrap(); + // TODO: do we want to compare one of the results to proj's output? } } From 22fd28d1fcd9e1db08320ff4a9d0f3c3aa9babc0 Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Thu, 18 Jan 2024 15:50:32 -0500 Subject: [PATCH 05/12] try alternate --- src/proj.rs | 50 +++++++++++++++++++++++++++----------------------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/src/proj.rs b/src/proj.rs index 6cd3aed6..c43c4b6b 100644 --- a/src/proj.rs +++ b/src/proj.rs @@ -1084,29 +1084,33 @@ impl Proj { indentation_width: Option, schema: Option<&str>, ) -> Result { - let mut opts = vec![]; - if let Some(multiline) = multiline { - if multiline { - opts.push(String::from("MULTILINE=YES")); - } else { - opts.push(String::from("MULTILINE=NO")); - } - } - if let Some(indentation_width) = indentation_width { - opts.push(format!("INDENTATION_WIDTH={}", indentation_width)); - } - if let Some(schema) = schema { - opts.push(format!("SCHEMA={}", schema)); - } // Do we have input options? Join them into a single string - let sep = if opts.len() > 1 { "," } else { "" }; - let joined = opts.join(sep); - let opts_c = CString::new(joined)?; - // it seems wasteful to leave the None check until now, but building the option pointers inside - // an if statement consistently causes libproj to segfault with a memory error due to bad input - let opts_p = match (multiline, indentation_width, schema) { - (None, None, None) => vec![ptr::null()], - _ => vec![opts_c.as_ptr()], + let opts_c = match (multiline, indentation_width, schema) { + (None, None, None) => None, + _ => { + let mut opts = vec![]; + if let Some(multiline) = multiline { + if multiline { + opts.push(String::from("MULTILINE=YES")); + } else { + opts.push(String::from("MULTILINE=NO")); + } + } + if let Some(indentation_width) = indentation_width { + opts.push(format!("INDENTATION_WIDTH={}", indentation_width)); + } + if let Some(schema) = schema { + opts.push(format!("SCHEMA={}", schema)); + } + let sep = if opts.len() > 1 { "," } else { "" }; + let joined = opts.join(sep); + Some(CString::new(joined)?) + } + }; + let opts_p = if let Some(opts_c) = opts_c { + vec![opts_c.as_ptr()] + } else { + vec![ptr::null()] }; unsafe { let out_ptr = proj_as_projjson(self.ctx, self.c_proj, opts_p.as_ptr()); @@ -1535,7 +1539,7 @@ mod test { fn test_projjson() { let from = "EPSG:2230"; let to = "EPSG:26946"; - let ft_to_m = Proj::new_known_crs(&from, &to, None).unwrap(); + let ft_to_m = Proj::new_known_crs(from, to, None).unwrap(); // Because libproj has been fussy about passing empty options strings we're testing both let _ = ft_to_m .to_projjson( From 1e94cc7b9316033452d5414a5f32172e0ddf63a3 Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Thu, 18 Jan 2024 16:15:11 -0500 Subject: [PATCH 06/12] cleanup --- src/proj.rs | 49 ++++++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/src/proj.rs b/src/proj.rs index c43c4b6b..d4a18d15 100644 --- a/src/proj.rs +++ b/src/proj.rs @@ -1084,36 +1084,45 @@ impl Proj { indentation_width: Option, schema: Option<&str>, ) -> Result { - // Do we have input options? Join them into a single string - let opts_c = match (multiline, indentation_width, schema) { + let opts_p = match (multiline, indentation_width, schema) { (None, None, None) => None, _ => { let mut opts = vec![]; if let Some(multiline) = multiline { if multiline { - opts.push(String::from("MULTILINE=YES")); + opts.push(CString::new("MULTILINE=YES")?); } else { - opts.push(String::from("MULTILINE=NO")); + opts.push(CString::new("MULTILINE=NO")?); } } if let Some(indentation_width) = indentation_width { - opts.push(format!("INDENTATION_WIDTH={}", indentation_width)); + opts.push(CString::new(format!( + "INDENTATION_WIDTH={}", + indentation_width + ))?); } if let Some(schema) = schema { - opts.push(format!("SCHEMA={}", schema)); + opts.push(CString::new(format!("SCHEMA={}", schema))?); } - let sep = if opts.len() > 1 { "," } else { "" }; - let joined = opts.join(sep); - Some(CString::new(joined)?) + + Some(opts) } }; - let opts_p = if let Some(opts_c) = opts_c { - vec![opts_c.as_ptr()] - } else { - vec![ptr::null()] - }; + unsafe { - let out_ptr = proj_as_projjson(self.ctx, self.c_proj, opts_p.as_ptr()); + // NOTE: we can't create the pointer to the vec of strings too early! As the [docstring + // of `as_ptr` + // mentions](https://doc.rust-lang.org/std/ffi/struct.CStr.html#method.as_ptr): + // > It is your responsibility to make sure that the underlying memory is not freed too + // > early. For example, the following code will cause undefined behavior when ptr is + // > used inside the unsafe block: + let out_ptr = if let Some(opts_p) = opts_p { + let opts_c = opts_p.iter().map(|x| x.as_ptr()).collect::>(); + proj_as_projjson(self.ctx, self.c_proj, opts_c.as_ptr()) + } else { + proj_as_projjson(self.ctx, self.c_proj, ptr::null()) + }; + if out_ptr.is_null() { // Not sure the best way to retrieve and return the error todo!() @@ -1539,15 +1548,9 @@ mod test { fn test_projjson() { let from = "EPSG:2230"; let to = "EPSG:26946"; - let ft_to_m = Proj::new_known_crs(from, to, None).unwrap(); + let ft_to_m = Proj::new_known_crs(&from, &to, None).unwrap(); // Because libproj has been fussy about passing empty options strings we're testing both - let _ = ft_to_m - .to_projjson( - Some(true), - None, - Some("https://proj.org/schemas/v0.7/projjson.schema.json"), - ) - .unwrap(); + let _ = ft_to_m.to_projjson(Some(true), None, None).unwrap(); let _ = ft_to_m.to_projjson(None, None, None).unwrap(); // TODO: do we want to compare one of the results to proj's output? } From cff5d5647bbf11d6be5511b79cc4023519cadf58 Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Thu, 18 Jan 2024 16:15:24 -0500 Subject: [PATCH 07/12] remove unneeded refs --- src/proj.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/proj.rs b/src/proj.rs index d4a18d15..f2372515 100644 --- a/src/proj.rs +++ b/src/proj.rs @@ -1548,7 +1548,7 @@ mod test { fn test_projjson() { let from = "EPSG:2230"; let to = "EPSG:26946"; - let ft_to_m = Proj::new_known_crs(&from, &to, None).unwrap(); + let ft_to_m = Proj::new_known_crs(from, to, None).unwrap(); // Because libproj has been fussy about passing empty options strings we're testing both let _ = ft_to_m.to_projjson(Some(true), None, None).unwrap(); let _ = ft_to_m.to_projjson(None, None, None).unwrap(); From 081091f1469766f5099a272e7985ebe541f18deb Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Thu, 18 Jan 2024 16:16:04 -0500 Subject: [PATCH 08/12] restore test with schema --- src/proj.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/proj.rs b/src/proj.rs index f2372515..655a5a0e 100644 --- a/src/proj.rs +++ b/src/proj.rs @@ -1550,7 +1550,13 @@ mod test { let to = "EPSG:26946"; let ft_to_m = Proj::new_known_crs(from, to, None).unwrap(); // Because libproj has been fussy about passing empty options strings we're testing both - let _ = ft_to_m.to_projjson(Some(true), None, None).unwrap(); + let _ = ft_to_m + .to_projjson( + Some(true), + None, + Some("https://proj.org/schemas/v0.7/projjson.schema.json"), + ) + .unwrap(); let _ = ft_to_m.to_projjson(None, None, None).unwrap(); // TODO: do we want to compare one of the results to proj's output? } From 6d0724117a5f2ee0e505a923a2dae4f52d6d1888 Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Thu, 18 Jan 2024 16:37:12 -0500 Subject: [PATCH 09/12] test? --- src/proj.rs | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/src/proj.rs b/src/proj.rs index 655a5a0e..413294df 100644 --- a/src/proj.rs +++ b/src/proj.rs @@ -1084,45 +1084,34 @@ impl Proj { indentation_width: Option, schema: Option<&str>, ) -> Result { - let opts_p = match (multiline, indentation_width, schema) { - (None, None, None) => None, + let out_ptr = match (multiline, indentation_width, schema) { + (None, None, None) => unsafe { proj_as_projjson(self.ctx, self.c_proj, ptr::null()) }, _ => { let mut opts = vec![]; if let Some(multiline) = multiline { if multiline { - opts.push(CString::new("MULTILINE=YES")?); + opts.push(CString::new("MULTILINE=YES")?) } else { - opts.push(CString::new("MULTILINE=NO")?); + opts.push(CString::new("MULTILINE=NO")?) } - } + }; if let Some(indentation_width) = indentation_width { opts.push(CString::new(format!( "INDENTATION_WIDTH={}", indentation_width - ))?); + ))?) } if let Some(schema) = schema { - opts.push(CString::new(format!("SCHEMA={}", schema))?); + opts.push(CString::new(format!("SCHEMA={}", schema))?) } - - Some(opts) + let opts_ptrs = opts.iter().map(|x| x.as_ptr()).collect::>(); + let result = unsafe { proj_as_projjson(self.ctx, self.c_proj, opts_ptrs.as_ptr()) }; + let x = opts; + result } }; unsafe { - // NOTE: we can't create the pointer to the vec of strings too early! As the [docstring - // of `as_ptr` - // mentions](https://doc.rust-lang.org/std/ffi/struct.CStr.html#method.as_ptr): - // > It is your responsibility to make sure that the underlying memory is not freed too - // > early. For example, the following code will cause undefined behavior when ptr is - // > used inside the unsafe block: - let out_ptr = if let Some(opts_p) = opts_p { - let opts_c = opts_p.iter().map(|x| x.as_ptr()).collect::>(); - proj_as_projjson(self.ctx, self.c_proj, opts_c.as_ptr()) - } else { - proj_as_projjson(self.ctx, self.c_proj, ptr::null()) - }; - if out_ptr.is_null() { // Not sure the best way to retrieve and return the error todo!() From 23a6085f0bba7e62d1e4cc779fa09a2bd14ce08e Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Thu, 18 Jan 2024 16:41:50 -0500 Subject: [PATCH 10/12] use steph's string concat --- src/proj.rs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/proj.rs b/src/proj.rs index 413294df..0b1dda77 100644 --- a/src/proj.rs +++ b/src/proj.rs @@ -1090,24 +1090,21 @@ impl Proj { let mut opts = vec![]; if let Some(multiline) = multiline { if multiline { - opts.push(CString::new("MULTILINE=YES")?) + opts.push(String::from("MULTILINE=YES")) } else { - opts.push(CString::new("MULTILINE=NO")?) + opts.push(String::from("MULTILINE=NO")) } }; if let Some(indentation_width) = indentation_width { - opts.push(CString::new(format!( - "INDENTATION_WIDTH={}", - indentation_width - ))?) + opts.push(format!("INDENTATION_WIDTH={}", indentation_width)) } if let Some(schema) = schema { - opts.push(CString::new(format!("SCHEMA={}", schema))?) + opts.push(format!("SCHEMA={}", schema)) } - let opts_ptrs = opts.iter().map(|x| x.as_ptr()).collect::>(); - let result = unsafe { proj_as_projjson(self.ctx, self.c_proj, opts_ptrs.as_ptr()) }; - let x = opts; - result + let sep = if opts.len() > 1 { "," } else { "" }; + let opts_c = CString::new(opts.join(sep))?; + let opts_ptrs = vec![opts_c.as_ptr()]; + unsafe { proj_as_projjson(self.ctx, self.c_proj, opts_ptrs.as_ptr()) } } }; From e74ccfbda087682c7af90c923b07135d9110ae83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20H=C3=BCgel?= Date: Fri, 19 Jan 2024 11:37:57 +0000 Subject: [PATCH 11/12] Try to simplify projjson call again --- src/proj.rs | 41 +++++++++++++++++++---------------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/src/proj.rs b/src/proj.rs index 0b1dda77..8f60f175 100644 --- a/src/proj.rs +++ b/src/proj.rs @@ -1084,31 +1084,28 @@ impl Proj { indentation_width: Option, schema: Option<&str>, ) -> Result { - let out_ptr = match (multiline, indentation_width, schema) { - (None, None, None) => unsafe { proj_as_projjson(self.ctx, self.c_proj, ptr::null()) }, - _ => { - let mut opts = vec![]; - if let Some(multiline) = multiline { - if multiline { - opts.push(String::from("MULTILINE=YES")) - } else { - opts.push(String::from("MULTILINE=NO")) - } - }; - if let Some(indentation_width) = indentation_width { - opts.push(format!("INDENTATION_WIDTH={}", indentation_width)) - } - if let Some(schema) = schema { - opts.push(format!("SCHEMA={}", schema)) - } - let sep = if opts.len() > 1 { "," } else { "" }; - let opts_c = CString::new(opts.join(sep))?; - let opts_ptrs = vec![opts_c.as_ptr()]; - unsafe { proj_as_projjson(self.ctx, self.c_proj, opts_ptrs.as_ptr()) } + let mut opts = vec![]; + if let Some(multiline) = multiline { + if multiline { + opts.push(CString::new(String::from("MULTILINE=YES"))?) + } else { + opts.push(CString::new(String::from("MULTILINE=NO"))?) } }; - + if let Some(indentation_width) = indentation_width { + opts.push(CString::new(format!( + "INDENTATION_WIDTH={}", + indentation_width + ))?) + } + if let Some(schema) = schema { + opts.push(CString::new(format!("SCHEMA={}", schema))?) + } + let mut opts_ptrs: Vec<_> = opts.iter().map(|cs| cs.as_ptr()).collect(); + // we always have to terminate with a null pointer, even if the opts are empty + opts_ptrs.push(ptr::null()); unsafe { + let out_ptr = proj_as_projjson(self.ctx, self.c_proj, opts_ptrs.as_ptr()); if out_ptr.is_null() { // Not sure the best way to retrieve and return the error todo!() From 4248536c1282a8dffe306f72645d8c5598f9fc47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20H=C3=BCgel?= Date: Mon, 1 Jul 2024 11:20:08 +0100 Subject: [PATCH 12/12] Return error on failure --- src/proj.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/proj.rs b/src/proj.rs index 8f60f175..4b28627b 100644 --- a/src/proj.rs +++ b/src/proj.rs @@ -125,6 +125,8 @@ pub enum ProjError { DownloadError(String, String, u8), #[error("The current definition could not be retrieved")] Definition, + #[error("The definition could not be represented in the requested JSON format")] + ExportToJson, } #[cfg(feature = "network")] @@ -1107,8 +1109,7 @@ impl Proj { unsafe { let out_ptr = proj_as_projjson(self.ctx, self.c_proj, opts_ptrs.as_ptr()); if out_ptr.is_null() { - // Not sure the best way to retrieve and return the error - todo!() + Err(ProjError::ExportToJson) } else { Ok(_string(out_ptr)?) }