pharos_plugins/
metadata.rs1use pharos_corefile::{Cursor, Error, origins_from_args_or_server_block};
4use pharos_proto::Name;
5#[cfg(feature = "plugin-kubernetes")]
6use pharos_proto::NameRef;
7use pharos_server::{Context, Next, Outcome, Plugin};
8
9#[must_use]
11pub fn is_label(label: &str) -> bool {
12 label
13 .find('/')
14 .is_some_and(|index| index > 0 && index + 1 < label.len())
15}
16
17#[derive(Debug)]
18pub struct Metadata {
19 pub domains: Vec<Name>,
20}
21
22impl Metadata {
23 #[cfg(feature = "plugin-kubernetes")]
24 pub(crate) fn matches(&self, cx: &Context<'_>) -> bool {
25 let Some(question) = cx.query.questions().next() else {
26 return false;
27 };
28 let count = question.name.labels().count();
29 self.domains.iter().any(|domain| {
30 let n = domain.label_count();
31 count >= n
33 && question
34 .name
35 .labels()
36 .skip(count - n)
37 .zip(NameRef::parse(domain.as_wire(), 0).unwrap().labels())
38 .all(|(a, b)| a.eq_ignore_ascii_case(b))
39 })
40 }
41}
42
43pub(crate) fn parse(cursor: &mut Cursor<'_>, domains: &[&str]) -> Result<Metadata, Error> {
44 cursor.next();
45 let args = cursor.remaining_args();
46 let args = args.iter().map(String::as_str).collect::<Vec<_>>();
47 let domains = origins_from_args_or_server_block(&args, domains)
48 .iter()
49 .filter_map(|domain| domain.parse().ok())
50 .collect();
51 if cursor.next_block() || cursor.next() {
52 return Err(cursor.arg_err());
53 }
54 Ok(Metadata { domains })
55}
56
57impl Plugin for Metadata {
58 async fn serve<N: Next + Send>(&self, cx: &mut Context<'_>, next: N) -> Outcome {
59 next.run(cx).await
60 }
61}