# Swag [![Build Status](https://github.com/go-openapi/swag/actions/workflows/go-test.yml/badge.svg)](https://github.com/go-openapi/swag/actions?query=workflow%3A"go+test") [![codecov](https://codecov.io/gh/go-openapi/swag/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/swag)

[![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io)
[![license](https://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/swag/master/LICENSE)
[![Go Reference](https://pkg.go.dev/badge/github.com/go-openapi/swag.svg)](https://pkg.go.dev/github.com/go-openapi/swag)
[![Go Report Card](https://goreportcard.com/badge/github.com/go-openapi/swag)](https://goreportcard.com/report/github.com/go-openapi/swag)

Package `swag` contains a bunch of helper functions for go-openapi and go-swagger projects.

You may also use it standalone for your projects.

> **NOTE**
> `swag` is one of the foundational building blocks of the go-openapi initiative.
> Most repositories in `github.com/go-openapi/...` depend on it in some way.
> And so does our CLI tool `github.com/go-swagger/go-swagger`,
> as well as the code generated by this tool.

* [Contents](#contents)
* [Dependencies](#dependencies)
* [Release Notes](#release-notes)
* [Note to contributors](#note-to-contributors)
* [TODOs, suggestions and plans](#todos-suggestions-and-plans)

## Contents

`go-openapi/swag` exposes a collection of relatively independent modules.

Moving forward, no additional feature will be added to the `swag` API directly at the root package level,
which remains there for backward-compatibility purposes. All exported top-level features are now deprecated.

Child modules will continue to evolve and some new ones may be added in the future.

| Module        | Content | Main features |
|---------------|---------|---------------|
| `cmdutils`     | utilities to work with CLIs ||
| `conv`        | type conversion utilities | convert between values and pointers for any types<br />convert from string to builtin types (wraps `strconv`)<br />require `./typeutils` (test dependency)<br /> |
| `fileutils`   | file utilities | |
| `jsonname`    | JSON utilities | infer JSON names from `go` properties<br /> | 
| `jsonutils`   | JSON utilities | fast json concatenation<br />read and write JSON from and to dynamic `go` data structures<br />~require `github.com/mailru/easyjson`~<br /> |
| `loading`     | file loading | load from file or http<br />require `./yamlutils`<br /> |
| `mangling`    | safe name generation | name mangling for `go`<br /> |
| `netutils`    | networking utilities | host, port from address<br /> |
| `stringutils` | `string` utilities | search in slice (with case-insensitive)<br />split/join query parameters as arrays<br /> |
| `typeutils`   | `go` types utilities | check the zero value for any type<br />safe check for a nil value<br /> |
| `yamlutils`   | YAML utilities | converting YAML to JSON<br />loading YAML into a dynamic YAML document<br />maintaining the original order of keys in YAML objects<br />require `./jsonutils`<br />~require `github.com/mailru/easyjson`~<br />require `go.yaml.in/yaml/v3`<br /> |

---

## Dependencies

The root module `github.com/go-openapi/swag` at the repo level maintains a few 
dependencies outside of the standard library.

* YAML utilities depend on `go.yaml.in/yaml/v3`
* JSON utilities depend on their registered adapter module:
   * by default, only the standard library is used
   * `github.com/mailru/easyjson` is now only a dependency for module
     `github.com/go-openapi/swag/jsonutils/adapters/easyjson/json`,
     for users willing to import that module.
   * integration tests and benchmarks use all the dependencies are published as their own module
* other dependencies are test dependencies drawn from `github.com/stretchr/testify`

## Release notes

### v0.25.1

* fixes a data race that could occur when using the standard library implementation of a JSON ordered map

### v0.25.0

**New with this release**:

* requires `go1.24`, as iterators are being introduced
* removes the dependency to `mailru/easyjson` by default (#68)
  * functionality remains the same, but performance may somewhat degrade for applications
    that relied on `easyjson`
  * users of the JSON or YAML utilities who want to use `easyjson` as their prefered JSON serializer library
    will be able to do so by registering this the corresponding JSON adapter at runtime. See below.
  * ordered keys in JSON and YAML objects: this feature used to rely solely on `easyjson`.
    With this release, an implementation relying on the standard `encoding/json` is provided.
  * an independent [benchmark](./jsonutils/adapters/testintegration/benchmarks/README.md) to compare the different adapters
* improves the "float is integer" check (`conv.IsFloat64AJSONInteger`) (#59)
* removes the _direct_ dependency to `gopkg.in/yaml.v3` (indirect dependency is still incurred through `stretchr/testify`) (#127)
* exposed `conv.IsNil()` (previously kept private): a safe nil check (accounting for the "non-nil interface with nil value" nonsensical go trick)

**What coming next?**

Moving forward, we want to :
* provide an implementation of the JSON adapter based on `encoding/json/v2`, for `go1.25` builds.
* provide similar implementations for `goccy/go-json` and `jsoniterator/go`, and perhaps some other
  similar libraries may be interesting too.


**How to explicitly register a dependency at runtime**?

The following would maintain how JSON utilities proposed by `swag` used work, up to `v0.24.1`.

  ```go
  import "github.com/go-openapi/swag/jsonutils/adapters/easyjson/json"

  func init() {
    json.Register()
  }
  ```

Subsequent calls to `jsonutils.ReadJSON()` or `jsonutils.WriteJSON()` will switch to `easyjson`
whenever the passed data structures implement the `easyjson.Unmarshaler` or `easyjson.Marshaler` respectively,
or fallback to the standard library.

### v0.24.0

With this release, we have largely modernized the API of `swag`:

* The traditional `swag` API is still supported: code that imports `swag` will still
  compile and work the same.
* A deprecation notice is published to encourage consumers of this library to adopt
  the newer API
* **Deprecation notice** 
  * configuration through global variables is now deprecated, in favor of options passed as parameters
  * all helper functions are moved to more specialized packages, which are exposed as 
    go modules. Importing such a module would reduce the footprint of dependencies.
  * _all_ functions, variables, constants exposed by the deprecated API have now moved, so
    that consumers of the new API no longer need to import github.com/go-openapi/swag, but
    should import the desired sub-module(s).

**New with this release**:

* [x] type converters and pointer to value helpers now support generic types
* [x] name mangling now support pluralized initialisms (issue #46)
      Strings like "contact IDs" are now recognized as such a plural form and mangled as a linter would expect.
* [x] performance: small improvements to reduce the overhead of convert/format wrappers (see issues #110, or PR #108)
* [x] performance: name mangling utilities run ~ 10% faster (PR #115)

---

## Note to contributors

A mono-repo structure comes with some unavoidable extra pains...

* Testing

> The usual `go test ./...` command, run from the root of this repo won't work any longer to test all submodules.
>
> Each module constitutes an independant unit of test. So you have to run `go test` inside each module.
> Or you may take a look at how this is achieved by CI
> [here] https://github.com/go-openapi/swag/blob/master/.github/workflows/go-test.yml).
>
> There are also some alternative tricks using `go work`, for local development, if you feel comfortable with
> go workspaces. Perhaps some day, we'll have a `go work test` to run all tests without any hack.

* Releasing

> Each module follows its own independant module versioning.
>
> So you have tags like `mangling/v0.24.0`, `fileutils/v0.24.0` etc that are used by `go mod` and `go get`
> to refer to the tagged version of each module specifically.
>
> This means we may release patches etc to each module independently.
>
> We'd like to adopt the rule that modules in this repo would only differ by a patch version
> (e.g. `v0.24.5` vs `v0.24.3`), and we'll level all modules whenever a minor version is introduced.
>
> A script in `./hack` is provided to tag all modules with the same version in one go.

* Continuous integration

> At this moment, all tests in all modules are systematically run over the full test matrix (3 platform x 2 go releases).
> This generates quite a lot of jobs.
>
> We ought to reduce the number of jobs required to test a PR focused on only a few modules.

## Todos, suggestions and plans

All kinds of contributions are welcome.

A few ideas:

* [x] Complete the split of dependencies to isolate easyjson from the rest
* [x] Improve CI to reduce needed tests
* [x] Replace dependency to `gopkg.in/yaml.v3` (`yamlutil`)
* [ ] Improve mangling utilities (improve readability, support for capitalized words,
      better word substitution for non-letter symbols...)
* [ ] Move back to this common shared pot a few of the technical features introduced by go-swagger independently
      (e.g. mangle go package names, search package with go modules support, ...)
* [ ] Apply a similar mono-repo approach to go-openapi/strfmt which suffer from similar woes: bloated API,
      imposed dependency to some database driver.
* [ ] Adapt `go-swagger` (incl. generated code) to the new `swag` API.
* [ ] Factorize some tests, as there is a lot of redundant testing code in `jsonutils`
* [ ] Benchmark & profiling: publish independently the tool built to analyze and chart benchmarks (e.g. similar to `benchvisual`)
* [ ] more thorough testing for nil / null case
* [ ] ci pipeline to manage releases
* [ ] cleaner mockery generation (doesn't work out of the box for all sub-modules)
