KWARC / rust-libxml

Rust wrapper for libxml2
https://crates.io/crates/libxml
MIT License
76 stars 38 forks source link

creating a text node after add_next_sibling and then also trying to add_next_sibling() it causes mutably shared node Err #99

Open insiderscore-jeff opened 2 years ago

insiderscore-jeff commented 2 years ago

This is on 0.3. So the scenario is appending a newly created text node, after you call add_next_sibling, and then call add_next_sibling with that causes a mutable reference Err. Replacing the text nodes with "real" nodes works correctly. Could be some underlying libxml muckery when merging adjacent text nodes? I somewhat remember a long while ago running into a bug in the perl libxml wrapper in that ballpark.

You could ask "why would you insert another text node rather than append_data?" which is a valid question, but this should still work, or at least give a more concise error as to what's up

the fact the first 2 add_next_sibling work fine, it is only after creating another text node after the add_next_sibling call where this falls apart.

thanks!

use libxml::parser::Parser; 
use libxml::tree::*;

fn xml_nodes_works()
{
    println!("xml_nodes_works");

    let mut d = Document::new().unwrap();

    let mut div = Node::new("DIV", None, &d).unwrap();
    d.set_root_element(&div);

    let mut n = Node::new("SPAN", None, &d).unwrap();
    let mut nt = Node::new("SPAN", None, &d).unwrap();
    let mut nt2 = Node::new("SPAN", None, &d).unwrap();

    // add text
    div.add_child(&mut n).unwrap();

    // lets skip the fact it is questionable why I'd be adding multiple text nodes

    // but is it because we mutate it twice.
    n.add_next_sibling(&mut nt).unwrap(); // ok
    n.add_next_sibling(&mut nt2).unwrap(); // ok

    println!("{}", d.node_to_string(&div)); // "n nt nt2 "

    let mut nt4 = Node::new("SPAN", None, &d).unwrap();
    n.add_next_sibling(&mut nt4).unwrap(); // works.

    println!("{}", d.node_to_string(&div)); // "n nt nt2 "
}

fn xml_text_broken()
{
    println!("xml_text_broken");

    let mut d = Document::new().unwrap();

    let mut div = Node::new("DIV", None, &d).unwrap();
    d.set_root_element(&div);

    let mut n = Node::new_text("n ", &d).unwrap();
    let mut nt = Node::new_text("nt ", &d).unwrap();
    let mut nt2 = Node::new_text("nt2 ", &d).unwrap();

    // add text
    div.add_child(&mut n).unwrap();

    // lets skip the fact it is questionable why I'd be adding multiple text nodes like this..

    n.add_next_sibling(&mut nt).unwrap(); // ok
    n.add_next_sibling(&mut nt2).unwrap(); // ok

    println!("{}", d.node_to_string(&div)); // "n nt nt2 "

    let mut nt4 = Node::new_text("nt4 ", &d).unwrap();
    n.add_next_sibling(&mut nt4).unwrap(); // panic! with reference error.

    println!("{}", d.node_to_string(&n));
}

fn main() {

    xml_nodes_works();
    xml_text_broken();
}

output:

<DIV><SPAN/><SPAN/><SPAN/></DIV>
<DIV><SPAN/><SPAN/><SPAN/><SPAN/></DIV>
xml_text_broken
<DIV>n nt nt2 </DIV>
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: "Can not mutably reference a shared Node \"text\"! Rc: weak count: 0; strong count: 3"', src/main.rs:61:34
insiderscore-jeff commented 2 years ago

Yep - so there's a case where if you call add_next_sibling with 2 text node's it converts into an append:

fn xml_test_merge()
{

    let mut d = Document::new().unwrap();

    let mut div = Node::new("DIV", None, &d).unwrap();
    d.set_root_element(&div);

    let mut n = Node::new_text("n ", &d).unwrap();
    let mut nt = Node::new_text("nt ", &d).unwrap();
    let mut nt2 = Node::new_text("nt2 ", &d).unwrap();

    // add text
    div.add_child(&mut n).unwrap();

    // lets skip the fact it is questionable why I'd be adding multiple text nodes like this..

    // but is it because we mutate it twice.
    n.add_next_sibling(&mut nt).unwrap(); // ok
    n.add_next_sibling(&mut nt2).unwrap(); // ok

    // lets look at n's content vs 
    println!("N: {}", n.get_content());     // and that it didn't actually add a sibling

    let ns = n.get_next_sibling().unwrap();

}

output:

     Running `target/debug/xmlbug`
N: n nt nt2 
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', src/main.rs:90:35