text:
#==============================================================================#
# ECR.jl
#
# This file is generated from:
# https://github.com/aws/aws-sdk-js/blob/master/apis/ecr-2015-09-21.normal.json
#==============================================================================#

__precompile__()

module ECR

using AWSCore

"""
    using AWSSDK.ECR.batch_check_layer_availability
    batch_check_layer_availability([::AWSConfig], arguments::Dict)
    batch_check_layer_availability([::AWSConfig]; repositoryName=, layerDigests=, <keyword arguments>)

    using AWSCore.Services.ecr
    ecr([::AWSConfig], "BatchCheckLayerAvailability", arguments::Dict)
    ecr([::AWSConfig], "BatchCheckLayerAvailability", repositoryName=, layerDigests=, <keyword arguments>)

# BatchCheckLayerAvailability Operation

Check the availability of multiple image layers in a specified registry and repository.

**Note**
> This operation is used by the Amazon ECR proxy, and it is not intended for general use by customers for pulling and pushing images. In most cases, you should use the `docker` CLI to pull, tag, and push images.

# Arguments

## `registryId = ::String`
The AWS account ID associated with the registry that contains the image layers to check. If you do not specify a registry, the default registry is assumed.

## `repositoryName = ::String` -- *Required*
The name of the repository that is associated with the image layers to check.

## `layerDigests = [::String, ...]` -- *Required*
The digests of the image layers to check.

# Returns

`BatchCheckLayerAvailabilityResponse`

# Exceptions

`RepositoryNotFoundException`, `InvalidParameterException` or `ServerException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/BatchCheckLayerAvailability)
"""
@inline batch_check_layer_availability(aws::AWSConfig=default_aws_config(); args...) = batch_check_layer_availability(aws, args)

@inline batch_check_layer_availability(aws::AWSConfig, args) = AWSCore.Services.ecr(aws, "BatchCheckLayerAvailability", args)

@inline batch_check_layer_availability(args) = batch_check_layer_availability(default_aws_config(), args)

"""
    using AWSSDK.ECR.batch_delete_image
    batch_delete_image([::AWSConfig], arguments::Dict)
    batch_delete_image([::AWSConfig]; repositoryName=, imageIds=, <keyword arguments>)

    using AWSCore.Services.ecr
    ecr([::AWSConfig], "BatchDeleteImage", arguments::Dict)
    ecr([::AWSConfig], "BatchDeleteImage", repositoryName=, imageIds=, <keyword arguments>)

# BatchDeleteImage Operation

Deletes a list of specified images within a specified repository. Images are specified with either `imageTag` or `imageDigest`.

You can remove a tag from an image by specifying the image's tag in your request. When you remove the last tag from an image, the image is deleted from your repository.

You can completely delete an image (and all of its tags) by specifying the image's digest in your request.

# Arguments

## `registryId = ::String`
The AWS account ID associated with the registry that contains the image to delete. If you do not specify a registry, the default registry is assumed.

## `repositoryName = ::String` -- *Required*
The repository that contains the image to delete.

## `imageIds = [[ ... ], ...]` -- *Required*
A list of image ID references that correspond to images to delete. The format of the `imageIds` reference is `imageTag=tag` or `imageDigest=digest`.
```
 imageIds = [[
        "imageDigest" =>  ::String,
        "imageTag" =>  ::String
    ], ...]
```

# Returns

`BatchDeleteImageResponse`

# Exceptions

`ServerException`, `InvalidParameterException` or `RepositoryNotFoundException`.

# Example: To delete multiple images

This example deletes images with the tags precise and trusty in a repository called ubuntu in the default registry for an account.

Input:
```
[
    "imageIds" => [
        [
            "imageTag" => "precise"
        ]
    ],
    "repositoryName" => "ubuntu"
]
```

Output:
```
Dict(
    "failures" => [

    ],
    "imageIds" => [
        Dict(
            "imageDigest" => "sha256:examplee6d1e504117a17000003d3753086354a38375961f2e665416ef4b1b2f",
            "imageTag" => "precise"
        )
    ]
)
```

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/BatchDeleteImage)
"""
@inline batch_delete_image(aws::AWSConfig=default_aws_config(); args...) = batch_delete_image(aws, args)

@inline batch_delete_image(aws::AWSConfig, args) = AWSCore.Services.ecr(aws, "BatchDeleteImage", args)

@inline batch_delete_image(args) = batch_delete_image(default_aws_config(), args)

"""
    using AWSSDK.ECR.batch_get_image
    batch_get_image([::AWSConfig], arguments::Dict)
    batch_get_image([::AWSConfig]; repositoryName=, imageIds=, <keyword arguments>)

    using AWSCore.Services.ecr
    ecr([::AWSConfig], "BatchGetImage", arguments::Dict)
    ecr([::AWSConfig], "BatchGetImage", repositoryName=, imageIds=, <keyword arguments>)

# BatchGetImage Operation

Gets detailed information for specified images within a specified repository. Images are specified with either `imageTag` or `imageDigest`.

# Arguments

## `registryId = ::String`
The AWS account ID associated with the registry that contains the images to describe. If you do not specify a registry, the default registry is assumed.

## `repositoryName = ::String` -- *Required*
The repository that contains the images to describe.

## `imageIds = [[ ... ], ...]` -- *Required*
A list of image ID references that correspond to images to describe. The format of the `imageIds` reference is `imageTag=tag` or `imageDigest=digest`.
```
 imageIds = [[
        "imageDigest" =>  ::String,
        "imageTag" =>  ::String
    ], ...]
```

## `acceptedMediaTypes = [::String, ...]`
The accepted media types for the request.

Valid values: `application/vnd.docker.distribution.manifest.v1+json` | `application/vnd.docker.distribution.manifest.v2+json` | `application/vnd.oci.image.manifest.v1+json`

# Returns

`BatchGetImageResponse`

# Exceptions

`ServerException`, `InvalidParameterException` or `RepositoryNotFoundException`.

# Example: To obtain multiple images in a single request

This example obtains information for an image with a specified image digest ID from the repository named ubuntu in the current account.

Input:
```
[
    "imageIds" => [
        [
            "imageTag" => "precise"
        ]
    ],
    "repositoryName" => "ubuntu"
]
```

Output:
```
Dict(
    "failures" => [

    ],
    "images" => [
        Dict(
            "imageId" => Dict(
                "imageDigest" => "sha256:example76bdff6d83a09ba2a818f0d00000063724a9ac3ba5019c56f74ebf42a",
                "imageTag" => "precise"
            ),
            "imageManifest" => "{
 "schemaVersion": 1,
 "name": "ubuntu",
 "tag": "precise",
...",
            "registryId" => "244698725403",
            "repositoryName" => "ubuntu"
        )
    ]
)
```

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/BatchGetImage)
"""
@inline batch_get_image(aws::AWSConfig=default_aws_config(); args...) = batch_get_image(aws, args)

@inline batch_get_image(aws::AWSConfig, args) = AWSCore.Services.ecr(aws, "BatchGetImage", args)

@inline batch_get_image(args) = batch_get_image(default_aws_config(), args)

"""
    using AWSSDK.ECR.complete_layer_upload
    complete_layer_upload([::AWSConfig], arguments::Dict)
    complete_layer_upload([::AWSConfig]; repositoryName=, uploadId=, layerDigests=, <keyword arguments>)

    using AWSCore.Services.ecr
    ecr([::AWSConfig], "CompleteLayerUpload", arguments::Dict)
    ecr([::AWSConfig], "CompleteLayerUpload", repositoryName=, uploadId=, layerDigests=, <keyword arguments>)

# CompleteLayerUpload Operation

Informs Amazon ECR that the image layer upload has completed for a specified registry, repository name, and upload ID. You can optionally provide a `sha256` digest of the image layer for data validation purposes.

**Note**
> This operation is used by the Amazon ECR proxy, and it is not intended for general use by customers for pulling and pushing images. In most cases, you should use the `docker` CLI to pull, tag, and push images.

# Arguments

## `registryId = ::String`
The AWS account ID associated with the registry to which to upload layers. If you do not specify a registry, the default registry is assumed.

## `repositoryName = ::String` -- *Required*
The name of the repository to associate with the image layer.

## `uploadId = ::String` -- *Required*
The upload ID from a previous [InitiateLayerUpload](@ref) operation to associate with the image layer.

## `layerDigests = [::String, ...]` -- *Required*
The `sha256` digest of the image layer.

# Returns

`CompleteLayerUploadResponse`

# Exceptions

`ServerException`, `InvalidParameterException`, `RepositoryNotFoundException`, `UploadNotFoundException`, `InvalidLayerException`, `LayerPartTooSmallException`, `LayerAlreadyExistsException` or `EmptyUploadException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/CompleteLayerUpload)
"""
@inline complete_layer_upload(aws::AWSConfig=default_aws_config(); args...) = complete_layer_upload(aws, args)

@inline complete_layer_upload(aws::AWSConfig, args) = AWSCore.Services.ecr(aws, "CompleteLayerUpload", args)

@inline complete_layer_upload(args) = complete_layer_upload(default_aws_config(), args)

"""
    using AWSSDK.ECR.create_repository
    create_repository([::AWSConfig], arguments::Dict)
    create_repository([::AWSConfig]; repositoryName=)

    using AWSCore.Services.ecr
    ecr([::AWSConfig], "CreateRepository", arguments::Dict)
    ecr([::AWSConfig], "CreateRepository", repositoryName=)

# CreateRepository Operation

Creates an image repository.

# Arguments

## `repositoryName = ::String` -- *Required*
The name to use for the repository. The repository name may be specified on its own (such as `nginx-web-app`) or it can be prepended with a namespace to group the repository into a category (such as `project-a/nginx-web-app`).

# Returns

`CreateRepositoryResponse`

# Exceptions

`ServerException`, `InvalidParameterException`, `RepositoryAlreadyExistsException` or `LimitExceededException`.

# Example: To create a new repository

This example creates a repository called nginx-web-app inside the project-a namespace in the default registry for an account.

Input:
```
[
    "repositoryName" => "project-a/nginx-web-app"
]
```

Output:
```
Dict(
    "repository" => Dict(
        "registryId" => "012345678901",
        "repositoryArn" => "arn:aws:ecr:us-west-2:012345678901:repository/project-a/nginx-web-app",
        "repositoryName" => "project-a/nginx-web-app"
    )
)
```

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/CreateRepository)
"""
@inline create_repository(aws::AWSConfig=default_aws_config(); args...) = create_repository(aws, args)

@inline create_repository(aws::AWSConfig, args) = AWSCore.Services.ecr(aws, "CreateRepository", args)

@inline create_repository(args) = create_repository(default_aws_config(), args)

"""
    using AWSSDK.ECR.delete_lifecycle_policy
    delete_lifecycle_policy([::AWSConfig], arguments::Dict)
    delete_lifecycle_policy([::AWSConfig]; repositoryName=, <keyword arguments>)

    using AWSCore.Services.ecr
    ecr([::AWSConfig], "DeleteLifecyclePolicy", arguments::Dict)
    ecr([::AWSConfig], "DeleteLifecyclePolicy", repositoryName=, <keyword arguments>)

# DeleteLifecyclePolicy Operation

Deletes the specified lifecycle policy.

# Arguments

## `registryId = ::String`
The AWS account ID associated with the registry that contains the repository. If you do not specify a registry, the default registry is assumed.

## `repositoryName = ::String` -- *Required*
The name of the repository.

# Returns

`DeleteLifecyclePolicyResponse`

# Exceptions

`ServerException`, `InvalidParameterException`, `RepositoryNotFoundException` or `LifecyclePolicyNotFoundException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/DeleteLifecyclePolicy)
"""
@inline delete_lifecycle_policy(aws::AWSConfig=default_aws_config(); args...) = delete_lifecycle_policy(aws, args)

@inline delete_lifecycle_policy(aws::AWSConfig, args) = AWSCore.Services.ecr(aws, "DeleteLifecyclePolicy", args)

@inline delete_lifecycle_policy(args) = delete_lifecycle_policy(default_aws_config(), args)

"""
    using AWSSDK.ECR.delete_repository
    delete_repository([::AWSConfig], arguments::Dict)
    delete_repository([::AWSConfig]; repositoryName=, <keyword arguments>)

    using AWSCore.Services.ecr
    ecr([::AWSConfig], "DeleteRepository", arguments::Dict)
    ecr([::AWSConfig], "DeleteRepository", repositoryName=, <keyword arguments>)

# DeleteRepository Operation

Deletes an existing image repository. If a repository contains images, you must use the `force` option to delete it.

# Arguments

## `registryId = ::String`
The AWS account ID associated with the registry that contains the repository to delete. If you do not specify a registry, the default registry is assumed.

## `repositoryName = ::String` -- *Required*
The name of the repository to delete.

## `force = ::Bool`
If a repository contains images, forces the deletion.

# Returns

`DeleteRepositoryResponse`

# Exceptions

`ServerException`, `InvalidParameterException`, `RepositoryNotFoundException` or `RepositoryNotEmptyException`.

# Example: To force delete a repository

This example force deletes a repository named ubuntu in the default registry for an account. The force parameter is required if the repository contains images.

Input:
```
[
    "force" => true,
    "repositoryName" => "ubuntu"
]
```

Output:
```
Dict(
    "repository" => Dict(
        "registryId" => "012345678901",
        "repositoryArn" => "arn:aws:ecr:us-west-2:012345678901:repository/ubuntu",
        "repositoryName" => "ubuntu"
    )
)
```

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/DeleteRepository)
"""
@inline delete_repository(aws::AWSConfig=default_aws_config(); args...) = delete_repository(aws, args)

@inline delete_repository(aws::AWSConfig, args) = AWSCore.Services.ecr(aws, "DeleteRepository", args)

@inline delete_repository(args) = delete_repository(default_aws_config(), args)

"""
    using AWSSDK.ECR.delete_repository_policy
    delete_repository_policy([::AWSConfig], arguments::Dict)
    delete_repository_policy([::AWSConfig]; repositoryName=, <keyword arguments>)

    using AWSCore.Services.ecr
    ecr([::AWSConfig], "DeleteRepositoryPolicy", arguments::Dict)
    ecr([::AWSConfig], "DeleteRepositoryPolicy", repositoryName=, <keyword arguments>)

# DeleteRepositoryPolicy Operation

Deletes the repository policy from a specified repository.

# Arguments

## `registryId = ::String`
The AWS account ID associated with the registry that contains the repository policy to delete. If you do not specify a registry, the default registry is assumed.

## `repositoryName = ::String` -- *Required*
The name of the repository that is associated with the repository policy to delete.

# Returns

`DeleteRepositoryPolicyResponse`

# Exceptions

`ServerException`, `InvalidParameterException`, `RepositoryNotFoundException` or `RepositoryPolicyNotFoundException`.

# Example: To delete the policy associated with a repository

This example deletes the policy associated with the repository named ubuntu in the current account.

Input:
```
[
    "repositoryName" => "ubuntu"
]
```

Output:
```
Dict(
    "policyText" => "{ ... }",
    "registryId" => "012345678901",
    "repositoryName" => "ubuntu"
)
```

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/DeleteRepositoryPolicy)
"""
@inline delete_repository_policy(aws::AWSConfig=default_aws_config(); args...) = delete_repository_policy(aws, args)

@inline delete_repository_policy(aws::AWSConfig, args) = AWSCore.Services.ecr(aws, "DeleteRepositoryPolicy", args)

@inline delete_repository_policy(args) = delete_repository_policy(default_aws_config(), args)

"""
    using AWSSDK.ECR.describe_images
    describe_images([::AWSConfig], arguments::Dict)
    describe_images([::AWSConfig]; repositoryName=, <keyword arguments>)

    using AWSCore.Services.ecr
    ecr([::AWSConfig], "DescribeImages", arguments::Dict)
    ecr([::AWSConfig], "DescribeImages", repositoryName=, <keyword arguments>)

# DescribeImages Operation

Returns metadata about the images in a repository, including image size, image tags, and creation date.

**Note**
> Beginning with Docker version 1.9, the Docker client compresses image layers before pushing them to a V2 Docker registry. The output of the `docker images` command shows the uncompressed image size, so it may return a larger image size than the image sizes returned by [DescribeImages](@ref).

# Arguments

## `registryId = ::String`
The AWS account ID associated with the registry that contains the repository in which to describe images. If you do not specify a registry, the default registry is assumed.

## `repositoryName = ::String` -- *Required*
A list of repositories to describe. If this parameter is omitted, then all repositories in a registry are described.

## `imageIds = [[ ... ], ...]`
The list of image IDs for the requested repository.
```
 imageIds = [[
        "imageDigest" =>  ::String,
        "imageTag" =>  ::String
    ], ...]
```

## `nextToken = ::String`
The `nextToken` value returned from a previous paginated `DescribeImages` request where `maxResults` was used and the results exceeded the value of that parameter. Pagination continues from the end of the previous results that returned the `nextToken` value. This value is `null` when there are no more results to return. This option cannot be used when you specify images with `imageIds`.

## `maxResults = ::Int`
The maximum number of repository results returned by `DescribeImages` in paginated output. When this parameter is used, `DescribeImages` only returns `maxResults` results in a single page along with a `nextToken` response element. The remaining results of the initial request can be seen by sending another `DescribeImages` request with the returned `nextToken` value. This value can be between 1 and 100. If this parameter is not used, then `DescribeImages` returns up to 100 results and a `nextToken` value, if applicable. This option cannot be used when you specify images with `imageIds`.

## `filter = ["tagStatus" =>  "TAGGED" or "UNTAGGED"]`
The filter key and value with which to filter your `DescribeImages` results.

# Returns

`DescribeImagesResponse`

# Exceptions

`ServerException`, `InvalidParameterException`, `RepositoryNotFoundException` or `ImageNotFoundException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/DescribeImages)
"""
@inline describe_images(aws::AWSConfig=default_aws_config(); args...) = describe_images(aws, args)

@inline describe_images(aws::AWSConfig, args) = AWSCore.Services.ecr(aws, "DescribeImages", args)

@inline describe_images(args) = describe_images(default_aws_config(), args)

"""
    using AWSSDK.ECR.describe_repositories
    describe_repositories([::AWSConfig], arguments::Dict)
    describe_repositories([::AWSConfig]; <keyword arguments>)

    using AWSCore.Services.ecr
    ecr([::AWSConfig], "DescribeRepositories", arguments::Dict)
    ecr([::AWSConfig], "DescribeRepositories", <keyword arguments>)

# DescribeRepositories Operation

Describes image repositories in a registry.

# Arguments

## `registryId = ::String`
The AWS account ID associated with the registry that contains the repositories to be described. If you do not specify a registry, the default registry is assumed.

## `repositoryNames = [::String, ...]`
A list of repositories to describe. If this parameter is omitted, then all repositories in a registry are described.

## `nextToken = ::String`
The `nextToken` value returned from a previous paginated `DescribeRepositories` request where `maxResults` was used and the results exceeded the value of that parameter. Pagination continues from the end of the previous results that returned the `nextToken` value. This value is `null` when there are no more results to return. This option cannot be used when you specify repositories with `repositoryNames`.

**Note**
> This token should be treated as an opaque identifier that is only used to retrieve the next items in a list and not for other programmatic purposes.

## `maxResults = ::Int`
The maximum number of repository results returned by `DescribeRepositories` in paginated output. When this parameter is used, `DescribeRepositories` only returns `maxResults` results in a single page along with a `nextToken` response element. The remaining results of the initial request can be seen by sending another `DescribeRepositories` request with the returned `nextToken` value. This value can be between 1 and 100. If this parameter is not used, then `DescribeRepositories` returns up to 100 results and a `nextToken` value, if applicable. This option cannot be used when you specify repositories with `repositoryNames`.

# Returns

`DescribeRepositoriesResponse`

# Exceptions

`ServerException`, `InvalidParameterException` or `RepositoryNotFoundException`.

# Example: To describe all repositories in the current account

The following example obtains a list and description of all repositories in the default registry to which the current user has access.

Input:
```
[

]
```

Output:
```
Dict(
    "repositories" => [
        Dict(
            "registryId" => "012345678910",
            "repositoryArn" => "arn:aws:ecr:us-west-2:012345678910:repository/ubuntu",
            "repositoryName" => "ubuntu"
        ),
        Dict(
            "registryId" => "012345678910",
            "repositoryArn" => "arn:aws:ecr:us-west-2:012345678910:repository/test",
            "repositoryName" => "test"
        )
    ]
)
```

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/DescribeRepositories)
"""
@inline describe_repositories(aws::AWSConfig=default_aws_config(); args...) = describe_repositories(aws, args)

@inline describe_repositories(aws::AWSConfig, args) = AWSCore.Services.ecr(aws, "DescribeRepositories", args)

@inline describe_repositories(args) = describe_repositories(default_aws_config(), args)

"""
    using AWSSDK.ECR.get_authorization_token
    get_authorization_token([::AWSConfig], arguments::Dict)
    get_authorization_token([::AWSConfig]; <keyword arguments>)

    using AWSCore.Services.ecr
    ecr([::AWSConfig], "GetAuthorizationToken", arguments::Dict)
    ecr([::AWSConfig], "GetAuthorizationToken", <keyword arguments>)

# GetAuthorizationToken Operation

Retrieves a token that is valid for a specified registry for 12 hours. This command allows you to use the `docker` CLI to push and pull images with Amazon ECR. If you do not specify a registry, the default registry is assumed.

The `authorizationToken` returned for each registry specified is a base64 encoded string that can be decoded and used in a `docker login` command to authenticate to a registry. The AWS CLI offers an `aws ecr get-login` command that simplifies the login process.

# Arguments

## `registryIds = [::String, ...]`
A list of AWS account IDs that are associated with the registries for which to get authorization tokens. If you do not specify a registry, the default registry is assumed.

# Returns

`GetAuthorizationTokenResponse`

# Exceptions

`ServerException` or `InvalidParameterException`.

# Example: To obtain an authorization token

This example gets an authorization token for your default registry.

Input:
```
[

]
```

Output:
```
Dict(
    "authorizationData" => [
        Dict(
            "authorizationToken" => "QVdTOkN...",
            "expiresAt" => "1470951892432",
            "proxyEndpoint" => "https://012345678901.dkr.ecr.us-west-2.amazonaws.com"
        )
    ]
)
```

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/GetAuthorizationToken)
"""
@inline get_authorization_token(aws::AWSConfig=default_aws_config(); args...) = get_authorization_token(aws, args)

@inline get_authorization_token(aws::AWSConfig, args) = AWSCore.Services.ecr(aws, "GetAuthorizationToken", args)

@inline get_authorization_token(args) = get_authorization_token(default_aws_config(), args)

"""
    using AWSSDK.ECR.get_download_url_for_layer
    get_download_url_for_layer([::AWSConfig], arguments::Dict)
    get_download_url_for_layer([::AWSConfig]; repositoryName=, layerDigest=, <keyword arguments>)

    using AWSCore.Services.ecr
    ecr([::AWSConfig], "GetDownloadUrlForLayer", arguments::Dict)
    ecr([::AWSConfig], "GetDownloadUrlForLayer", repositoryName=, layerDigest=, <keyword arguments>)

# GetDownloadUrlForLayer Operation

Retrieves the pre-signed Amazon S3 download URL corresponding to an image layer. You can only get URLs for image layers that are referenced in an image.

**Note**
> This operation is used by the Amazon ECR proxy, and it is not intended for general use by customers for pulling and pushing images. In most cases, you should use the `docker` CLI to pull, tag, and push images.

# Arguments

## `registryId = ::String`
The AWS account ID associated with the registry that contains the image layer to download. If you do not specify a registry, the default registry is assumed.

## `repositoryName = ::String` -- *Required*
The name of the repository that is associated with the image layer to download.

## `layerDigest = ::String` -- *Required*
The digest of the image layer to download.

# Returns

`GetDownloadUrlForLayerResponse`

# Exceptions

`ServerException`, `InvalidParameterException`, `LayersNotFoundException`, `LayerInaccessibleException` or `RepositoryNotFoundException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/GetDownloadUrlForLayer)
"""
@inline get_download_url_for_layer(aws::AWSConfig=default_aws_config(); args...) = get_download_url_for_layer(aws, args)

@inline get_download_url_for_layer(aws::AWSConfig, args) = AWSCore.Services.ecr(aws, "GetDownloadUrlForLayer", args)

@inline get_download_url_for_layer(args) = get_download_url_for_layer(default_aws_config(), args)

"""
    using AWSSDK.ECR.get_lifecycle_policy
    get_lifecycle_policy([::AWSConfig], arguments::Dict)
    get_lifecycle_policy([::AWSConfig]; repositoryName=, <keyword arguments>)

    using AWSCore.Services.ecr
    ecr([::AWSConfig], "GetLifecyclePolicy", arguments::Dict)
    ecr([::AWSConfig], "GetLifecyclePolicy", repositoryName=, <keyword arguments>)

# GetLifecyclePolicy Operation

Retrieves the specified lifecycle policy.

# Arguments

## `registryId = ::String`
The AWS account ID associated with the registry that contains the repository. If you do not specify a registry, the default registry is assumed.

## `repositoryName = ::String` -- *Required*
The name of the repository.

# Returns

`GetLifecyclePolicyResponse`

# Exceptions

`ServerException`, `InvalidParameterException`, `RepositoryNotFoundException` or `LifecyclePolicyNotFoundException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/GetLifecyclePolicy)
"""
@inline get_lifecycle_policy(aws::AWSConfig=default_aws_config(); args...) = get_lifecycle_policy(aws, args)

@inline get_lifecycle_policy(aws::AWSConfig, args) = AWSCore.Services.ecr(aws, "GetLifecyclePolicy", args)

@inline get_lifecycle_policy(args) = get_lifecycle_policy(default_aws_config(), args)

"""
    using AWSSDK.ECR.get_lifecycle_policy_preview
    get_lifecycle_policy_preview([::AWSConfig], arguments::Dict)
    get_lifecycle_policy_preview([::AWSConfig]; repositoryName=, <keyword arguments>)

    using AWSCore.Services.ecr
    ecr([::AWSConfig], "GetLifecyclePolicyPreview", arguments::Dict)
    ecr([::AWSConfig], "GetLifecyclePolicyPreview", repositoryName=, <keyword arguments>)

# GetLifecyclePolicyPreview Operation

Retrieves the results of the specified lifecycle policy preview request.

# Arguments

## `registryId = ::String`
The AWS account ID associated with the registry that contains the repository. If you do not specify a registry, the default registry is assumed.

## `repositoryName = ::String` -- *Required*
The name of the repository.

## `imageIds = [[ ... ], ...]`
The list of imageIDs to be included.
```
 imageIds = [[
        "imageDigest" =>  ::String,
        "imageTag" =>  ::String
    ], ...]
```

## `nextToken = ::String`
The `nextToken` value returned from a previous paginated
 `GetLifecyclePolicyPreviewRequest` request where `maxResults` was used and the
 results exceeded the value of that parameter. Pagination continues from the end of the
 previous results that returned the `nextToken` value. This value is
 `null` when there are no more results to return. This option cannot be used when you specify images with `imageIds`.

## `maxResults = ::Int`
The maximum number of repository results returned by `GetLifecyclePolicyPreviewRequest` in
 paginated output. When this parameter is used, `GetLifecyclePolicyPreviewRequest` only returns
 `maxResults` results in a single page along with a `nextToken`
 response element. The remaining results of the initial request can be seen by sending
 another `GetLifecyclePolicyPreviewRequest` request with the returned `nextToken`
 value. This value can be between 1 and 100. If this
 parameter is not used, then `GetLifecyclePolicyPreviewRequest` returns up to
 100 results and a `nextToken` value, if
 applicable. This option cannot be used when you specify images with `imageIds`.

## `filter = ["tagStatus" =>  "TAGGED" or "UNTAGGED"]`
An optional parameter that filters results based on image tag status and all tags, if tagged.

# Returns

`GetLifecyclePolicyPreviewResponse`

# Exceptions

`ServerException`, `InvalidParameterException`, `RepositoryNotFoundException` or `LifecyclePolicyPreviewNotFoundException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/GetLifecyclePolicyPreview)
"""
@inline get_lifecycle_policy_preview(aws::AWSConfig=default_aws_config(); args...) = get_lifecycle_policy_preview(aws, args)

@inline get_lifecycle_policy_preview(aws::AWSConfig, args) = AWSCore.Services.ecr(aws, "GetLifecyclePolicyPreview", args)

@inline get_lifecycle_policy_preview(args) = get_lifecycle_policy_preview(default_aws_config(), args)

"""
    using AWSSDK.ECR.get_repository_policy
    get_repository_policy([::AWSConfig], arguments::Dict)
    get_repository_policy([::AWSConfig]; repositoryName=, <keyword arguments>)

    using AWSCore.Services.ecr
    ecr([::AWSConfig], "GetRepositoryPolicy", arguments::Dict)
    ecr([::AWSConfig], "GetRepositoryPolicy", repositoryName=, <keyword arguments>)

# GetRepositoryPolicy Operation

Retrieves the repository policy for a specified repository.

# Arguments

## `registryId = ::String`
The AWS account ID associated with the registry that contains the repository. If you do not specify a registry, the default registry is assumed.

## `repositoryName = ::String` -- *Required*
The name of the repository with the policy to retrieve.

# Returns

`GetRepositoryPolicyResponse`

# Exceptions

`ServerException`, `InvalidParameterException`, `RepositoryNotFoundException` or `RepositoryPolicyNotFoundException`.

# Example: To get the current policy for a repository

This example obtains the repository policy for the repository named ubuntu.

Input:
```
[
    "repositoryName" => "ubuntu"
]
```

Output:
```
Dict(
    "policyText" => "{
  "Version" : "2008-10-17",
  "Statement" : [ {
    "Sid" : "new statement",
    "Effect" : "Allow",
    "Principal" : {
     "AWS" : "arn:aws:iam::012345678901:role/CodeDeployDemo"
    },
"Action" : [ "ecr:GetDownloadUrlForLayer", "ecr:BatchGetImage", "ecr:BatchCheckLayerAvailability" ]
 } ]
}",
    "registryId" => "012345678901",
    "repositoryName" => "ubuntu"
)
```

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/GetRepositoryPolicy)
"""
@inline get_repository_policy(aws::AWSConfig=default_aws_config(); args...) = get_repository_policy(aws, args)

@inline get_repository_policy(aws::AWSConfig, args) = AWSCore.Services.ecr(aws, "GetRepositoryPolicy", args)

@inline get_repository_policy(args) = get_repository_policy(default_aws_config(), args)

"""
    using AWSSDK.ECR.initiate_layer_upload
    initiate_layer_upload([::AWSConfig], arguments::Dict)
    initiate_layer_upload([::AWSConfig]; repositoryName=, <keyword arguments>)

    using AWSCore.Services.ecr
    ecr([::AWSConfig], "InitiateLayerUpload", arguments::Dict)
    ecr([::AWSConfig], "InitiateLayerUpload", repositoryName=, <keyword arguments>)

# InitiateLayerUpload Operation

Notify Amazon ECR that you intend to upload an image layer.

**Note**
> This operation is used by the Amazon ECR proxy, and it is not intended for general use by customers for pulling and pushing images. In most cases, you should use the `docker` CLI to pull, tag, and push images.

# Arguments

## `registryId = ::String`
The AWS account ID associated with the registry to which you intend to upload layers. If you do not specify a registry, the default registry is assumed.

## `repositoryName = ::String` -- *Required*
The name of the repository to which you intend to upload layers.

# Returns

`InitiateLayerUploadResponse`

# Exceptions

`ServerException`, `InvalidParameterException` or `RepositoryNotFoundException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/InitiateLayerUpload)
"""
@inline initiate_layer_upload(aws::AWSConfig=default_aws_config(); args...) = initiate_layer_upload(aws, args)

@inline initiate_layer_upload(aws::AWSConfig, args) = AWSCore.Services.ecr(aws, "InitiateLayerUpload", args)

@inline initiate_layer_upload(args) = initiate_layer_upload(default_aws_config(), args)

"""
    using AWSSDK.ECR.list_images
    list_images([::AWSConfig], arguments::Dict)
    list_images([::AWSConfig]; repositoryName=, <keyword arguments>)

    using AWSCore.Services.ecr
    ecr([::AWSConfig], "ListImages", arguments::Dict)
    ecr([::AWSConfig], "ListImages", repositoryName=, <keyword arguments>)

# ListImages Operation

Lists all the image IDs for a given repository.

You can filter images based on whether or not they are tagged by setting the `tagStatus` parameter to `TAGGED` or `UNTAGGED`. For example, you can filter your results to return only `UNTAGGED` images and then pipe that result to a [BatchDeleteImage](@ref) operation to delete them. Or, you can filter your results to return only `TAGGED` images to list all of the tags in your repository.

# Arguments

## `registryId = ::String`
The AWS account ID associated with the registry that contains the repository in which to list images. If you do not specify a registry, the default registry is assumed.

## `repositoryName = ::String` -- *Required*
The repository with image IDs to be listed.

## `nextToken = ::String`
The `nextToken` value returned from a previous paginated `ListImages` request where `maxResults` was used and the results exceeded the value of that parameter. Pagination continues from the end of the previous results that returned the `nextToken` value. This value is `null` when there are no more results to return.

**Note**
> This token should be treated as an opaque identifier that is only used to retrieve the next items in a list and not for other programmatic purposes.

## `maxResults = ::Int`
The maximum number of image results returned by `ListImages` in paginated output. When this parameter is used, `ListImages` only returns `maxResults` results in a single page along with a `nextToken` response element. The remaining results of the initial request can be seen by sending another `ListImages` request with the returned `nextToken` value. This value can be between 1 and 100. If this parameter is not used, then `ListImages` returns up to 100 results and a `nextToken` value, if applicable.

## `filter = ["tagStatus" =>  "TAGGED" or "UNTAGGED"]`
The filter key and value with which to filter your `ListImages` results.

# Returns

`ListImagesResponse`

# Exceptions

`ServerException`, `InvalidParameterException` or `RepositoryNotFoundException`.

# Example: To list all images in a repository

This example lists all of the images in the repository named ubuntu in the default registry in the current account.

Input:
```
[
    "repositoryName" => "ubuntu"
]
```

Output:
```
Dict(
    "imageIds" => [
        Dict(
            "imageDigest" => "sha256:764f63476bdff6d83a09ba2a818f0d35757063724a9ac3ba5019c56f74ebf42a",
            "imageTag" => "precise"
        )
    ]
)
```

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/ListImages)
"""
@inline list_images(aws::AWSConfig=default_aws_config(); args...) = list_images(aws, args)

@inline list_images(aws::AWSConfig, args) = AWSCore.Services.ecr(aws, "ListImages", args)

@inline list_images(args) = list_images(default_aws_config(), args)

"""
    using AWSSDK.ECR.put_image
    put_image([::AWSConfig], arguments::Dict)
    put_image([::AWSConfig]; repositoryName=, imageManifest=, <keyword arguments>)

    using AWSCore.Services.ecr
    ecr([::AWSConfig], "PutImage", arguments::Dict)
    ecr([::AWSConfig], "PutImage", repositoryName=, imageManifest=, <keyword arguments>)

# PutImage Operation

Creates or updates the image manifest and tags associated with an image.

**Note**
> This operation is used by the Amazon ECR proxy, and it is not intended for general use by customers for pulling and pushing images. In most cases, you should use the `docker` CLI to pull, tag, and push images.

# Arguments

## `registryId = ::String`
The AWS account ID associated with the registry that contains the repository in which to put the image. If you do not specify a registry, the default registry is assumed.

## `repositoryName = ::String` -- *Required*
The name of the repository in which to put the image.

## `imageManifest = ::String` -- *Required*
The image manifest corresponding to the image to be uploaded.

## `imageTag = ::String`
The tag to associate with the image. This parameter is required for images that use the Docker Image Manifest V2 Schema 2 or OCI formats.

# Returns

`PutImageResponse`

# Exceptions

`ServerException`, `InvalidParameterException`, `RepositoryNotFoundException`, `ImageAlreadyExistsException`, `LayersNotFoundException` or `LimitExceededException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/PutImage)
"""
@inline put_image(aws::AWSConfig=default_aws_config(); args...) = put_image(aws, args)

@inline put_image(aws::AWSConfig, args) = AWSCore.Services.ecr(aws, "PutImage", args)

@inline put_image(args) = put_image(default_aws_config(), args)

"""
    using AWSSDK.ECR.put_lifecycle_policy
    put_lifecycle_policy([::AWSConfig], arguments::Dict)
    put_lifecycle_policy([::AWSConfig]; repositoryName=, lifecyclePolicyText=, <keyword arguments>)

    using AWSCore.Services.ecr
    ecr([::AWSConfig], "PutLifecyclePolicy", arguments::Dict)
    ecr([::AWSConfig], "PutLifecyclePolicy", repositoryName=, lifecyclePolicyText=, <keyword arguments>)

# PutLifecyclePolicy Operation

Creates or updates a lifecycle policy. For information about lifecycle policy syntax, see [Lifecycle Policy Template](http://docs.aws.amazon.com/AmazonECR/latest/userguide/LifecyclePolicies.html).

# Arguments

## `registryId = ::String`
The AWS account ID associated with the registry that contains the repository. If you do
 not specify a registry, the default registry is assumed.

## `repositoryName = ::String` -- *Required*
The name of the repository to receive the policy.

## `lifecyclePolicyText = ::String` -- *Required*
The JSON repository policy text to apply to the repository.

# Returns

`PutLifecyclePolicyResponse`

# Exceptions

`ServerException`, `InvalidParameterException` or `RepositoryNotFoundException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/PutLifecyclePolicy)
"""
@inline put_lifecycle_policy(aws::AWSConfig=default_aws_config(); args...) = put_lifecycle_policy(aws, args)

@inline put_lifecycle_policy(aws::AWSConfig, args) = AWSCore.Services.ecr(aws, "PutLifecyclePolicy", args)

@inline put_lifecycle_policy(args) = put_lifecycle_policy(default_aws_config(), args)

"""
    using AWSSDK.ECR.set_repository_policy
    set_repository_policy([::AWSConfig], arguments::Dict)
    set_repository_policy([::AWSConfig]; repositoryName=, policyText=, <keyword arguments>)

    using AWSCore.Services.ecr
    ecr([::AWSConfig], "SetRepositoryPolicy", arguments::Dict)
    ecr([::AWSConfig], "SetRepositoryPolicy", repositoryName=, policyText=, <keyword arguments>)

# SetRepositoryPolicy Operation

Applies a repository policy on a specified repository to control access permissions.

# Arguments

## `registryId = ::String`
The AWS account ID associated with the registry that contains the repository. If you do not specify a registry, the default registry is assumed.

## `repositoryName = ::String` -- *Required*
The name of the repository to receive the policy.

## `policyText = ::String` -- *Required*
The JSON repository policy text to apply to the repository.

## `force = ::Bool`
If the policy you are attempting to set on a repository policy would prevent you from setting another policy in the future, you must force the [SetRepositoryPolicy](@ref) operation. This is intended to prevent accidental repository lock outs.

# Returns

`SetRepositoryPolicyResponse`

# Exceptions

`ServerException`, `InvalidParameterException` or `RepositoryNotFoundException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/SetRepositoryPolicy)
"""
@inline set_repository_policy(aws::AWSConfig=default_aws_config(); args...) = set_repository_policy(aws, args)

@inline set_repository_policy(aws::AWSConfig, args) = AWSCore.Services.ecr(aws, "SetRepositoryPolicy", args)

@inline set_repository_policy(args) = set_repository_policy(default_aws_config(), args)

"""
    using AWSSDK.ECR.start_lifecycle_policy_preview
    start_lifecycle_policy_preview([::AWSConfig], arguments::Dict)
    start_lifecycle_policy_preview([::AWSConfig]; repositoryName=, <keyword arguments>)

    using AWSCore.Services.ecr
    ecr([::AWSConfig], "StartLifecyclePolicyPreview", arguments::Dict)
    ecr([::AWSConfig], "StartLifecyclePolicyPreview", repositoryName=, <keyword arguments>)

# StartLifecyclePolicyPreview Operation

Starts a preview of the specified lifecycle policy. This allows you to see the results before creating the lifecycle policy.

# Arguments

## `registryId = ::String`
The AWS account ID associated with the registry that contains the repository. If you do not specify a registry, the default registry is assumed.

## `repositoryName = ::String` -- *Required*
The name of the repository to be evaluated.

## `lifecyclePolicyText = ::String`
The policy to be evaluated against. If you do not specify a policy, the current policy for the repository is used.

# Returns

`StartLifecyclePolicyPreviewResponse`

# Exceptions

`ServerException`, `InvalidParameterException`, `RepositoryNotFoundException`, `LifecyclePolicyNotFoundException` or `LifecyclePolicyPreviewInProgressException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/StartLifecyclePolicyPreview)
"""
@inline start_lifecycle_policy_preview(aws::AWSConfig=default_aws_config(); args...) = start_lifecycle_policy_preview(aws, args)

@inline start_lifecycle_policy_preview(aws::AWSConfig, args) = AWSCore.Services.ecr(aws, "StartLifecyclePolicyPreview", args)

@inline start_lifecycle_policy_preview(args) = start_lifecycle_policy_preview(default_aws_config(), args)

"""
    using AWSSDK.ECR.upload_layer_part
    upload_layer_part([::AWSConfig], arguments::Dict)
    upload_layer_part([::AWSConfig]; repositoryName=, uploadId=, partFirstByte=, partLastByte=, layerPartBlob=, <keyword arguments>)

    using AWSCore.Services.ecr
    ecr([::AWSConfig], "UploadLayerPart", arguments::Dict)
    ecr([::AWSConfig], "UploadLayerPart", repositoryName=, uploadId=, partFirstByte=, partLastByte=, layerPartBlob=, <keyword arguments>)

# UploadLayerPart Operation

Uploads an image layer part to Amazon ECR.

**Note**
> This operation is used by the Amazon ECR proxy, and it is not intended for general use by customers for pulling and pushing images. In most cases, you should use the `docker` CLI to pull, tag, and push images.

# Arguments

## `registryId = ::String`
The AWS account ID associated with the registry to which you are uploading layer parts. If you do not specify a registry, the default registry is assumed.

## `repositoryName = ::String` -- *Required*
The name of the repository to which you are uploading layer parts.

## `uploadId = ::String` -- *Required*
The upload ID from a previous [InitiateLayerUpload](@ref) operation to associate with the layer part upload.

## `partFirstByte = ::Int` -- *Required*
The integer value of the first byte of the layer part.

## `partLastByte = ::Int` -- *Required*
The integer value of the last byte of the layer part.

## `layerPartBlob = blob` -- *Required*
The base64-encoded layer part payload.

# Returns

`UploadLayerPartResponse`

# Exceptions

`ServerException`, `InvalidParameterException`, `InvalidLayerPartException`, `RepositoryNotFoundException`, `UploadNotFoundException` or `LimitExceededException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/UploadLayerPart)
"""
@inline upload_layer_part(aws::AWSConfig=default_aws_config(); args...) = upload_layer_part(aws, args)

@inline upload_layer_part(aws::AWSConfig, args) = AWSCore.Services.ecr(aws, "UploadLayerPart", args)

@inline upload_layer_part(args) = upload_layer_part(default_aws_config(), args)

end # module ECR

#==============================================================================#
# End of file
#==============================================================================#


text:
module MemoryMutate
  using StaticArrays

  # TODO:
  #  undefined symbols weird error
  #    changed `throw` to `error`
  #    but this issue might be raised by `@assert`
  #      it occurs instead of an `undefined variable` error, which is hidden there
  #    have this, when using `@ptr oc->pb` where `@ptr oc[]->pb` would be correct
  # `@ptr r` where r is a Ref, returns a pointer to `Base.RefValue` of something. we might just want a pointer to something
  # we could, for non-bitstypes, allow `@mem a = x` to implement `unsafe_store!(Ptr{A}(pointer_from_objref(a)), x)`
  # Another reason to support this is, that when a C function is given a pointer (e.g. because it takes it's argument by reference) which resides inside of an allocated bitstype struct …
  #   put in another way: when we want to pass a C-reference/pointer which refers to a part of an (immutable) bitstype, this has to be a pointer for Julia
  #   because casting it to a RefValue would require type (tag) information at that memory location, which is not present (i.e. you cannot "cast" a Ptr{T} into a RefValue{T})
  #     there is unsafe_pointer_to_objref, but this requires (!) the type (tag) information to be present, so (in best case) this will segfault
  # hand-in-hand might also goes the GC-preserving of such @ptr statements
  #   so we might want to have something like
  #     ptr = @ptr base a.b.c.d.e
  #     @GC.preserve base f(ptr)
  #   the idea is, that @ptr stores the rightmost "Julia-GC-allocated" Julia-object into `base` that we can use then to preserve in `@GC.preserve`
  #     currently we need to manually guarantee that the pointers "lifetime" extends for the statement using it
  # for the next version we might change some of the internals to better handle the cases where fieldoffsets cannot be statically determined
  #   Nash 2016 - Inference Convergence Algorithm in Julia
  #     https://juliacomputing.com/blog/2016/04/04/inference-convergence.html
  #   Optionally-generated functions
  #     https://docs.julialang.org/en/v1/manual/metaprogramming/index.html#Optionally-generated-functions-1
  #   we also might want to "fuse" all such fieldknowledge-collecting functions using
  #     for (n,fA) in enumerate(fieldnames(T))
  #       push!(exprs, :( f == $(QuoteNode(fA)) && ($sym_idx = … something where f ≡ fA … ) ))
  #     end
  #   into a single one
  #   in order to do so, we could use a naming scheme for the generated variables of the different "levels", using `parentN` and `childN` for more clarity
  #   Furthermore, instead of generating nested if-blocks, we might just propagate locally the most "current" arguments, having a single `unsafe_store_generated2` call at the end
  #     is this an issue somehow for constant resolving?
  #   Currently, we avoid to pass around types in variables.
  #     If the compiler can create compiler-constants out of these, it might be a more elegant approch.
  #     But to obtain type properties, e.g. offsets, in a statical way, it was necessary to generate a case for every field.
  # the GMSH API shows how to pass allocated memory to Julia (does it?)
  #    function getElements(dim = -1, tag = -1)
  #      api_elementTypes_ = Ref{Ptr{Cint}}()
  #      api_elementTypes_n_ = Ref{Csize_t}()
  #      api_elementTags_ = Ref{Ptr{Ptr{Cint}}}()
  #      …
  #      ierr = Ref{Cint}()
  #      ccall((:gmshModelMeshGetElements, gmsh.lib), Nothing,
  #            (Ptr{Ptr{Cint}}, Ptr{Csize_t}, Ptr{Ptr{Ptr{Cint}}}, Ptr{Ptr{Csize_t}}, Ptr{Csize_t}, Ptr{Ptr{Ptr{Cint}}}, Ptr{Ptr{Csize_t}}, Ptr{Csize_t}, Cint, Cint, Ptr{Cint}),
  #            api_elementTypes_, api_elementTypes_n_, api_elementTags_, api_elementTags_n_, api_elementTags_nn_, api_nodeTags_, api_nodeTags_n_, api_nodeTags_nn_, dim, tag, ierr)
  #      …
  #      elementTypes = unsafe_wrap(Array, api_elementTypes_[], api_elementTypes_n_[], own=true)
  #      tmp_api_elementTags_ = unsafe_wrap(Array, api_elementTags_[], api_elementTags_nn_[], own=true)
  #      tmp_api_elementTags_n_ = unsafe_wrap(Array, api_elementTags_n_[], api_elementTags_nn_[], own=true)
  #      …
  #      return elementTypes, elementTags, nodeTags
  #    end
  # the CImGui.jl (C/C++) wrapper has an @c macro which seems to perform what we do with @ptr
  #   @c CImGui.ShowDemoWindow(&show_demo_window)
  #  i.e.
  #    @macroexpand @c f(&x)
  #      cref     = Ref(x)
  #      cref_ret = f(cref)
  #      x        = cref[]
  #      cref_ret
  #  it is also using
  #   @cstatic f=Cfloat(0.0) counter=Cint(0) begin … @c CImGui.SliderFloat("float", &f, 0, 1) … end
  #  i.e.
  #   @macroexpand @cstatic f=Cfloat(0.0) begin; foo; end
  #   let global static_f
  #       local f = static_f
  #      begin
  #        foo
  #      end
  #      static_f = f
  #      f
  #    end

  export @mem
  export @yolo
  export @ptr
  export @voidptr
  export @typedptr

  # TODO: may fuse fieldindex_generated, fieldisbitstype_generated and fieldisimmutable_generated into a single function
  # TODO: spread a few assertions with descriptive error messages

  # RPtr: this Ptr-wrapper is to distinguish values that we treat lazily by their pointers (RPtr) from real pointer values (Ptr)
  #   e.g. if we are given a pointer to some struct and want to access some field of it, then one can avoid to unsafe_load the whole struct and refer to that value by just adding the field's offset to the given pointer (and reinterpret that pointer)
  #   what comes out of that procedure is an RPtr{T} then: it should behave like T for all our purposes, but we alerady know that T's memory location
  struct RPtr{T}; x::Ptr{T}; end
  Base.show(io::IO,ptr::RPtr{T}) where T = print(io,"R"*repr(ptr.x))

  # fieldindex_generated: obtain a field's index from its Symbol. this is due to `fieldoffset` requiring an integer instead of a symbol
  #  statically unrolls the cases for all fieldnames of T
  (fieldindex_generated(::          T  , f::Symbol)::UInt64) where T = fieldindex_generated(T,f)
  (fieldindex_generated(::Type{RPtr{T}}, f::Symbol)::UInt64) where T = fieldindex_generated(T,f)
  @generated function (fieldindex_generated(::Type{T}, f::Symbol)::UInt64) where T
    # inside the generated function macro, parameters and their type are the same, e.g. v == T
    if T <: Ptr; T = T.parameters[1]; end
    sym_idx = gensym()
    exprs = [:( $sym_idx = UInt64(0) )]
    for (n,fA) in enumerate(fieldnames(T))
      push!(exprs, :( f == $(QuoteNode(fA)) && ($sym_idx = UInt64($n)) ))
    end
    push!(exprs, :( return $sym_idx ))
    return Expr(:block,exprs...)
  end # generates a Core.Compiler.Const((:x, :b), false) and @code_warntype is okay with that

  isbitstypeandnotapointer(T::Type) = isbitstype(T) && ~(T <: Ptr)

  # fieldisbitstype_generated: obtains whether a field is a bitstype from its name
  #  statically unrolls the cases for all fieldnames of T
  fieldisbitstype_generated(::          T  ,f::Symbol) where T = fieldisbitstype_generated(T,f)
  fieldisbitstype_generated(::Type{RPtr{T}},f::Symbol) where T = fieldisbitstype_generated(T,f)
  @generated function fieldisbitstype_generated(::Type{T},f::Symbol) where T
    # inside the generated function macro, parameters and their type are the same, e.g. v == T
    sym_bit = gensym()
    exprs = [:( $sym_bit = false )]
    for (n,fA) in enumerate(fieldnames(T))
      push!(exprs, :( f == $(QuoteNode(fA)) && ($sym_bit = isbitstypeandnotapointer($(fieldtypes(T)[n]))) ))
    end
    push!(exprs, :( return $sym_bit ))
    return Expr(:block,exprs...)
  end

  # fieldisimmutable_generated: obtain wheter a field is immutable from its name
  #  statically unrolls the cases for all fieldnames of T
  fieldisimmutable_generated(::          T  ,f::Symbol) where T = fieldisimmutable_generated(T,f)
  fieldisimmutable_generated(::Type{RPtr{T}},f::Symbol) where T = fieldisimmutable_generated(T,f)
  @generated function fieldisimmutable_generated(::Type{T},f::Symbol) where T
    # inside the generated function macro, parameters and their type are the same, e.g. v == T
    sym_imm = gensym()
    exprs = [:( $sym_imm = false )]
    for (n,fA) in enumerate(fieldnames(T))
      push!(exprs, :( f == $(QuoteNode(fA)) && ($sym_imm = $(!fieldtypes(T)[n].mutable)) ))
    end
    push!(exprs, :( return $sym_imm ))
    return Expr(:block,exprs...)
  end

  # static variants suffice to obtain properties of value's types
  #   in the case where we are dereferencing with :getindex
  #   this is used to determine the `bit` value
  #   which means:
  #     "if there is a `fld` symbol, then this is a Bool, whether the type of `val`.`fld` is a bitstype"
  #   it is used to determine which `unsafe_store_generated2` statement to use:
  #     "is the current level's value of bitstype?                             "
  #     "  then: use one of the previous level's as a base pointer             " = keep the base (in another interpretation)
  #     "  else: use unsafe_store! on pointer_from_objref fot the current level" = advance the base (in another interpretation)
  #   so, returning true here, will keep the old base
  #   and returning false here, will advance the base to this level
  # TODO: the `bit` value for each level seems to be used to determine whether or not `val`.`fld` qualifies as a new base(-pointer)
  #   our variable naming should reflect that purpose
  refisbitstype_static(x::    RPtr{T}    ) where    T      = refisbitstype_static(x.x) # behave like T
  refisbitstype_static( ::     Ptr{T}    ) where    T      = isbitstype(T) # can advance to the next level, if T wants so
  refisbitstype_static( ::     Ref{T}    ) where    T      = isbitstype(T) # can advance to the next level, if T wants so
  refisbitstype_static( :: Ref{Ptr{T}}   ) where    T      = false # can advance the base to the next level in any way
  refisbitstype_static( :: Ref{Ref{T}}   ) where    T      = false # can advance the base to the next level in any way
  refisbitstype_static( ::NTuple{N,T}    ) where {N,T}     = isbitstype(T) # can advance to the next level, if T wants so
  refisbitstype_static( ::SArray{E,T,N,M}) where {E,T,N,M} = isbitstype(T) # can advance to the next level, if T wants so
  # fieldisbitstype_static(::T, f::Symbol) where T = isbitstype(fieldtype(T,f))
  ismutable_static(::T) where T = T.mutable
  isreference_static(::T) where T = T <: Ref # note, that we have Ptr <: Ref
  (fieldoffset_static(::         T  , i::UInt64)::Int64) where T = fieldoffset(T,i)
  (fieldoffset_static(::RPtr{    T }, i::UInt64)::Int64) where T = fieldoffset(T,i)
  (fieldoffset_static(:: Ptr{    T }, i::UInt64)::Int64) where T = fieldoffset(T,i)
  (fieldoffset_static(::RPtr{Ptr{T}}, i::UInt64)::Int64) where T = fieldoffset(T,i)

  # some convenience  utilities
  pointer_or_pointer_from_objref(x::         T  ) where T = pointer_from_objref(x)
  pointer_or_pointer_from_objref(x::     Ptr{T} ) where T = x
  pointer_or_pointer_from_objref(x::RPtr{    T }) where T = x.x
  pointer_or_pointer_from_objref(x::RPtr{Ptr{T}}) where T = unsafe_load(x.x) # lazily dereference a RPtr
  pointer_from_objref_typed(x :: T) where T = reinterpret(Ptr{T},pointer_from_objref(x))
  function cumprod_SimpleVector(v :: Core.SimpleVector)
    c = [1]
    for n = 1:length(v)-1
      push!(c,c[end]*v[n])
    end
    return c
  end
  structinfo(T) = [(fieldoffset(T,i), fieldname(T,i), fieldtype(T,i)) for i = 1:getfieldcount(T)];
  # derefPtrPtr(x::T) where T = x
  # derefPtrPtr(x::Ptr{Ptr{T}}) where T = unsafe_load(x)
  # derefRPtr(x::T) where T = x
  # derefRPtr(x::RPtr{Ptr{T}}) where T = unsafe_load(x.x)
  PtrOrNothing(x::    T ) where T = nothing
  PtrOrNothing(x::Ptr{T}) where T = Ptr{Nothing}(x)

  # unsafe_store_generated2:
  #  from the collected information, an `unsafe_store`-statement is set up to perform the operation
  #  this function generates just a single statement, or a statement for every field in `T` ("unrolling" T's fields)
  #    NOTE: the unrolling was necessary 'just' for a correct pointer-type in `unsafe_store` that can be optimized out
  #  this is the final call
  # arguments:
  #        :: T      = the parent, containing the field to be set and it's memory, (but it might be a bitstype, so unaccessable for `pointer_from_objref`)
  #   f    :: S      = the field's Symbol or Nothing, when an index was used
  #                    TODO: this is redundant: the offset was already calculated before; we just need this here again, to provide the right type for Ptr in `unsafe_store!`
  #                          this kind of redundancies occurs to avoid, passing around types in variables ... which could also be a possible approach
  #   base :: B      = the rightmost non-bitstype in an access sequence, which also contains the field's memory to be set, but might not be the immediate parent of that field (i.e. where `pointer_from_objref` could be used)
  #                    nested if-statements are generated to provide this
  #   off  :: Int64  = the offset relative to a pointer to `base`, where the target field resides
  #   rhs  :: R      = the right hand side to be assigned
  #   ptr  :: P      = a Ptr{Nothing}, tracked for `base` in cases where `pointer_from_objref` cannot be used (i.e. `base` is immutable)
  #                    this `ptr` has the offset already added
  #                    TODO: if we could track the pointer's type from the outside, the unrolling could be saved (we already do so with `val` if it becomes a pointer)
  # flags:
  #   mode
  #   accumulatePointers
  #   writeReferences
  #   reallocateImmutableRHSpointer
  @inline @generated function unsafe_store_generated2(::Val{mode},::T,f::S,base::B,off::Int64,rhs::R,ptr::P,::Val{accumulatePointers},::Val{writeReferences},::Val{reallocateImmutableRHSpointer}) where {mode,T,S<:Union{Symbol,Nothing},B,R,P<:Union{Ptr,Nothing},accumulatePointers,writeReferences,reallocateImmutableRHSpointer}
    U = T <: RPtr ? T.parameters[1] : T
    exprs = []
    # exprs = [:( println("T = $($T)"); println("U = $($U)"); println("B = $($B)"); println("P = $($P)"); println("off = $off"); println("f = $(repr(f))") )]
    sym_tmp = gensym("tmp")
    fnames = []
    ftypes = []
    unrolling = true
    if U <: SArray || U <: NTuple
      unrolling = false
      fnames = [:nothing]
      ftypes = [U.parameters[2]]
    elseif U <: Ptr
      V = U.parameters[1]
      # push!(exprs,:( println("V = $($V)") ))
      # unrolling = false
      # fnames = [:nothing]
      # ftypes = [V]
      # if S <: Nothing # write directly to the pointer
      #   unrolling = false
      #   fnames = [:nothing]
      #   ftypes = [V]
      if V <: SArray || V <: NTuple
        unrolling = false
        fnames = [:nothing]
        ftypes = [V.parameters[2]]
      elseif S <: Nothing # unrolling would create statements of the form `nothing == ...` which have no effect
        unrolling = false
        fnames = [:nothing]
        ftypes = [V]
      else
        unrolling = true
        fnames = fieldnames(V)
        ftypes = fieldtypes(V)
      end
    else
      unrolling = true
      fnames = fieldnames(U)
      ftypes = fieldtypes(U)
    end
    for (n,(fname,ftype)) in enumerate(zip(fnames,ftypes))
      action = :()
      if B.mutable || B <: RPtr || B <: Ptr # B is mutable, so `pointer_from_objref(base)` is valid
        if isbitstype(ftype) # write a bitstype into the memory of `base`
          mode == :assignment && ( action = :( @GC.preserve base unsafe_store!(reinterpret(Ptr{$(ftype)},pointer_or_pointer_from_objref(base)+off),rhs) ) )
          mode == :pointer    && ( action = :(                                 reinterpret(Ptr{$(ftype)},pointer_or_pointer_from_objref(base)+off)      ) )
        elseif writeReferences # write a reference into the memory of `base`
          # if the field we want to write to is not a bitstype, then we end up with it's parent as the `base`
          # in that case, we are at the second-to-last level, where the normal assignment can be used
          # so we do not see this case
          mode == :assignment && (
            action = :( @assert isa(rhs,$(ftype)) "The value to assign is of type '$($(R))', but the field to assign it to is of type '$($(ftype))'";
              @GC.preserve base unsafe_store!(reinterpret(Ptr{Ptr{Nothing}},pointer_or_pointer_from_objref(base)+off),pointer_from_objref(rhs)) ) )
          mode == :pointer    && ( action = :(reinterpret(Ptr{Ptr{Nothing}},pointer_or_pointer_from_objref(base)+off)                           ) )
        else
          # we do not see this case either
          action = :(error("""
            From type '$($T)' the field '$($(QuoteNode(fname)))' is of type '$($ftype)' and it is located in type '$($(B))' at offset $(off).
            The field type '$($ftype)' is a non-bitstype. You need to set 'writeReferences = true' to enable this operation.
            """))
        end
      elseif accumulatePointers # B is immutable, so we cannot use `pointer_from_objref(base)` and use an accumulated pointer instead
        if P != Nothing # if we have such pointer, we can use it
          if isbitstype(ftype) # write a bitstype into the memory at `ptr`
            mode == :assignment && ( action = :( @GC.preserve base unsafe_store!(reinterpret(Ptr{$(ftype)},ptr),rhs) ) )
            mode == :pointer    && ( action = :(                                 reinterpret(Ptr{$(ftype)},ptr)      ) )
          elseif writeReferences # write a reference into the memory at `ptr`
            if ftype.mutable # `rhs` is already allocated and `pointer_from_objref(rhs)` is valid
              mode == :assignment && (
                action = :( @assert isa(rhs,$(ftype)) "The value to assign is of type '$($(R))', but the field to assign it to is of type '$($(ftype))'";
                  @GC.preserve base unsafe_store!(reinterpret(Ptr{Ptr{Nothing}},ptr),pointer_from_objref(rhs)) ) )
              mode == :pointer    && ( action = :(reinterpret(Ptr{Ptr{Nothing}},ptr) ) )
            elseif reallocateImmutableRHSpointer # we need to allocate `rhs` with `Ref`
              # TODO: can use just `Ptr{Ptr{Nothing}}(pointer_from_objref($sym_tmp))`
              mode == :assignment && (
                action = :( @assert isa(rhs,$(ftype)) "The value to assign is of type '$($(R))', but the field to assign it to is of type '$($(ftype))'";
                  $sym_tmp = Ref(rhs);
                  @GC.preserve base $sym_tmp unsafe_store!(reinterpret(Ptr{Ptr{Nothing}},ptr),unsafe_load(reinterpret(Ptr{Ptr{Nothing}},pointer_from_objref($sym_tmp)))) ) )
              mode == :pointer && ( action = :(            reinterpret(Ptr{Ptr{Nothing}},ptr)                                                                            ) )
            else
              action = :(error("""
              The given right hand side is of type '$($(R))' which is an immutable non-isbitstype.
              You need to set 'reallocateImmutableRHSpointer = true' to enable this operation.
              """))
            end
          else
            action = :(error("""
            From type '$($T)' the field '$($(QuoteNode(fname)))' is of type '$($(ftype))' and it is located in type '$($(B))' at offset $(off).
            This is a non-bitstype. You need to set 'writeReferences = true' to enable this operation.
            """))
          end
        else
          action = :(error("""There is neither a reference, nor a mutable present in the assigment to be used as a base pointer."""))
        end
      else
        action = :(error("""
          From type '$($T)' the field '$($(QuoteNode(fname)))' is of type '$($(ftype))' and it is located in type '$($(B))' at offset $(off).
          The base type '$($(B))' is immutable. You need to set 'accumulatePointers = true' to enable this operation.
          """))
      end
      # push!(exprs,:( println( $(repr(action)) ) ))
      if unrolling
        push!(exprs,:( f == $(QuoteNode(fname)) && return ($action) ))
      else
        push!(exprs,:( $action )) # NOTE: this is set up to be just executed once (so no "unrolling" in this case)
      end
    end
    # push!(exprs,:( error("the field '$($(QuoteNode(fname)))' was not found in type '$($(T))'") ))
    return Expr(:block,exprs...)
    # return :(println($exprs))
  end
  # fallback for assigning references
  @inline #= TODO: check these ones! =# unsafe_store_generated2(::Val{:assignment}, ::Ref,::Nothing,base::     B ,off::Int64,rhs,_,_,_) where    B  = (base[] = rhs) # @assert isa(base,Ref), @assert off == 0
  @inline #= TODO: check these ones! =# unsafe_store_generated2(::Val{:pointer}, ::Ref{T},::Nothing,base::     B ,off::Int64,rhs,_,_,_) where {T,B} = reinterpret(Ptr{T},pointer_from_objref(base)) # @assert isa(base,Ref), @assert off == 0
  @inline #= TODO: check these ones! =# unsafe_store_generated2(::Val{:assignment}, ::Ref,::Nothing,base::RPtr{B},off::Int64,rhs,_,_,_) where    B  = (unsafe_load(base.x)[] = rhs)
  @inline #= TODO: check these ones! =# unsafe_store_generated2(::Val{:pointer}, ::Ref{T},::Nothing,base::RPtr{B},off::Int64,rhs,_,_,_) where {T,B} = reinterpret(Ptr{T},base.x)

  # a fallback for easy cases
  setfield_or_deref(::Val{:assignment},x::    T ,f::Symbol ,rhs) where T = setfield!(x,f,rhs)
  setfield_or_deref(::Val{:assignment},x::    T , ::Nothing,rhs) where T = (x[] = rhs)
  setfield_or_deref(::Val{:pointer   },x::    T ,f::Symbol ,rhs) where T = reinterpret(Ptr{fieldtype(T,f)},pointer_from_objref(x)+fieldoffset(T,fieldindex_generated(x,f))) # TODO: check whether this also optimizes statically
  setfield_or_deref(::Val{:pointer   },x::Ref{T}, ::Nothing,rhs) where T = reinterpret(Ptr{          T   },pointer_from_objref(x)                                         )
  # fieldtype_static(::T, f::Symbol) where T = fieldtype(T,f)

  # indexoffset_static: calculate an offset from indices and a type
  (                    indexoffset_static(::              T      , prev::Int64, indices...)::Int64) where T = indexoffset_static(T, prev, indices...) # wrapper to pass a variable, but we are only interested in types
  (                    indexoffset_static(::Type{RPtr{    T    }}, prev::Int64, indices...)::Int64) where T = indexoffset_static(T,    0, indices...) # behave like T, but discard the previous offset
  (                    indexoffset_static(::Type{         T     }, prev::Int64            )::Int64) where T = Int64(0) # if there is no index, then we are dereferencing (!) and discard the previous offset (TODO: this logic should not belong to here)
  @generated function (indexoffset_static(::Type{SArray{E,T,N,M}}, prev::Int64, indices...)::Int64) where {E,T,N,M} # obtain the offset into a SArray (which is inlined and therefore we add the previous offset)
    sym     = gensym()
    dims    = E.parameters
    strides = MemoryMutate.cumprod_SimpleVector(dims)
    elsize  = T.isbitstype ? sizeof(T) : sizeof(Ptr{Nothing}) # nonbitstypes are stored as "hidden references"/pointers
    index   = Expr(:call,:+,[:( (indices[$i]-1)*($s) ) for (i,s) in enumerate(strides)]...)
    return T.isbitstype ? :( prev + $index * $elsize ) : :( $index * $elsize ) # if T is not a bitstype, then the array is not a bitstype and we will receive a pointer to the beginning of the array so the previous index has to be discarded
  end
  @generated function (indexoffset_static(::Type{NTuple{N,T}}, prev::Int64, indices...) :: Int64) where {N,T} # obtain the offset into a NTuple (which is inlined and therefore we add the previous offset)
    elsize = T.isbitstype ? sizeof(T) : sizeof(Ptr{Nothing}) # nonbitstypes are stored as "hidden references"/pointers
    index  = :( indices[1]-1 )
    return T.isbitstype ? :( prev + $index * $elsize ) : :( $index * $elsize ) # if T is not a bitstype, then the array is not a bitstype and we will receive a pointer to the beginning of the array so the previous index has to be discarded
  end
  (                    indexoffset_static(::Type{Ptr{T}}, prev::Int64            )::Int64) where T = Int64(0) # if there is no index, then we are dereferencing (!) and discard the previous offset (TODO: this logic should not belong to here)
  @generated function (indexoffset_static(::Type{Ptr{T}}, prev::Int64, indices...)::Int64) where {T} # obtain the offset into a Ptr as a C-Style Array (which is not inlined and therefore discard the previous offset)
    # @assert prev == 0
    elsize = T.isbitstype ? sizeof(T) : sizeof(Ptr{Nothing}) # nonbitstypes are stored as "hidden references"/pointers
    index  = :( indices[1]-1 )
    return :( $index * $elsize )
  end

  # fieldpointer_static: obtain the pointer to a field within some datatype
  #   TODO: this seems in some sense redundant now, we have this one and a combination of indexoffset_static + some cases in unsafe_store_generated2
  #         BUT, here we cover the part of Julia-references to be overwritten in unsafe_store_generated2, when accumulatePointers is set
  # NOTE: see `getfieldorpointer`
  @generated function fieldpointer_static(basebase,base::T,off::Int64,prev::Ptr)::Ptr{Nothing} where T
    return (
        T <: Ptr
      ? :(base+off) # if we are already a pointer, then just add the offset and discard the previous offset
      : T.mutable
      ? :( pointer_from_objref(base)+off ) # if we are mutable, the use `pointer_from_objref`, add the offset and discard the previous offset
      : T.isbitstype
      ? :( prev+off ) # if we are a bitstype, then we're inlined, so just add the offset to the previous offset
      : (T <: SArray || T <: NTuple || T <: MArray) # NOTE: this is for non-bitstype (i.e. non-inlined) SArrays/NTuples. TODO: MArray? at least throw an error
      ? :( @GC.preserve basebase unsafe_load(unsafe_load(reinterpret(Ptr{Ptr{Ptr{Nothing}}},prev)))+off ) # expect a pointer-pointer-pointer at the previous offset, dereference that pointer twice, and add the offset to it
        # sizeof(SArray{Tuple{N},SomeNonIsBitsType,1,N}) == 8 ⇒ it seems that SArray of non-isbits types allocates a single, intermediate array (we perform double dereferencing here)
      : :( @GC.preserve basebase unsafe_load(reinterpret(Ptr{Ptr{Nothing}},prev))+off ) # all other non-inlined types: expect a pointer-pointer ad the previous offset, dereference it and add the offset to it
      )
  end
  @generated function fieldpointer_static(basebase,base::T,off::Int64,::Nothing) where T # the case where no previous offset is present
    return ( T <: Ptr
           ? :( base   + off )
           : T <: RPtr
           ? :( base.x + off )
           : T.mutable
           ? :( pointer_from_objref(base) + off )
           : :( nothing )
           )
  end

  # leftBalance:
  #   Julia's -> operator has a right-fixity, where C's -> operator has a left-fixity
  #     e.g.    a -> b -> c
  #     Julia:  a ->(b -> c)
  #     C    : (a -> b)-> c
  #   that is what we are re-ordering here
  # see Tree.agda
  #   :(a.b.c.d->e.f.g.x = v) == :( ((a.b).c).d -> (((e.f).g).x = v) )
  #     true
  #   leftBalance(:( ((a.b).c).d -> (((e.f).g).x = v) ))
  #     :(((((((a.b).c).d->e)).f).g).x = v)
  function leftBalance(expr)
    # println()
    # println(expr)
    # dump(expr)
    if expr isa Expr
      if expr.head == :(->)
        # a -> b seems to (always?) produce an intermediate :block with a preceeding LineNumberNode that we are filtering out
        inner = expr.args[2] isa Expr && expr.args[2].head == :block && expr.args[2].args[1] isa LineNumberNode ? expr.args[2].args[2] : expr.args[2]
        if inner isa Expr
          if inner.head == :(->)
            # println("leftBalance (e ⇒ (f ⇒ g))     = leftBalance ((e ⇒ f) ⇒ g)")
            # a -> b seems to (always?) produce an intermediate :block with a preceeding LineNumberNode that we are filtering out
            innerinner = inner.args[2] isa Expr && inner.args[2].head == :block && inner.args[2].args[1] isa LineNumberNode ? inner.args[2].args[2] : inner.args[2]
            return leftBalance(Expr(:(->), leftBalance(Expr(:(->), expr.args[1], inner.args[1])), innerinner))
          else
            # println("leftBalance (e ⇒ E H [])      = (e ⇒ E H [])")
            #   unimplemented / does not occur
            # println("leftBalance (e ⇒ E H (a ∷ x)) = E H (leftBalance (leftBalance e ⇒ a) ∷ (map leftBalance x))")
            if inner.head == :call
              #   return Expr(inner.head, inner.args[1], leftBalance(Expr(:(->),expr.args[1],inner.args[2])), map(leftBalance,inner.args[3:end])... )
              # println("inner.head        = $(inner.head)")
              # println("inner.args[1]     = $(inner.args[1])")
              # println("expr.args[1]      = $(expr.args[1])")
              # println("inner.args[2]     = $(inner.args[2])")
              # println("inner.args[3:end] = $(inner.args[3:end])")
              # println()
              return Expr(inner.head, inner.args[1],            (Expr(:(->),leftBalance(expr.args[1]),inner.args[2])), map(leftBalance,inner.args[3:end])... )
            # elseif inner.head == :(.)
            #   return Expr(inner.head, inner.args[1], leftBalance(Expr(:(->),expr.args[1],inner.args[2])), map(leftBalance,inner.args[3:end])... )
            else
              # println("inner.head        = $(inner.head)")
              # println("expr.args[1]      = $(expr.args[1])")
              # println("inner.args[1]     = $(inner.args[1])")
              # println("inner.args[3:end] = $(inner.args[2:end])")
              # println()
              return Expr(inner.head,                leftBalance(Expr(:(->),leftBalance(expr.args[1]),inner.args[1])), map(leftBalance,inner.args[2:end])... )
            end
          end
        else
          # println("leftBalance (e ⇒ S)           = e ⇒ S")
          # filter out LineNumberNodes
          inner = expr.args[2] isa Expr && expr.args[2].head == :block && expr.args[2].args[1] isa LineNumberNode ? expr.args[2].args[2] : expr.args[2]
          return Expr(expr.head,expr.args[1],inner)
        end
      else
        # println("leftBalance (E H x)           = E H (map leftBalance x)")
        if expr.head == :call
          return Expr(expr.head, expr.args[1], map(leftBalance,expr.args[2:end])...)
        else
          return Expr(expr.head, map(leftBalance,expr.args)...)
        end
      end
    else
      # println("leftBalance S                 = S")
      return expr
    end
  end

  # getfieldorpointer: obtain a field from a Ptr, RPtr or "regular" (i.e. all others) type
  #   used getfield on "regular" (i.e. all other) types
  #   obtains a fieldpointer, if the base value is a pointer
  #     does not attempt to load the base value, except when the base value is a RPtr{Ptr{T}}
  #     returns an RPtr in that case
  #   statically unrolls the cases for all fieldnames of T
  # TODO: BUG: what happens when T is a non-bitstype? => see `fieldpointer_static`
  @inline                     getfieldorpointer(x::RPtr{    T },f::Symbol,idx::UInt64) where {T} = getfieldorpointer(            x.x ,f,idx) # x.x isa Ptr{T}
  @inline                     getfieldorpointer(x::         T  ,f::Symbol,idx::UInt64) where {T} =          getfield(            x   ,f)
  @inline                     getfieldorpointer(x::RPtr{Ptr{T}},f::Symbol,idx::UInt64) where {T} = getfieldorpointer(unsafe_load(x.x),f,idx) # x.x isa Ptr{Ptr{T}}
  @inline @generated function getfieldorpointer(x::     Ptr{T} ,f::Symbol,idx::UInt64) where {T}
    sym_res = gensym()
    exprs = [:( $sym_res = false )]
    for (n,fA) in enumerate(fieldnames(T))
      # push!(exprs, :( f == $(QuoteNode(fA)) && ($sym_res = $derefPtrPtr(Ptr{$(fieldtypes(T)[n])}(x+$(fieldoffset(T,n))))  ) ))
      push!(exprs, :( f == $(QuoteNode(fA)) && ( $sym_res = RPtr(Ptr{$(fieldtypes(T)[n])}(x+$(fieldoffset(T,n)))) ) ) )
    end
    push!(exprs, :( return $sym_res ))
    return Expr(:block,exprs...)
  end

  # mem_helper: a wrapper around mem_helper1, to catch non-assignment-cases
  function mem_helper(expr, mode :: Symbol = :assignment, accumulatePointers :: Bool = false, writeReferences :: Bool = false, reallocateImmutableRHSpointer :: Bool = false, followPointers :: Bool = false)
    if followPointers
      expr = leftBalance(expr)
    end
    if mode == :assignment && expr.head != :(=)
      action = mem_helper1(expr, :pointer, accumulatePointers, writeReferences, reallocateImmutableRHSpointer, followPointers)
      return :( unsafe_load($action) )
    else
      action = mem_helper1(expr, mode, accumulatePointers, writeReferences, reallocateImmutableRHSpointer, followPointers)
      return action
    end
  end

  # mem_helper1:
  #   this is the main macro, responsible for collecting information of a "multi-level" sequence or "access-chain"
  #      a.b.c.d
  #      a->b->c->d
  #      a[].b[].c[].d[]
  #      a[].b[1].c[2,3].d[4,5,6]
  #      and mixtures
  #    and it calls `unsafe_store_generated2` at the end to perform the assignment (or just return the pointer)
  # accumulatePointers           : because pointer_from_objref is prohibited on immutables, we collect the pointers manually, beginning from the last occuring mutable
  # writeReferences              : when in an immutable (non-isbits) type we want to set a field of non-isbits type, this is represented as a pointer and we update the pointer value then
  # reallocateImmutableRHSpointer: when in an immutable (non-isbits) type we want to set a field of immutable non-isbits type, again this is a pointer, but also pointer_from_objref is now prohibited on this right-hand-side.
  #                                We use unsafe_load(reinterpret(Ptr{Ptr{Nothing}},Ref(rhs))) to obtain a pointer in that case.
  function mem_helper1(expr, mode :: Symbol = :assignment, accumulatePointers :: Bool = false, writeReferences :: Bool = false, reallocateImmutableRHSpointer :: Bool = false, followPointers :: Bool = false)
    if mode == :assignment
      @assert expr.head == :(=) "Expression for mutating must be an assignment (=)." # unless we use a->b->c, but this was already catched by `mem_helper` which is calling us
      RHS = expr.args[2]
      cur = expr.args[1]
    end
    if mode == :pointer
      RHS = nothing
      cur = expr
    end

    # level[n] contains an expression for the current level of access, most likely just a symbol but could be an arbitrary expression as well, or `nothing` if the dereference `[]` occured
    levels = []
    cases  = []
    while isa(cur,Expr)
      if cur.head == :(.)
        pushfirst!(levels,cur.args[2])
        pushfirst!(cases,:getfield)
        cur = cur.args[1]
      elseif cur.head == :call && cur.args[1] == :getfield # getproperty … is what is called by .
        pushfirst!(levels,cur.args[3])
        pushfirst!(cases,:getfield)
        cur = cur.args[2]
      elseif cur.head == :ref
        pushfirst!(cases,:getindex)
        pushfirst!(levels,cur.args[2:end])
        cur = cur.args[1]
      elseif followPointers && cur isa Expr && cur.head == :(->)
        pushfirst!(cases,:getfield)
        # e.g. in a.b->c.d we have, that a and c aren't QuoteNodes anymore, which is okay for a, but noch for c
        if cur.args[2] isa Symbol; pushfirst!(levels,QuoteNode(cur.args[2]))
        else                     ; pushfirst!(levels,          cur.args[2] )
        end
        cur = cur.args[1] # LineNumberNodes are already filtered out by `leftBalance` in the calling `mem_helper`
      else
        break
      end
    end
    pushfirst!(levels,cur)
    pushfirst!(cases,:getfield)
    length(levels) <= 1 && return mode == :pointer ? esc(:($pointer_from_objref_typed($expr))) : esc(expr)
    # println(collect(zip(levels,cases)))

    # for each level, generate symbols to hold
    # TODO: name these `parent` and `child` for more clarity
    sym_val = [] # every level produces the parent-value for the next level
    sym_ptr = [] # the cumulated pointer from the first mutable/reference
    # sym_typ = [] # the type of `val`. It turned out, that it's necessary to use generic or generated functions to obtain compiler constants for the type's properties instead of using the type directly. That is why we introduce `fld`, `idx`, `bit`, `ref` and `mut`.
    sym_ref = [] # a Bool, whether the type of `val` is a reference type
    sym_mut = [] # a Bool, whether the type of `val` is mutable
    sym_fld = [] # this is a symbol (the "value" of the next level's "expression", `level[n+1]`), to be used to produce the `val` of the next level or `nothing` to signal to apply dereferencing `[]`
    sym_idx = [] # if there is a `fld` symbol, then this is it's numerical index fieldnames-table of the type of `val`. `idx` is zero if the symbol does not occur as a field
    sym_bit = [] # if there is a `fld` symbol, then this is a Bool, whether the type of `val`.`fld` is a bitstype
    sym_off = [] # if there is a `fld` symbol, then this is a UInt64 which accumulates the offsets of previous levels and "resets" every time we hit a non-bitstype
    for m = length(levels):-1:1
      pushfirst!(sym_val,gensym("val$(m)"))
      pushfirst!(sym_ptr,gensym("ptr$(m)"))
      # pushfirst!(sym_typ,gensym("typ$(m)"))
      pushfirst!(sym_fld,gensym("fld$(m)"))
      pushfirst!(sym_idx,gensym("idx$(m)"))
      pushfirst!(sym_bit,gensym("bit$(m)"))
      pushfirst!(sym_ref,gensym("ref$(m)"))
      pushfirst!(sym_mut,gensym("mut$(m)"))
      pushfirst!(sym_off,gensym("off$(m)"))
    end

    # setting up the expressions to hold compiler constants: for the first level
    expr_val = [:( $(sym_val[1]) = $(levels[1])                       )]
    expr_mut = [:( $(sym_mut[1]) = $ismutable_static($(sym_val[1]))   )]
    expr_ref = [:( $(sym_ref[1]) = $isreference_static($(sym_val[1])) )]
    expr_fld = []
    expr_idx = []
    expr_bit = []
    expr_off = []
    if cases[2] == :getindex # the next level is a dereference `[]`
      expr_fld = [:( $(sym_fld[1]) = nothing                              )]
      # expr_idx = [:( $(sym_idx[1]) = ()                                   )]
      expr_idx = [:( $(sym_idx[1]) = $(Expr(:tuple,levels[2]...))         )]
      expr_bit = [:( $(sym_bit[1]) = $refisbitstype_static($(sym_val[1])) )]
      # expr_off = [:( $(sym_off[1]) = UInt64(0)                            )]
      expr_off = [:( $(sym_off[1]) = $indexoffset_static($(sym_val[1]),0,$(sym_idx[1])...)      )]
    else # the next level provides a symbol
      expr_fld = [:( $(sym_fld[1]) = $(levels[2])                                             )]
      expr_idx = [:( $(sym_idx[1]) = $fieldindex_generated($(sym_val[1]),$(sym_fld[1]))       )]
      expr_bit = [:( $(sym_bit[1]) = $fieldisbitstype_generated($(sym_val[1]), $(sym_fld[1])) )]
      expr_off = [:( $(sym_off[1]) = $fieldoffset_static($(sym_val[1]), $(sym_idx[1]))        )] # TODO: sym_idx becomes 0 when it is not a field of the corresp. struct
    end
    expr_ptr = [:( $(sym_ptr[1]) = $(sym_mut[1]) ? pointer_from_objref($(sym_val[1])) + $(sym_off[1]) : $PtrOrNothing($(sym_val[1])) )]

    # setting up the expressions to hold compiler constants: for the following levels
    for n=2:length(levels)-1 # every level, we "get" a value: either by dereferencing or by accessing a field
      if cases[n] == :getindex # the current level's value is obtained via dereferencing `[]` the previous level's value
        push!(expr_val,:( $(sym_val[n]) = getindex($(sym_val[n-1]), $(sym_idx[n-1])...) )) # do the dereferencing `[]` to obtain a value
      else # the current level's value is obtained via a symbol from the previous level's value
        # push!(expr_val,:( $(sym_val[n]) = getfield($(sym_val[n-1]), $(sym_fld[n-1])) ))
        push!(expr_val,:( $(sym_val[n]) = $getfieldorpointer($(sym_val[n-1]), $(sym_fld[n-1]), $(sym_idx[n-1])) ))
      end
        push!(expr_mut,:( $(sym_mut[n]) = $ismutable_static($(sym_val[n]))     ))
        push!(expr_ref,:( $(sym_ref[n]) = $isreference_static($(sym_val[n-1])) ))
      if cases[n+1] == :getindex # the next level is a dereference `[]`
        push!(expr_fld,:( $(sym_fld[n]) = nothing                              ))
        # push!(expr_idx,:( $(sym_idx[n]) = ()                                   ))
        push!(expr_idx,:( $(sym_idx[n]) = $(Expr(:tuple,levels[n+1]...))       ))
        push!(expr_bit,:( $(sym_bit[n]) = $refisbitstype_static($(sym_val[n])) ))
        # push!(expr_off,:( $(sym_off[n]) = UInt64(0)                            ))
        push!(expr_off,:( $(sym_off[n]) = $indexoffset_static($(sym_val[n]),$(sym_off[n-1]),$(sym_idx[n])...) ))
      else # the next level provides a symbol
        push!(expr_fld,:( $(sym_fld[n]) = $(levels[n+1]) ))
        push!(expr_idx,:( $(sym_idx[n]) = $fieldindex_generated($(sym_val[n]),$(sym_fld[n])) ))
        push!(expr_bit,:( $(sym_bit[n]) = $fieldisbitstype_generated($(sym_val[n]), $(sym_fld[n])) ))
        push!(expr_off,:( $(sym_off[n]) = $fieldoffset_static($(sym_val[n]), $(sym_idx[n])) + ($(sym_bit[n-1]) ? $(sym_off[n-1]) : Int64(0)) )) # TODO: sym_idx becomes 0 when it is not a field of the corresp. struct
      end
      push!(expr_ptr,:( $(sym_ptr[n]) = $fieldpointer_static($(sym_val[n-1]),$(sym_val[n]),$(sym_off[n]),$(sym_ptr[n-1])) ))
    end

    # https://github.com/JuliaLang/julia/issues/10578
    #   "Comments can be passed through using the Expr(:meta, ...) functionality we have now."
    #   Couldn't get this working, so we are using LineNumberNode instead for some debug information
    exprs = []
    for n=1:length(levels)-1
      level_str = replace(repr(levels[n]),"\n" => "")
      push!(exprs,LineNumberNode(-1,"level$(n) $(level_str)"))
      push!(exprs,expr_val[n])
      push!(exprs,expr_fld[n])
      push!(exprs,expr_idx[n])
      # push!(exprs,expr_chk[n])
      push!(exprs,expr_bit[n])
      push!(exprs,expr_mut[n])
      push!(exprs,expr_off[n])
      push!(exprs,expr_ptr[n])
    end

    # obtain the result
    push!(exprs,LineNumberNode(-1,"result"))
    sym_rhs = gensym("RHS")
    push!(exprs,:( $sym_rhs = $RHS ))

    #=
    generate nested `if` statements based on the previously obtained compiler constants to catch all the cases
      If we are at level[n], then
      - this level's value is obtained from the previous level's value via dereferencing or via a symbol,
      - whether this value is a bitstype was already detected in the previous level and it is bit[n-1]
      - and if this level's value is of a bitstype, then we assume it to be inlined into the parent's struct and therefore the parent's value or one it's ancestors have to be used as a base pointer.
       - … because pointer_from_objref will fail on bitstypes
      so it becomes
      - bit[n-1] ⇒ is this level's field a bitstype? Then use the previous expression from level[n-1] instead.
      - val[n] ⇒ Else, use this as base and perform store operation
    =#

    # the first level: use the first level's value as the base to assign at the second-to-last accumulated offset the `rhs` of the second-to-last field's type in the second-to-last value
    # expr_sto = :(
    #     $(LineNumberNode(-1,"@val1+off$(length(levels)-1) <- val$(length(levels)-1)"))
    #   ; $unsafe_store_generated($(sym_val[length(levels)-1]),$(sym_fld[length(levels)-1]),$(sym_val[1]),$(sym_off[length(levels)-1]),$sym_rhs)
    #   )
    expr_sto = :(
      $(LineNumberNode(-1,"@val1+off$(length(levels)-1) <- val$(length(levels)-1)"))
      ; $unsafe_store_generated2($(Val(mode)),$(sym_val[length(levels)-1]),$(sym_fld[length(levels)-1]),$(sym_val[1]),$(sym_off[length(levels)-1]),$sym_rhs,$(sym_ptr[1]),$(Val(accumulatePointers)),$(Val(writeReferences)),$(Val(reallocateImmutableRHSpointer)))
      )
    if length(levels) == 2 # if the first level is the second-to-last level AND it is mutable, we can use the normal assignment
      expr_rec = :(
            $(sym_mut[1]) # is the current level's value of mutable?
          ? $setfield_or_deref($(Val(mode)),$(sym_val[1]),$(sym_fld[1]),$sym_rhs)
          : $expr_sto # use unsafe_store! on pointer_from_objref for the first level
          )
    else # directly use unsafe_store! on pointer_from_objref for the first level
      expr_rec = expr_sto
    end

    # the remaining levels: use the current level's value as the base to assign at the second-to-last accumulated offset the `rhs` of the second-to-last field's type in the second-to-last value
    for n=2:length(levels)-1
      # expr_sto = :(
      #     $(LineNumberNode(-1,"@val$(n)+off$(length(levels)-1) <- val$(length(levels)-1)"))
      #   ; $unsafe_store_generated($(sym_val[length(levels)-1]), $(sym_fld[length(levels)-1]), $(sym_val[n]), $(sym_off[length(levels)-1]),$sym_rhs)
      #   )
      expr_sto = :(
          $(LineNumberNode(-1,"@val$(n)+off$(length(levels)-1) <- val$(length(levels)-1)"))
          ; $unsafe_store_generated2($(Val(mode)),$(sym_val[length(levels)-1]), $(sym_fld[length(levels)-1]), $(sym_val[n]), $(sym_off[length(levels)-1]),$sym_rhs,$(sym_ptr[n]),$(Val(accumulatePointers)),$(Val(writeReferences)),$(Val(reallocateImmutableRHSpointer)))
        )
      if n == length(levels)-1 # if the current level is the second-to-last level AND it is mutable, we can use the normal assignment
        expr_rec = :( $(sym_bit[n-1]) # is the current level's value of bitstype?
          ? $expr_rec # use one of the previous level's as a base pointer
          : ( $(sym_mut[n]) # is the current level's value mutable?
            ? $setfield_or_deref($(Val(mode)),$(sym_val[length(levels)-1]),$(sym_fld[length(levels)-1]),$sym_rhs) # use the normal assignment
            : $expr_sto # use unsafe_store! on pointer_from_objref fot the current level
            )
          )
      else
        expr_rec = :( $(sym_bit[n-1]) # is the current level's value of bitstype?
          ? $expr_rec # use one of the previous level's as a base pointer
          : $expr_sto # use unsafe_store! on pointer_from_objref fot the current level
          )
      end
    end
    push!(exprs,:( $expr_rec ))

    # unsafe_store! returns a pointer, but we want the assignment (=) to return the right hand side
    mode == :assignment && push!(exprs,:( $sym_rhs ))

    return esc(Expr(:block,exprs...))
  end

  # exported macros
  #   the `let...end` was necessary to omit repl confusion which occurred sometimes when errors happened (even unrelated ones)
  macro mem(expr)
    return :( let; $(mem_helper(expr,:assignment,false,false,false,true)); end )
  end
  macro yolo(expr)
    return :( let; $(mem_helper(expr,:assignment,true,true,true,true)); end )
  end
  macro ptr(expr)
    return :( let; $(mem_helper(expr,:pointer,true,true,true,true)); end )
  end
  macro voidptr(expr) # Cvoid == Nothing
    return :(reinterpret(Ptr{Nothing},let; $(mem_helper(expr,:pointer,true,true,true,true)); end))
  end
  macro typedptr(type,expr)
    return :(reinterpret(Ptr{$type},let; $(mem_helper(expr,:pointer,true,true,true,true)); end))
  end
end

ext: jl


text:
function apprunning(io::IO, app::App, ip::IPAddr, port::Int)
    if ip == Sockets.localhost
        print(io, """

          App running at:
          http://localhost:$port/\n
        """)
    else
        print(io, """

          App running at:
          http://$ip:$port/\n
        """)
    end

    routetable(io, app)
end

apprunning(app::App, ip::IPAddr, port::Int) = apprunning(stdout, app, ip, port)

function parsebody(::JSONParser, req::HTTP.Request)
    body = IOBuffer(HTTP.payload(req))
    if eof(body)
        return JSON3.Object()
    else
        return JSON3.read(body)
    end
end

function parsebody(::TextParser, req::HTTP.Request)
    body = HTTP.payload(req)
    return String(body)
end

serve(app::App) = serve(app, Sockets.localhost, 8081)
serve(app::App, port::Int) = serve(app, Sockets.localhost, port)
serve(app::App, ip::IPAddr) = serve(app, ip, 8081)
serve(app::App, ip::AbstractString) = serve(app, parse(IPAddr, ip), 8081)
serve(app::App, ip::AbstractString, port::Int) = serve(app, parse(IPAddr, ip), port)

function serve(app::App, ip::IPAddr, port::Int)
    apprunning(app, ip, port)

    HTTP.serve(ip, port) do req::HTTP.Request
        req_body = parsebody(app.reqparser, req)
        res_body = HTTP.handle(app.router, req, req_body)
        return Res(res_body)
    end
end


text:
using Flux
using Functors
using DataStructures
using Pickle.Torch
using Pickle.Torch: StridedView

using LinearAlgebra

"""
  `get_state_dict(layer)`

Collect model parameters into one `OrderedDict` which also
known as `state_dict` in PyTorch.

model parameters are get from `Functors.functor`.
"""
function get_state_dict(layer)
  state = OrderedDict{String, Any}()
  get_state_dict(state, nothing, layer)
  return state
end

function get_state_dict(state, prefix, layer)
  param = Functors.functor(layer)[1]
  ks = keys(param)
  for k in ks
    cprefix = isnothing(prefix) ? String(k) : join((prefix, k), '.')
    get_state_dict(state, cprefix, param[k])
  end
end

function get_state_dict(state, prefix, x::AbstractArray)
  state[prefix] = x
end

"""
  `load_state!(layer, state)`

Load the model parameter from `state` into the `layer`.
give warning if something appear in `state` but not `layer`.
"""
function load_state!(layer, state)
  for k in keys(state)
    if hasfield(typeof(layer), k)
      load_state!(getfield(layer, k),  getfield(state, k))
    else
      @warn "$(Base.typename(typeof(layer))) doesn't have field $k."
    end
  end
  pf = Functors.functor(layer) |> first |> keys
  rem = setdiff(pf, keys(state))
  if (!iszero ∘ length)(rem)
    @warn "Some fields of $(Base.typename(typeof(layer))) aren't initialized with loaded state: $(rem...)"
  end
end

function load_state!(weight::A1, state::A2) where {A1<:AbstractArray, A2<:AbstractArray}
    weight .= state
end

function load_state!(weight::T, state::S) where {T<:Transpose, S<:StridedView}
  # weight is probably share weighted
  if weight != state
    weight .= state
  end
end

include("./utils.jl")
include("./base.jl")
include("./bert.jl")
include("./gpt2.jl")
include("./roberta.jl")

for model in :[
  bert,
  gpt2,
  roberta,
].args
  for (name, type) in get_model_type(Val(model))
    @eval get_model_type(::Val{$(Meta.quot(model))}, ::Val{$(Meta.quot(name))}) = $type
  end
end

"""
  `load_model!(model::HGFPreTrainedModel, state)`

Similar to [`load_state!`](@ref) but only work for
huggingface pre-trained models, this is used for
handling loading state of different level.
"""
function load_model!(model::HGFPreTrainedModel, state)
  basekey = basemodelkey(model)

  load_to_base = isbasemodel(model)
  load_from_base = !haskey(state, basekey)

  if load_to_base ⊻ load_from_base # not same level
    if load_to_base
      basestate = state[basekey]
      load_state!(model, basestate)
    else # load_from_base
      @warn "load from base: prediction layer not found in state: initialized."
      model_to_load = basemodel(model)
      load_state!(model_to_load, state)
    end
  else
    load_state!(model, state)
  end
end

"""
  `load_model(model_type, model_name; config=load_config(model_name))`

build model with given `model_type` and load state from
`model_name`.
"""
function load_model(model_type, model_name; config=load_config(model_name))
  model = model_type(config)
  state = load_state(model_name)
  load_model!(model, state)
  return model
end

"""
  `save_model(model_name, model; path=pwd(), weight_name=DEFAULT_WEIGHT_NAME)`

save the `model` at `<path>/<model_name>/<weight_name>`.
"""
function save_model(model_name, model; path=pwd(), weight_name=DEFAULT_WEIGHT_NAME)
  model_path = joinpath(path, model_name)
  !isdir(model_path) && error("$model_path is not a dir.")
  model_file = joinpath(model_path, weight_name)
  state = get_state_dict(model)
  Torch.THsave(model_file, state)
  return model_file
end