Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid pointer redirection for Optional slices in Go #198

Merged
merged 9 commits into from
Sep 24, 2024
Merged
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions cli/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ pub struct TypeScriptParams {
pub struct GoParams {
pub package: String,
pub uppercase_acronyms: Vec<String>,
pub no_pointer_slice: bool,
hculea marked this conversation as resolved.
Show resolved Hide resolved
pub type_mappings: HashMap<String, String>,
}

Expand Down
1 change: 1 addition & 0 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ fn language(
package: config.go.package,
type_mappings: config.go.type_mappings,
uppercase_acronyms: config.go.uppercase_acronyms,
no_pointer_slice: config.go.no_pointer_slice,
..Default::default()
}),
#[cfg(not(feature = "go"))]
Expand Down
52 changes: 51 additions & 1 deletion core/src/language/go.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,24 @@ pub struct Go {
/// Whether or not to exclude the version header that normally appears at the top of generated code.
/// If you aren't generating a snapshot test, this setting can just be left as a default (false)
pub no_version_header: bool,
/// Whether or not slices should be translated with a pointer redirection.
///
/// It is rather unusual in Go to have pointers to slices. This is because, in Go, slices are already reference types.
/// However, an edge case can occur:
///
/// type A struct {
/// Slice []string `json:",omitempty"`
/// }
///
/// type B struct {
/// Slice *[]string `json:",omitempty"`
/// }
/// For type A, both Slice: nil and Slice: []string{} have the same JSON serialisation (Slice is omitted).
/// For type B Slice: nil and Slice: []string{} both have a different JSON serialisation.
/// In the first case, Slice is omitted. In the second case the field has the value [].
///
/// This, however, is rarely applicable in practice, and having this feature does not justify exposing an unintuitive user interface.
pub no_pointer_slice: bool,
}

impl Language for Go {
Expand Down Expand Up @@ -102,7 +120,15 @@ impl Language for Go {
format!("[]{}", self.format_type(rtype, generic_types)?)
}
SpecialRustType::Option(rtype) => {
format!("*{}", self.format_type(rtype, generic_types)?)
format!(
"{}{}",
if rtype.is_vec() && self.no_pointer_slice {
""
} else {
"*"
},
self.format_type(rtype, generic_types)?
)
}
SpecialRustType::HashMap(rtype1, rtype2) => format!(
"map[{}]{}",
Expand Down Expand Up @@ -505,3 +531,27 @@ fn convert_acronyms_to_uppercase(uppercase_acronyms: Vec<String>, name: &str) ->
}
res
}

mod test {
#[test]
fn no_pointer_slice() {
let mut go = super::Go {
..Default::default()
};

let optional_slice = super::SpecialRustType::Option(Box::new(
crate::rust_types::RustType::Special(super::SpecialRustType::Vec(Box::new(
crate::rust_types::RustType::Special(super::SpecialRustType::I32),
))),
));

// by default, optional slices should be translated with a pointer redirection
let go_slice = super::Language::format_special_type(&mut go, &optional_slice, &[]).unwrap();
assert_eq!(go_slice, "*[]int");

// when specifically opting out of this via the config, the pointer redirection should be omitted
go.no_pointer_slice = true;
let go_slice = super::Language::format_special_type(&mut go, &optional_slice, &[]).unwrap();
assert_eq!(go_slice, "[]int");
}
}
Loading