1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
use crate::OpenapiSchema;
use indexmap::IndexMap;
use openapiv3::{ReferenceOr, Schema, SchemaKind, Type};
use std::borrow::Cow;

pub type Dependencies = IndexMap<String, OpenapiSchema>;
pub type Properties = IndexMap<String, ReferenceOr<Box<Schema>>>;
pub type Required = Vec<String>;

fn add_dependencies(dependencies: &mut Dependencies, other: &mut Dependencies) {
	while let Some((dep_name, dep_schema)) = other.pop() {
		if !dependencies.contains_key(&dep_name) {
			dependencies.insert(dep_name, dep_schema);
		}
	}
}

pub fn inline_if_unnamed(
	dependencies: &mut Dependencies,
	mut schema: OpenapiSchema,
	doc: Option<&'static str>
) -> ReferenceOr<Schema> {
	add_dependencies(dependencies, &mut schema.dependencies);
	match schema.name.as_ref() {
		Some(schema_name) => {
			let ref_name = schema_name.replace(|c: char| !c.is_alphanumeric(), "_");
			let mut reference = "#/components/schemas/".to_string();
			reference.push_str(&ref_name);
			dependencies.insert(ref_name, schema);
			ReferenceOr::Reference { reference }
		},

		None => {
			let mut schema = schema.into_schema();
			if let Some(doc) = doc {
				schema.schema_data.description = Some(doc.to_string());
			}
			ReferenceOr::Item(schema)
		}
	}
}

struct FlattenError(Cow<'static, str>);

fn flatten_impl(properties: &mut Properties, required: &mut Required, schema: SchemaKind) -> Result<(), FlattenError> {
	let mut obj = match schema {
		SchemaKind::Type(Type::Object(obj)) => obj,
		SchemaKind::OneOf { .. } => {
			return Err(FlattenError(
				"#[serde(flatten)] is currently not supported for enums with non-unit variants".into()
			))
		},
		_ => return Err(FlattenError("Expected object".into()))
	};

	while let Some((prop_name, prop_schema)) = obj.properties.pop() {
		if properties.contains_key(&prop_name) {
			return Err(FlattenError(format!("Duplicate property name {}", prop_name).into()));
		}
		properties.insert(prop_name, prop_schema);
	}
	required.extend(obj.required.into_iter());

	Ok(())
}

pub fn flatten(
	dependencies: &mut Dependencies,
	properties: &mut Properties,
	required: &mut Required,
	mut schema: OpenapiSchema
) {
	add_dependencies(dependencies, &mut schema.dependencies);
	match flatten_impl(properties, required, schema.schema) {
		Ok(_) => {},
		Err(e) => panic!("Flattening produced an error: {}", e.0)
	};
}