Current section

Files

Jump to
oapi_generator lib open_api reader state.ex
Raw

lib/open_api/reader/state.ex

defmodule OpenAPI.Reader.State do
@moduledoc """
State of the reader phase of code generation
This struct is created at the beginning of the read phase using data from the overall
`OpenAPI.State`. It is fully managed by the read phase, and it is unlikely that client libraries
would read or write to this struct.
"""
require Logger
alias OpenAPI.Reader.Config
alias OpenAPI.Spec.Path.Parameter
@typedoc "Decode function for raw Yaml"
@type decoder :: decoder(term)
@typedoc "Decode function for raw Yaml"
@type decoder(t) :: (t, yaml -> {map, t})
@typedoc "Key or index of a Yaml document."
@type path_segment :: String.t() | integer
@typedoc "OpenAPI reader state"
@type t :: %__MODULE__{
base_file: String.t() | nil,
base_file_path: [path_segment],
config: Config.t(),
current_file: String.t() | nil,
current_file_path: [path_segment],
files: %{optional(String.t()) => yaml | nil},
last_ref_file: String.t() | nil,
last_ref_path: [path_segment],
path_parameters: [Parameter.t()],
refs: %{optional(String.t()) => map},
spec: Spec.t() | nil
}
@typedoc "Raw Yaml input"
@type yaml :: map | list
defstruct [
:base_file,
:base_file_path,
:config,
:current_file,
:current_file_path,
:files,
:last_ref_file,
:last_ref_path,
:path_parameters,
:refs,
:spec
]
#
# Creation
#
@doc false
@spec new(Config.t()) :: t
def new(config) do
%__MODULE__{
base_file: nil,
base_file_path: [],
config: config,
current_file: nil,
current_file_path: [],
files: files(config),
last_ref_file: nil,
last_ref_path: [],
path_parameters: [],
refs: %{},
spec: nil
}
end
@spec files(Config.t()) :: %{optional(String.t()) => yaml | nil}
defp files(config) do
%Config{additional_files: additional_files, file: file, passed_files: passed_files} = config
[passed_files, file, additional_files]
|> List.flatten()
|> Enum.reject(&is_nil/1)
|> Enum.map(fn filename -> {filename, nil} end)
|> Enum.into(%{})
end
#
# Manipulation
#
@doc false
@spec with_path(t, yaml, path_segment, decoder) ::
{t, term}
def with_path(state, yaml, path_segment, decoder) do
%__MODULE__{
base_file_path: original_base_file_path,
current_file_path: original_current_file_path,
last_ref_path: original_last_ref_path
} = state
state = %__MODULE__{
state
| base_file_path: [path_segment | original_base_file_path],
current_file_path: [path_segment | original_current_file_path],
last_ref_path: [path_segment | original_last_ref_path]
}
{state, result} = decoder.(state, yaml)
state = %__MODULE__{
state
| base_file_path: original_base_file_path,
current_file_path: original_current_file_path,
last_ref_path: original_last_ref_path
}
{state, result}
end
@doc false
@spec with_ref(t, yaml, decoder) :: {t, term}
def with_ref(state, %{"$ref" => ref}, decoder) do
[new_file, new_ref_path] = String.split(ref, "#")
new_file = Path.join(state.current_file, new_file)
new_ref_path_segments = String.split(new_ref_path, "/", trim: true)
%__MODULE__{
last_ref_file: original_last_ref_file,
last_ref_path: original_last_ref_path
} = state
new_ref = "#{new_file}##{new_ref_path}"
state = %__MODULE__{
state
| last_ref_file: new_file,
last_ref_path: Enum.reverse(new_ref_path_segments)
}
stored_yaml = state.refs[new_ref]
{state, yaml} =
if stored_yaml do
{state, stored_yaml}
else
state = OpenAPI.Reader.ensure_file(state, new_file)
yaml = get_in(state.files[new_file], new_ref_path_segments)
state = %__MODULE__{state | refs: Map.put(state.refs, new_ref, yaml)}
{state, yaml}
end
{state, result} = decoder.(state, yaml)
state = %__MODULE__{
state
| last_ref_file: original_last_ref_file,
last_ref_path: original_last_ref_path
}
{state, result}
end
def with_ref(state, yaml, decoder), do: decoder.(state, yaml)
end