1use std::{
22 any::type_name,
23 borrow::Cow,
24 ops::{Deref, DerefMut},
25 os::raw::c_void,
26 ptr::{NonNull, null_mut},
27};
28
29use crate::{
30 globals::{get_deregister_node_func, get_register_node_func},
31 valid::{xml_add_id, xml_is_id, xml_remove_id},
32};
33
34use super::{
35 InvalidNodePointerCastError, NodeCommon, XML_XML_NAMESPACE, XmlAttributeType, XmlDocPtr,
36 XmlElementType, XmlGenericNodePtr, XmlNodePtr, XmlNsPtr, xml_free_node_list, xml_new_doc_text,
37 xml_new_ns, xml_new_reconciled_ns, xml_ns_in_scope, xml_static_copy_node_list,
38 xml_tree_err_memory,
39};
40
41#[repr(C)]
42pub struct XmlAttr {
43 pub _private: *mut c_void, pub(crate) typ: XmlElementType, pub name: Box<str>, pub(crate) children: Option<XmlNodePtr>, pub(crate) last: Option<XmlNodePtr>, pub(crate) parent: Option<XmlNodePtr>, pub next: Option<XmlAttrPtr>, pub(crate) prev: Option<XmlAttrPtr>, pub(crate) doc: Option<XmlDocPtr>, pub ns: Option<XmlNsPtr>, pub(crate) atype: Option<XmlAttributeType>, pub(crate) psvi: *mut c_void, }
56
57impl XmlAttr {
58 #[doc(alias = "xmlSearchNsByHref")]
63 pub fn search_ns_by_href(&mut self, doc: Option<XmlDocPtr>, href: &str) -> Option<XmlNsPtr> {
64 if href == XML_XML_NAMESPACE {
65 let mut doc = doc.or(self.document())?;
66 if doc.old_ns.is_none() {
68 return doc.ensure_xmldecl();
69 } else {
70 return doc.old_ns;
71 }
72 }
73 let mut node = self.parent.map(XmlGenericNodePtr::from);
74 while let Some(now) = node {
75 if matches!(
76 now.element_type(),
77 XmlElementType::XmlEntityRefNode
78 | XmlElementType::XmlEntityNode
79 | XmlElementType::XmlEntityDecl
80 ) {
81 return None;
82 }
83 if let Some(now) = XmlNodePtr::try_from(now)
84 .ok()
85 .filter(|now| now.element_type() == XmlElementType::XmlElementNode)
86 {
87 let mut cur = now.ns_def;
89 while let Some(cur_ns) = cur {
90 if cur_ns.href.is_some()
91 && cur_ns.href().as_deref() == Some(href)
92 && cur_ns.prefix().is_some()
93 && xml_ns_in_scope(
94 doc,
95 XmlGenericNodePtr::from_raw(self as *mut Self),
96 Some(now.into()),
97 cur_ns.prefix.as_deref(),
98 ) == 1
99 {
100 return Some(cur_ns);
101 }
102 cur = cur_ns.next;
103 }
104 let cur = now.ns;
105 if let Some(cur) = cur.filter(|cur| {
106 cur.href.as_deref().is_some_and(|h| h == href)
107 && cur.prefix().is_some()
108 && xml_ns_in_scope(
109 doc,
110 XmlGenericNodePtr::from_raw(self as *mut Self),
111 Some(now.into()),
112 cur.prefix.as_deref(),
113 ) == 1
114 }) {
115 return Some(cur);
116 }
117 }
118 node = now.parent();
119 }
120 None
121 }
122
123 pub(super) fn get_prop_node_value_internal(&self) -> Option<String> {
124 if let Some(children) = self.children() {
127 if children.next().is_none()
128 && matches!(
129 children.element_type(),
130 XmlElementType::XmlTextNode | XmlElementType::XmlCDATASectionNode
131 )
132 {
133 let children = XmlNodePtr::try_from(children).unwrap();
134 return children.content.clone();
136 } else if let Some(ret) = children.get_string(self.document(), 1) {
137 return Some(ret);
138 }
139 }
140 Some("".to_owned())
141 }
142
143 #[doc(alias = "xmlNodeGetContent")]
152 pub fn get_content(&self) -> Option<String> {
153 self.get_prop_node_value_internal()
154 }
155
156 #[doc(alias = "xmlBufGetNodeContent")]
164 pub fn get_content_to(&self, buf: &mut String) -> i32 {
165 assert!(matches!(
166 self.element_type(),
167 XmlElementType::XmlAttributeNode
168 ));
169 let mut tmp = self.children();
170
171 while let Some(now) = tmp {
172 if matches!(now.element_type(), XmlElementType::XmlTextNode) {
173 let now = XmlNodePtr::try_from(now).unwrap();
174 buf.push_str(now.content.as_deref().unwrap());
175 } else {
176 now.get_content_to(buf);
177 }
178 tmp = now.next();
179 }
180 0
181 }
182
183 #[doc(alias = "xmlNodeSetBase")]
185 #[cfg(any(feature = "libxml_tree", feature = "xinclude"))]
186 pub fn set_base(&mut self, _uri: Option<&str>) {
187 use crate::tree::XML_XML_NAMESPACE;
188
189 self.search_ns_by_href(self.document(), XML_XML_NAMESPACE);
190 }
191
192 #[doc(alias = "xmlSetTreeDoc")]
194 pub fn set_doc(&mut self, doc: Option<XmlDocPtr>) {
195 if self.document() != doc {
196 if let Some(children) = self.children() {
197 children.set_doc_all_sibling(doc);
198 }
199
200 self.set_document(doc);
202 }
203 }
204}
205
206impl Default for XmlAttr {
207 fn default() -> Self {
208 Self {
209 _private: null_mut(),
210 typ: XmlElementType::XmlAttributeNode,
211 name: "".into(),
212 children: None,
213 last: None,
214 parent: None,
215 next: None,
216 prev: None,
217 doc: None,
218 ns: None,
219 atype: None,
220 psvi: null_mut(),
221 }
222 }
223}
224
225impl NodeCommon for XmlAttr {
226 fn document(&self) -> Option<XmlDocPtr> {
227 self.doc
228 }
229 fn set_document(&mut self, doc: Option<XmlDocPtr>) {
230 self.doc = doc;
231 }
232 fn element_type(&self) -> XmlElementType {
233 self.typ
234 }
235 fn name(&self) -> Option<Cow<'_, str>> {
236 Some(Cow::Borrowed(&self.name))
237 }
238 fn children(&self) -> Option<XmlGenericNodePtr> {
239 self.children.map(|children| children.into())
240 }
241 fn set_children(&mut self, children: Option<XmlGenericNodePtr>) {
242 self.children = children.map(|children| XmlNodePtr::try_from(children).unwrap());
243 }
244 fn last(&self) -> Option<XmlGenericNodePtr> {
245 self.last.map(|last| last.into())
246 }
247 fn set_last(&mut self, last: Option<XmlGenericNodePtr>) {
248 self.last = last.map(|children| XmlNodePtr::try_from(children).unwrap());
249 }
250 fn next(&self) -> Option<XmlGenericNodePtr> {
251 self.next.map(|next| next.into())
252 }
253 fn set_next(&mut self, next: Option<XmlGenericNodePtr>) {
254 self.next = next.map(|next| XmlAttrPtr::try_from(next).unwrap())
255 }
256 fn prev(&self) -> Option<XmlGenericNodePtr> {
257 self.prev.map(|prev| prev.into())
258 }
259 fn set_prev(&mut self, prev: Option<XmlGenericNodePtr>) {
260 self.prev = prev.map(|prev| XmlAttrPtr::try_from(prev).unwrap())
261 }
262 fn parent(&self) -> Option<XmlGenericNodePtr> {
263 self.parent.map(|parent| parent.into())
264 }
265 fn set_parent(&mut self, parent: Option<XmlGenericNodePtr>) {
266 self.parent = parent.map(|children| XmlNodePtr::try_from(children).unwrap());
267 }
268
269 fn unlink(&mut self) {
270 if let Some(mut parent) = self.parent {
271 let attr = unsafe {
272 XmlAttrPtr::from_raw(self).unwrap()
276 };
277 if parent.properties == attr {
278 parent.properties = attr.and_then(|attr| attr.next);
279 }
280 self.set_parent(None);
281 }
282 if let Some(mut next) = self.next() {
283 next.set_prev(self.prev());
284 }
285 if let Some(mut prev) = self.prev() {
286 prev.set_next(self.next());
287 }
288 self.set_next(None);
289 self.set_prev(None);
290 }
291}
292
293#[derive(PartialEq, Eq, PartialOrd, Ord)]
294pub struct XmlAttrPtr(NonNull<XmlAttr>);
295
296impl XmlAttrPtr {
297 pub(crate) fn new(node: XmlAttr) -> Option<Self> {
302 let boxed = Box::new(node);
303 NonNull::new(Box::leak(boxed)).map(Self)
304 }
305
306 pub(crate) unsafe fn from_raw(
315 ptr: *mut XmlAttr,
316 ) -> Result<Option<Self>, InvalidNodePointerCastError> {
317 unsafe {
318 if ptr.is_null() {
319 return Ok(None);
320 }
321 match (*ptr).element_type() {
322 XmlElementType::XmlAttributeNode => Ok(Some(Self(NonNull::new_unchecked(ptr)))),
323 _ => Err(InvalidNodePointerCastError {
324 from: (*ptr).element_type(),
325 to: type_name::<Self>(),
326 }),
327 }
328 }
329 }
330
331 pub(crate) unsafe fn free(self) {
341 unsafe {
342 let _ = *Box::from_raw(self.0.as_ptr());
343 }
344 }
345
346 #[doc(alias = "xmlRemoveProp")]
362 pub unsafe fn remove_prop(self) -> i32 {
363 unsafe {
364 let Some(mut parent) = self
365 .parent()
366 .map(|parent| XmlNodePtr::try_from(parent).unwrap())
367 else {
368 return -1;
369 };
370 let mut tmp = parent.properties;
371 if tmp == Some(self) {
372 parent.properties = self.next;
373 if let Some(mut next) = self.next {
374 next.prev = None;
375 }
376 xml_free_prop(self);
377 return 0;
378 }
379 while let Some(mut now) = tmp {
380 if now.next == Some(self) {
381 now.next = self.next;
382 if let Some(mut next) = now.next {
383 next.prev = Some(now);
384 }
385 xml_free_prop(self);
386 return 0;
387 }
388 tmp = now.next;
389 }
390 -1
391 }
392 }
393}
394
395impl Clone for XmlAttrPtr {
396 fn clone(&self) -> Self {
397 *self
398 }
399}
400
401impl Copy for XmlAttrPtr {}
402
403impl Deref for XmlAttrPtr {
404 type Target = XmlAttr;
405 fn deref(&self) -> &Self::Target {
406 unsafe { self.0.as_ref() }
412 }
413}
414
415impl DerefMut for XmlAttrPtr {
416 fn deref_mut(&mut self) -> &mut Self::Target {
417 unsafe { self.0.as_mut() }
423 }
424}
425
426impl TryFrom<XmlGenericNodePtr> for XmlAttrPtr {
427 type Error = InvalidNodePointerCastError;
428
429 fn try_from(value: XmlGenericNodePtr) -> Result<Self, Self::Error> {
430 match value.element_type() {
431 XmlElementType::XmlAttributeNode => Ok(Self(value.0.cast())),
432 _ => Err(InvalidNodePointerCastError {
433 from: value.element_type(),
434 to: type_name::<Self>(),
435 }),
436 }
437 }
438}
439
440impl From<XmlAttrPtr> for XmlGenericNodePtr {
441 fn from(value: XmlAttrPtr) -> Self {
442 Self(value.0 as NonNull<dyn NodeCommon>)
443 }
444}
445
446impl From<XmlAttrPtr> for *mut XmlAttr {
447 fn from(value: XmlAttrPtr) -> Self {
448 value.0.as_ptr()
449 }
450}
451
452#[doc(alias = "xmlNewDocProp")]
460pub fn xml_new_doc_prop(
461 doc: Option<XmlDocPtr>,
462 name: &str,
463 value: Option<&str>,
464) -> Option<XmlAttrPtr> {
465 let Some(mut cur) = XmlAttrPtr::new(XmlAttr {
467 typ: XmlElementType::XmlAttributeNode,
468 name: name.into(),
469 doc,
470 ..Default::default()
471 }) else {
472 xml_tree_err_memory("building attribute");
473 return None;
474 };
475 if let Some(value) = value {
476 cur.children = doc.and_then(|doc| doc.get_node_list(value));
477 cur.last = None;
478
479 let mut tmp = cur.children();
480 while let Some(mut now) = tmp {
481 now.set_parent(Some(cur.into()));
482 if now.next().is_none() {
483 cur.set_last(Some(now));
484 }
485 tmp = now.next();
486 }
487 }
488
489 if let Some(register) = get_register_node_func() {
490 register(cur.into());
491 }
492
493 Some(cur)
494}
495
496pub(super) fn xml_new_prop_internal(
497 node: Option<XmlNodePtr>,
498 ns: Option<XmlNsPtr>,
499 name: &str,
500 value: Option<&str>,
501) -> Option<XmlAttrPtr> {
502 if node.is_some_and(|node| !matches!(node.element_type(), XmlElementType::XmlElementNode)) {
503 return None;
504 }
505
506 let Some(mut cur) = XmlAttrPtr::new(XmlAttr {
508 typ: XmlElementType::XmlAttributeNode,
509 parent: node,
510 ns,
511 name: name.into(),
512 ..Default::default()
513 }) else {
514 xml_tree_err_memory("building attribute");
515 return None;
516 };
517
518 let mut doc = None;
519 if let Some(node) = node {
520 doc = node.doc;
521 cur.doc = doc;
522 }
523
524 if let Some(value) = value {
525 cur.set_children(xml_new_doc_text(doc, Some(value)).map(|node| node.into()));
526 cur.set_last(None);
527 let mut tmp = cur.children();
528 while let Some(mut now) = tmp {
529 now.set_parent(Some(cur.into()));
530 if now.next().is_none() {
531 cur.set_last(Some(now));
532 }
533 tmp = now.next();
534 }
535 }
536
537 if let Some(mut node) = node {
539 if let Some(mut prev) = node.properties {
540 while let Some(next) = prev.next {
541 prev = next;
542 }
543 prev.next = Some(cur);
544 cur.prev = Some(prev);
545 } else {
546 node.properties = Some(cur);
547 }
548 }
549
550 if let Some(value) = value {
551 if let Some(node) = node {
552 if xml_is_id(node.doc, Some(node), Some(cur)) == 1 {
553 xml_add_id(None, node.doc.unwrap(), value, cur);
554 }
555 }
556 }
557
558 if let Some(register) = get_register_node_func() {
559 register(cur.into());
560 }
561
562 Some(cur)
563}
564
565#[doc(alias = "xmlNewProp")]
568#[cfg(any(feature = "libxml_tree", feature = "html", feature = "schema"))]
569pub fn xml_new_prop(
570 node: Option<XmlNodePtr>,
571 name: &str,
572 value: Option<&str>,
573) -> Option<XmlAttrPtr> {
574 xml_new_prop_internal(node, None, name, value)
575}
576
577#[doc(alias = "xmlNewNsProp")]
580pub fn xml_new_ns_prop(
581 node: Option<XmlNodePtr>,
582 ns: Option<XmlNsPtr>,
583 name: &str,
584 value: Option<&str>,
585) -> Option<XmlAttrPtr> {
586 xml_new_prop_internal(node, ns, name, value)
587}
588
589pub(super) unsafe fn xml_copy_prop_internal(
590 doc: Option<XmlDocPtr>,
591 target: Option<XmlNodePtr>,
592 cur: XmlAttrPtr,
593) -> Option<XmlAttrPtr> {
594 unsafe {
595 if target
596 .is_some_and(|target| !matches!(target.element_type(), XmlElementType::XmlElementNode))
597 {
598 return None;
599 }
600 let mut ret = if let Some(target) = target {
601 xml_new_doc_prop(target.doc, &cur.name, None)
602 } else if let Some(doc) = doc {
603 xml_new_doc_prop(Some(doc), &cur.name, None)
604 } else if let Some(parent) = cur.parent() {
605 xml_new_doc_prop(parent.document(), &cur.name, None)
606 } else if let Some(children) = cur.children() {
607 xml_new_doc_prop(children.document(), &cur.name, None)
608 } else {
609 xml_new_doc_prop(None, &cur.name, None)
610 }?;
611 ret.parent = target;
612
613 if let Some((cur_ns, mut target)) = cur.ns.zip(target) {
614 let prefix = cur_ns.prefix();
615 let target_doc = target.doc;
616 if let Some(ns) = target.search_ns(target_doc, prefix.as_deref()) {
617 if ns.href == cur_ns.href {
621 ret.ns = Some(ns);
623 } else {
624 ret.ns = xml_new_reconciled_ns(target.doc, target, cur_ns);
627 }
628 } else {
629 if let Some(ns) = cur.parent().unwrap().search_ns(cur.doc, prefix.as_deref()) {
633 let mut root = XmlGenericNodePtr::from(target);
634 let mut pred = None;
635
636 while let Some(parent) = root.parent() {
637 pred = Some(root);
638 root = parent;
639 }
640 ret.ns = if Some(root) == target.doc.map(|doc| doc.into()) {
641 xml_new_ns(
643 pred.map(|p| XmlNodePtr::try_from(p).unwrap()),
644 ns.href.as_deref(),
645 ns.prefix().as_deref(),
646 )
647 } else {
648 xml_new_ns(
649 Some(XmlNodePtr::try_from(root).unwrap()),
650 ns.href.as_deref(),
651 ns.prefix().as_deref(),
652 )
653 };
654 }
655 }
656 } else {
657 ret.ns = None;
658 }
659
660 if let Some(children) = cur.children() {
661 let doc = ret.doc;
662 let parent = Some(ret.into());
663 ret.set_children(xml_static_copy_node_list(Some(children), doc, parent));
664 ret.set_last(None);
665 let mut tmp = ret.children();
666 while let Some(now) = tmp {
667 if now.next().is_none() {
669 ret.set_last(Some(now));
670 }
671 tmp = now.next();
672 }
673 }
674 if let Some(target_doc) = target.and_then(|target| target.doc).filter(|_| {
676 cur.doc.is_some()
677 && cur
678 .parent
679 .filter(|&p| xml_is_id(cur.doc, Some(p), Some(cur)) != 0)
680 .is_some()
681 }) {
682 let children = cur.children();
683 if let Some(id) = children.and_then(|c| c.get_string(cur.doc, 1)) {
684 xml_add_id(None, target_doc, &id, ret);
685 }
686 }
687 Some(ret)
688 }
689}
690
691#[doc(alias = "xmlCopyProp")]
695pub unsafe fn xml_copy_prop(target: Option<XmlNodePtr>, cur: XmlAttrPtr) -> Option<XmlAttrPtr> {
696 unsafe { xml_copy_prop_internal(None, target, cur) }
697}
698
699#[doc(alias = "xmlCopyPropList")]
703pub unsafe fn xml_copy_prop_list(
704 target: Option<XmlNodePtr>,
705 mut cur: Option<XmlAttrPtr>,
706) -> Option<XmlAttrPtr> {
707 unsafe {
708 if target
709 .is_some_and(|target| !matches!(target.element_type(), XmlElementType::XmlElementNode))
710 {
711 return None;
712 }
713 let mut ret = None;
714 let mut p = None::<XmlAttrPtr>;
715 while let Some(now) = cur {
716 let Some(mut q) = xml_copy_prop(target, now) else {
717 xml_free_prop_list(ret);
718 return None;
719 };
720 if let Some(mut np) = p {
721 np.next = Some(q);
722 q.prev = Some(np);
723 p = Some(q);
724 } else {
725 ret = Some(q);
726 p = Some(q);
727 }
728 cur = now.next;
729 }
730 ret
731 }
732}
733
734#[doc(alias = "xmlFreeProp")]
736pub unsafe fn xml_free_prop(cur: XmlAttrPtr) {
737 unsafe {
738 if let Some(deregister) = get_deregister_node_func() {
739 deregister(cur.into());
740 }
741
742 if let Some(doc) = cur
744 .doc
745 .filter(|_| matches!(cur.atype, Some(XmlAttributeType::XmlAttributeID)))
746 {
747 xml_remove_id(doc, cur);
748 }
749 if let Some(children) = cur.children() {
750 xml_free_node_list(Some(children));
751 }
752 cur.free();
753 }
754}
755
756#[doc(alias = "xmlFreePropList")]
758pub unsafe fn xml_free_prop_list(cur: Option<XmlAttrPtr>) {
759 unsafe {
760 if let Some(cur) = cur {
761 let mut next = cur.next;
762 xml_free_prop(cur);
763 while let Some(now) = next {
764 next = now.next;
765 xml_free_prop(now);
766 }
767 }
768 }
769}