1use pyo3::prelude::*;
2use pyo3::types::PyList;
3
4pub mod dns;
5pub mod ip;
6pub mod node;
7pub mod target;
8pub mod utils;
9
10use dns::ScopeMode;
11use target::RadixTarget;
12use utils::host_size_key;
13
14#[pyclass]
15struct PyRadixTarget {
16 inner: RadixTarget,
17}
18
19#[pymethods]
20impl PyRadixTarget {
21 #[new]
22 #[pyo3(signature = (hosts = None, strict_scope = false, acl_mode = false))]
23 fn new(hosts: Option<Bound<'_, PyList>>, strict_scope: bool, acl_mode: bool) -> PyResult<Self> {
24 let scope_mode = match (strict_scope, acl_mode) {
25 (false, false) => ScopeMode::Normal,
26 (true, false) => ScopeMode::Strict,
27 (false, true) => ScopeMode::Acl,
28 (true, true) => {
29 return Err(pyo3::exceptions::PyValueError::new_err(
30 "strict_scope and acl_mode are mutually exclusive",
31 ));
32 }
33 };
34 let mut inner = RadixTarget::new(&[], scope_mode);
35 if let Some(hosts_list) = hosts {
36 for host in hosts_list.iter() {
37 inner.insert(&host.extract::<String>()?);
38 }
39 }
40 Ok(PyRadixTarget { inner })
41 }
42
43 fn insert(&mut self, value: &str) -> Option<String> {
44 self.inner.insert(value)
45 }
46
47 fn len(&self) -> usize {
48 self.inner.len()
49 }
50
51 fn is_empty(&self) -> bool {
52 self.inner.is_empty()
53 }
54
55 fn contains(&self, value: &str) -> bool {
56 self.inner.contains(value)
57 }
58
59 fn delete(&mut self, value: &str) -> bool {
60 self.inner.delete(value)
61 }
62
63 fn get(&self, value: &str) -> Option<String> {
64 self.inner.get(value)
65 }
66
67 fn prune(&mut self) -> usize {
68 self.inner.prune()
69 }
70
71 fn defrag(&mut self) -> (Vec<String>, Vec<String>) {
72 let (cleaned, new) = self.inner.defrag();
73 (cleaned.into_iter().collect(), new.into_iter().collect())
74 }
75
76 fn __repr__(&self) -> String {
77 format!(
78 "RadixTarget(strict_scope={}, {} hosts)",
79 self.inner.strict_scope(),
80 self.inner.len()
81 )
82 }
83
84 fn __eq__(&self, other: &PyRadixTarget) -> bool {
85 self.inner == other.inner
86 }
87
88 fn __hash__(&self) -> u64 {
89 self.inner.hash()
90 }
91
92 fn contains_target(&self, other: &PyRadixTarget) -> bool {
93 self.inner.contains_target(&other.inner)
94 }
95
96 fn __iter__(slf: PyRef<'_, Self>) -> PyResult<PyRadixTargetIterator> {
97 let hosts: Vec<String> = slf.inner.hosts().iter().cloned().collect();
98 Ok(PyRadixTargetIterator { hosts, index: 0 })
99 }
100
101 fn __bool__(&self) -> bool {
102 !self.inner.is_empty()
103 }
104
105 fn __len__(&self) -> usize {
106 self.inner.len()
107 }
108
109 fn __str__(&self) -> String {
110 let mut hosts: Vec<String> = self.inner.hosts().iter().cloned().collect();
111 hosts.sort();
112
113 if hosts.len() <= 5 {
114 hosts.join(",")
115 } else {
116 format!("{},…", hosts[..5].join(","))
117 }
118 }
119
120 fn copy(&self) -> PyRadixTarget {
121 PyRadixTarget {
122 inner: self.inner.copy(),
123 }
124 }
125}
126
127#[pyclass]
128struct PyRadixTargetIterator {
129 hosts: Vec<String>,
130 index: usize,
131}
132
133#[pymethods]
134impl PyRadixTargetIterator {
135 fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> {
136 slf
137 }
138
139 fn __next__(&mut self) -> Option<String> {
140 if self.index < self.hosts.len() {
141 let result = self.hosts[self.index].clone();
142 self.index += 1;
143 Some(result)
144 } else {
145 None
146 }
147 }
148}
149
150#[pyfunction]
152fn py_host_size_key(host: &Bound<'_, pyo3::PyAny>) -> PyResult<(i64, String)> {
153 let host_str = host.str()?.to_string();
155 Ok(host_size_key(&host_str))
156}
157
158#[pymodule]
159fn _radixtarget(m: &Bound<'_, PyModule>) -> PyResult<()> {
160 m.add_class::<PyRadixTarget>()?;
161 m.add_function(wrap_pyfunction!(py_host_size_key, m)?)?;
162 Ok(())
163}