serde_json_lodash 0.1.16

lodash.js ported version, work with serde_json::Value
Documentation
# serde_json_lodash

[![Documentation](https://img.shields.io/crates/v/serde_json_lodash?label=latest)](https://docs.rs/serde_json_lodash)
[![build status](https://github.com/up9cloud/serde_json_lodash/workflows/CI/badge.svg?branch=main&event=push)](https://github.com/up9cloud/serde_json_lodash/actions)
![Downloads](https://img.shields.io/crates/d/serde_json_lodash.svg)

[serde_json::Value](https://docs.serde.rs/serde_json/value/enum.Value.html) with [lodash.js](https://github.com/lodash/lodash) spec, makes life easier.

## Usage

> Cargo.toml

```toml
[dependencies]
serde_json_lodash = "0.1"
```

> main.rs

```rust
#[macro_use] extern crate serde_json_lodash;
use serde_json::json;
fn main() {
  // macro style, optional parameters
  assert_eq!(
    merge!(json!({'a':1}), json!({'b':2}), json!({'c':3})),
    json!({'a': 1, 'b': 2, 'c': 3})
  );

  // fn style, fixed parameters
  use serde_json_lodash::merge;
  assert_eq!(
    merge(json!({'a':1}), json!({'b':2})),
    json!({'a': 1, 'b': 2})
  );

  // `x_`, `_x` helpers for simple types
  assert_eq!(capitalize!(json!("FRED")), json!("Fred"));
  assert_eq!(x_capitalize!("FRED"), json!("Fred"));
  assert_eq!(capitalize_x!(json!("FRED")), "Fred".to_owned());
  assert_eq!(x_capitalize_x!("FRED"), "Fred".to_owned());
}
```

## Concepts

All implements should be same as lodash as possible

How?

- Every function from lodash.js should be implemented both `fn` and `macro`
  - marco is for optional parameters usages
- The main inputs and return values should be *`serde_json::Value`*, excepts:
  - Inputs:
    - If the input parameters are options, not data, always using *primitive type* instead Value
      - e.q. `_.chunk(array, [size=1])` => `::chunk(json!([1,2,3]), 2)`, size should be `usize`, not `Value::Number`
    - Some cases we use *`std::ops::Fn`* as input parameter
      - e.q. `_.findIndex(array, predicate, ...)` => `::find_index(..., predicate: fn(&Value) -> bool, ...)`
  - Retune values:
    - If return value is statistic, using *primitive type* instead Value
      - e.q. `_.findIndex(...)` => `::find_index(...) -> isize`, return value should be `isize`, not `Value::Number`
    - Because there is no `undefined` type in serde_json, so if the original function return `undefined`, the ported version should return Value::Null
- If the original function allows optional parameters:
  - known amount, then the ported fn should *should be as required*
    - e.q. `_.get(object, path, [defaultValue])` => `::get(object, path, defaultValue)`
  - infinity amount, the ported fn should *only keep one, and no more optionals*
    - e.q. `_.merge(object, [...sources])` => `::merge(object, source)`, but macro could `::merge!(object, source1, source2, ...)`
- It might implement helper functions, for different input and output types:
  - with *`x_` prefix*: input is not Value, will be downgrade type
    - e.q. `x_capitalize(&str) -> Value`
  - with *`_x` suffix*: output is not Value, will be downgrade type
    - e.q. `capitalize_x(Value) -> String`
  - with *both `x_` and `_x`*
    - e.q. `x_capitalize_x(&str) -> &str`, `x_add_x(n: Number, n2: Number) -> Number`
  - If the function accept multiple types, the helper functions will only choose one type to implement
    - e.q. `_.toString([1,2])`, `_.toString(123)` => `::x_to_string(v: &str) -> Value`
- About the test cases:
  - `Examples:` section should be exactly same as the examples in lodash doc.
  - More test cases should all be put in the `More examples` section, we relied on powerful rust's doc test

## Dev memo

```bash
# Up
./dev.sh

# Watch and test single file
./dev.sh --doc set

# Lint
./lint.sh

# Preview doc
cargo doc --open

# Bump patch version and push
./bump_push.sh
```

> Check lodash.js api

```console
$ npm i
$ node
Welcome to Node.js v15.14.0.
Type ".help" for more information.
> const l = require('lodash')
undefined
> l.toString()
''
>
```