Repository: diesel-rs/diesel
File: diesel/src/pg/expression/functions.rs (Rust)

Code before:
    /// #     use diesel::sql_types::{Array, Json, Text};
    /// #     use serde_json::Value;
    /// #     let connection = &mut establish_connection();
    /// let json = diesel::select(json_object::<Array<Text>,_>(vec!["hello","world"]))
    ///                 .get_result::<Option<Value>>(connection)?;
    /// let expected:Value = serde_json::json!({"hello":"world"});
    /// assert_eq!(expected,json.unwrap());
    ///
    /// let json = diesel::select(json_object::<Array<Text>,_>(vec!["hello","world","John","Doe"]))
    ///                 .get_result::<Option<Value>>(connection)?;
    /// let expected:Value = serde_json::json!({"hello":"world","John":"Doe"});
    /// assert_eq!(expected,json.unwrap());
    ///
    /// let json = diesel::select(json_object::<Array<Text>,_>(vec!["hello","world","John"]))
    ///                 .get_result::<Option<Value>>(connection);
    /// assert!(json.is_err());
    ///
    /// let empty:Vec<String> = Vec::new();
    /// let json = diesel::select(json_object::<Array<Text>,_>(empty))
    ///                 .get_result::<Option<Value>>(connection);
    /// assert!(json.is_err());
    ///
    /// #     Ok(())
    /// # }
    /// ```
    fn json_object<Arr:MaybeNullableValue<Array<Nullable<Text>>>>(text_array:Arr) -> Nullable<Json>;
}


Review comment (refactor):
I'm sorry but this is still not correct. The correct signature would be:

```suggestion
    fn json_object<Arr:ArrayOrNullableArray + MaybeNullableValue<Json>>(text_array:Arr) -> Arr::Out where Arr::Inner: TextOrNullableText;
```

To explain the bounds:
* `Arr: ArrayOrNullableArray` says the input type is either `Array<T>` or `Nullable<Array<T>>`
* `Arr: MaybeNullableValue<Json>` says that the output values has the same nullability as the input value. That means `Nullable<Array<T>>` evaluates to `Nullable<Json>` and `Array<T>` evaluates to `Json` via the `Arr::Out` return type.
* `Arr::Inner: TextOrNullableText` says that the inner type of the array is either `Text` or `Nullable<Text>`

Now the bad thing is that the `declare_sql_function!()` macro does not support where clauses, so we need to have a new helper trait for this. For that we need to declare:

```rust
trait TextArrayOrNullableTextArray {}
```

and implement it for the following types:
* `Array<Text>`
* `Nullable<Array<Text>>`
* `Array<Nullable<Text>>`
* `Nullable<Array<Nullable<Text>>>`

(which essentially only lists the supported argument types)

That gives us then the following function signature:
```suggestion
    fn json_object<Arr:TextArrayOrNullableTextArray + MaybeNullableValue<Json>>>(text_array:Arr) -> Arr:Out;
```
```

``

Code after:
    /// # fn run_test() -> QueryResult<()> {
    /// #     use diesel::dsl::to_jsonb;
    /// #     use serde_json::{json, Value};
    /// #     use diesel::sql_types::{Integer, Array, Jsonb, Text, Nullable};
    /// #     let connection = &mut establish_connection();
    /// let result = diesel::select(to_jsonb::<Integer, _>(1))
    ///     .get_result::<Value>(connection)?;
    ///
    /// assert_eq!(json!(1), result);
    ///
    /// let result = diesel::select(to_jsonb::<Array<Text>, _>(vec!["abc", "def"]))
    ///     .get_result::<Value>(connection)?;
    ///
    /// assert_eq!(json!(["abc", "def"]), result);
    ///
    /// let result = diesel::select(to_jsonb::<Array<Nullable<Text>>, _>(Vec::<String>::new()))
    ///     .get_result::<Value>(connection)?;
    ///
    /// assert_eq!(json!([]), result);
    ///
    /// let result = diesel::select(to_jsonb::<Nullable<Text>, _>(None::<String>))
    ///     .get_result::<Option<Value>>(connection)?;
    ///
    /// assert!(result.is_none());
    ///
    /// #     Ok(())
    /// # }
    /// ```
    fn to_jsonb<E: MaybeNullableValue<Jsonb>>(e: E) -> E::Out;
}

define_sql_function! {
    /// Builds a JSON object out of a text array. The array must have an even number of members,
    /// in which case they are taken as alternating key/value pairs
    ///
    /// # Example
    ///
    /// ```rust
    /// # include!("../../doctest_setup.rs");
    /// #
    /// # fn main() {
    /// #     run_test().unwrap();
    /// # }
    /// #
    /// # fn run_test() -> QueryResult<()> {
    /// #     use diesel::dsl::json_object;
    /// #     use diesel::sql_types::{Array, Json, Nullable, Text};
    /// #     use serde_json::Value;
    /// #     let connection = &mut establish_connection();
    /// let json = diesel::select(json_object::<Array<Text>,_>(vec!["hello","world"]))
    ///                 .get_result::<Value>(connection)?;