{{#ifneq purpose 'METADATA'}}
const {{constantName (concat nativeType 'Keys')}}: string[] = [{{#each properties}}{{{stringLiteral serializedName}}}{{#hasMore}}, {{/hasMore}}{{/each}}]

{{/ifneq}}
function model{{className nativeType.parentType}}FromRequestContent(name: string, value: any, knownKeys: Record<string, boolean> = {}): {{#if discriminator}}Omit<{{nativeType.parentType}}, {{{stringLiteral discriminator.serializedName}}}>{{else}}{{nativeType}}{{/if}} {
{{#ifeq purpose 'METADATA'}}
	const result: {{{nativeType}}} = {
	{{#with properties.value}}
	{{#unless readOnly}}
	{{#if required}}
	{{#if nullable}}
		{{{stringLiteral serializedName}}}: allowNull({{>frag/fromRequest schema prefix=''}})(`${name}.{{serializedName}}`, value),
	{{else}}
		{{{stringLiteral serializedName}}}: {{>frag/fromRequest schema prefix=''}}(`${name}.{{serializedName}}`, value),
	{{/if}}
	{{else}}
	{{#if nullable}}
		{{{stringLiteral serializedName}}}: allowNullOrUndefined({{>frag/fromRequest schema prefix=''}})(`${name}.{{serializedName}}`, value),
	{{else}}
		{{{stringLiteral serializedName}}}: allowUndefined({{>frag/fromRequest schema prefix=''}})(`${name}.{{serializedName}}`, value),
	{{/if}}
	{{/if}}
	{{/unless}}
	{{/with}}
	}
{{else}}
	if (typeof value !== 'object' || value === undefined || value === null) {
		throw `Invalid type for ${name}: expected object got ${typeof value}`
	}

	{{constantName (concat nativeType 'Keys')}}.forEach(k => knownKeys[k] = true)

	const result: {{#if discriminator}}Omit<{{nativeType.parentType}}, {{{stringLiteral discriminator.serializedName}}}>{{else}}{{nativeType}}{{/if}} = {
	{{#each parents}}
		...{{>frag/fromRequest . prefix=''}}Content(name, value, knownKeys),
	{{/each}}
	{{#each properties}}
	{{#unless readOnly}}
	{{#if required}}
	{{#if nullable}}
		{{{stringLiteral serializedName}}}: allowNull({{>frag/fromRequest schema prefix=''}})(`${name}.{{serializedName}}`, value[{{{stringLiteral serializedName}}}]),
	{{else}}
		{{{stringLiteral serializedName}}}: {{>frag/fromRequest schema prefix=''}}(`${name}.{{serializedName}}`, value[{{{stringLiteral serializedName}}}]),
	{{/if}}
	{{else}}
	{{#if nullable}}
		{{{stringLiteral serializedName}}}: allowNullOrUndefined({{>frag/fromRequest schema prefix=''}})(`${name}.{{serializedName}}`, value[{{{stringLiteral serializedName}}}]),
	{{else}}
		{{{stringLiteral serializedName}}}: allowUndefined({{>frag/fromRequest schema prefix=''}})(`${name}.{{serializedName}}`, value[{{{stringLiteral serializedName}}}]),
	{{/if}}
	{{/if}}
	{{/unless}}
	{{/each}}
	{{#each discriminatorValues}}
		{{{stringLiteral schema.discriminator.serializedName}}}: {{{literalValue}}},
	{{/each}}
	}
{{/ifeq}}

	return result
}

function model{{className nativeType.parentType}}ToResponseContent(name: string, value: {{nativeType}}, knownKeys: Record<string, boolean> = {}): ToResponse<{{#if discriminator}}Omit<{{nativeType.parentType}}, {{{stringLiteral discriminator.serializedName}}}>{{else}}{{nativeType}}{{/if}}> {
{{#ifeq purpose 'METADATA'}}
	throw 'toResponse not supported for metadata schemas'
{{else}}
	if (typeof value !== 'object' || value === undefined || value === null) {
		throw `Invalid type for ${name}: expected object got ${typeof value}`
	}

	{{constantName (concat nativeType 'Keys')}}.forEach(k => knownKeys[k] = true)
	
	const result: ToResponse<{{#if discriminator}}Omit<{{nativeType.parentType}}, {{{stringLiteral discriminator.serializedName}}}>{{else}}{{nativeType}}{{/if}}> = {
	{{#each parents}}
		...{{>frag/toResponse . prefix=''}}Content(name, value as unknown as {{{nativeType}}}, knownKeys),
	{{/each}}
	{{#each properties}}
	{{#unless writeOnly}}
	{{#if required}}
	{{#if nullable}}
		{{{stringLiteral serializedName}}}: allowNull({{>frag/toResponse schema prefix=''}})(`${name}.{{serializedName}}`, value[{{{stringLiteral serializedName}}}]),
	{{else}}
		{{{stringLiteral serializedName}}}: {{>frag/toResponse schema prefix=''}}(`${name}.{{serializedName}}`, value[{{{stringLiteral serializedName}}}]),
	{{/if}}
	{{else}}
	{{#if nullable}}
		{{{stringLiteral serializedName}}}: allowNullOrUndefined({{>frag/toResponse schema prefix=''}})(`${name}.{{serializedName}}`, value[{{{stringLiteral serializedName}}}]),
	{{else}}
		{{{stringLiteral serializedName}}}: allowUndefined({{>frag/toResponse schema prefix=''}})(`${name}.{{serializedName}}`, value[{{{stringLiteral serializedName}}}]),
	{{/if}}
	{{/if}}
	{{/unless}}
	{{/each}}
	{{#each discriminatorValues}}
		{{{stringLiteral schema.discriminator.serializedName}}}: {{{literalValue}}},
	{{/each}}
	}

	return result
{{/ifeq}}
}

export function model{{className nativeType.parentType}}FromRequest(name: string, value: any): {{nativeType}} {
	{{#if discriminator}}
	const discriminatorValue = value.{{discriminator.serializedName}}
	switch (discriminatorValue) {
		{{#each discriminator.references}}
		case {{{literalValue}}}:
			return {{>frag/fromRequest schema prefix=''}}(name, value)
		{{/each}}
	}

	throw `Invalid value for ${name}: didn't contain a known discriminator value: ${discriminatorValue}`
	{{else}}
	const knownKeys: Record<string, boolean> = {}
	const result: {{nativeType}} = model{{className nativeType.parentType}}FromRequestContent(name, value, knownKeys)

	/* Known keys */
	if (__options?.failOnUnknownProperties) {
		for (const key of Object.keys(value)) {
			if (!knownKeys[key]) {
				throw `Unexpected key in {{nativeType}}: ${key}`
			}
		}
	}

	return result
	{{/if}}
}

export function model{{className nativeType.parentType}}ToResponse(name: string, value: {{nativeType}}): ToResponse<{{nativeType}}> {
	{{#if discriminator}}
	const discriminatorValue = value.{{discriminator.serializedName}}
	switch (discriminatorValue) {
		{{#each discriminator.references}}
		case {{{literalValue}}}:
			return {{>frag/toResponse schema prefix=''}}(name, value)
		{{/each}}
	}

	throw `Invalid value for ${name}: didn't contain a known discriminator value: ${discriminatorValue}`
	{{else}}
	const knownKeys: Record<string, boolean> = {}
	const result: ToResponse<{{nativeType}}> = model{{className nativeType.parentType}}ToResponseContent(name, value, knownKeys)

	/* Known keys */
	if (__options?.failOnUnknownProperties) {
		for (const key of Object.keys(value)) {
			if (!knownKeys[key]) {
				throw `Unexpected key in {{nativeType}}: ${key}`
			}
		}
	}

	return result
	{{/if}}
}
{{>nestedValidation}}
