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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
use std::path::Path;

use clang::{Entity, EntityKind};

use crate::entity::WalkAction;

/// Visitor to be used in conjunction with [EntityWalker]
pub trait EntityWalkerVisitor<'tu> {
	/// Check whether the visitor is interested in entities from the specified file
	#[allow(unused)]
	fn wants_file(&mut self, path: &Path) -> bool {
		true
	}

	/// Pass a supported [Entity] to the visitor
	fn visit_entity(&mut self, entity: Entity<'tu>) -> WalkAction;
}

/// Trait to recursively visit every clang [Entity] supported by the OpenCV binding generator
pub trait EntityWalkerExt<'tu> {
	/// Recursively visits every clang [Entity] supported by the OpenCV binding generator starting from the `self`
	fn walk_opencv_entities(self, visitor: impl EntityWalkerVisitor<'tu>);
}

impl<'tu> EntityWalkerExt<'tu> for Entity<'tu> {
	fn walk_opencv_entities(self, mut visitor: impl EntityWalkerVisitor<'tu>) {
		self.visit_children(|root_decl, _| {
			let res = if let Some(loc) = root_decl.get_location() {
				if let Some(file) = loc.get_file_location().file.map(|f| f.get_path()) {
					if visitor.wants_file(&file) {
						match root_decl.get_kind() {
							EntityKind::Namespace => {
								if root_decl.get_name().map_or(false, |name| name.starts_with("cv")) {
									visit_cv_namespace(root_decl, &mut visitor)
								} else {
									WalkAction::Continue
								}
							}
							EntityKind::MacroDefinition
							| EntityKind::MacroExpansion
							| EntityKind::EnumDecl
							| EntityKind::TypedefDecl => visitor.visit_entity(root_decl),
							EntityKind::FunctionDecl
							| EntityKind::InclusionDirective
							| EntityKind::UnionDecl
							| EntityKind::UnexposedDecl
							| EntityKind::StructDecl
							| EntityKind::Constructor
							| EntityKind::Method
							| EntityKind::FunctionTemplate
							| EntityKind::ConversionFunction
							| EntityKind::ClassTemplate
							| EntityKind::ClassDecl
							| EntityKind::Destructor
							| EntityKind::LinkageSpec
							| EntityKind::VarDecl => WalkAction::Continue,
							_ => {
								unreachable!("Unsupported decl for file: {:#?}", root_decl)
							}
						}
					} else {
						WalkAction::Continue
					}
				} else {
					WalkAction::Continue
				}
			} else {
				WalkAction::Continue
			};
			res.into()
		});
	}
}

fn visit_cv_namespace<'tu>(ns: Entity<'tu>, visitor: &mut impl EntityWalkerVisitor<'tu>) -> WalkAction {
	let is_interrupted = ns.visit_children(|decl, _| {
		let res = match decl.get_kind() {
			EntityKind::Namespace => visit_cv_namespace(decl, visitor),
			EntityKind::ClassDecl
			| EntityKind::ClassTemplate
			| EntityKind::ClassTemplatePartialSpecialization
			| EntityKind::StructDecl
			| EntityKind::EnumDecl
			| EntityKind::FunctionDecl
			| EntityKind::TypedefDecl
			| EntityKind::VarDecl
			| EntityKind::TypeAliasDecl => visitor.visit_entity(decl),
			EntityKind::Constructor
			| EntityKind::ConversionFunction
			| EntityKind::Destructor
			| EntityKind::Method
			| EntityKind::UnexposedDecl
			| EntityKind::FunctionTemplate
			| EntityKind::UsingDeclaration
			| EntityKind::UsingDirective
			| EntityKind::TypeAliasTemplateDecl
			| EntityKind::LinkageSpec => {
				/* ignoring */
				WalkAction::Continue
			}
			_ => {
				unreachable!("Unsupported decl for OpenCV namespace: {:#?}", decl)
			}
		};
		res.into()
	});
	WalkAction::continue_until(is_interrupted)
}