// Read a Ksudoku's XML save file. use std::collections::HashMap; use std::error; use std::fmt; use std::fs::File; use std::io::BufReader; use xml::reader::{EventReader, XmlEvent}; use xml::writer::XmlEvent as WrXmlEvent; use xml::EmitterConfig; #[derive(Debug, Clone)] struct UnsupportedGroup { message: String, } impl fmt::Display for UnsupportedGroup { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Unsupported group: {}", self.message) } } impl error::Error for UnsupportedGroup {} pub fn load_ksudoku(filename: std::path::PathBuf) -> Result> { let fh = File::open(filename)?; let buffer = BufReader::new(fh); let parser = EventReader::new(buffer); // I'm interested in values and (maybe) solution let mut values: String = String::from(""); let mut element_name: String = String::from(""); for e in parser { match e { Ok(XmlEvent::StartElement { name, attributes, .. }) => { element_name = name.local_name.clone(); if element_name == "graph" { // Check the attributes here // // Verify these are correct / not some other puzzle type! // {"specific-type": "Plain", "type": "sudoku", "order": "9"} let mut attrs: HashMap = HashMap::new(); for a in attributes { attrs.insert(a.name.to_string(), a.value); } let blank = String::new(); // Format in specific order here. let expected = format!( "{}-{}-{}", attrs.get(&"specific-type".to_string()).unwrap_or(&blank), attrs.get(&"type".to_string()).unwrap_or(&blank), attrs.get(&"order".to_string()).unwrap_or(&blank) ); if expected != "Plain-sudoku-9" { // println!("Unknown ksudoku type! {}", expected); return Err(Box::new(UnsupportedGroup { message: format!("Unsupported Ksudoku game type: {}", expected), })); } println!("{:?}", attrs); } } Ok(XmlEvent::Characters(text)) => { if element_name == "values" { values = text.clone(); } } Err(e) => { return Err(Box::new(e)); } _ => {} } } Ok(values) } pub fn save_ksudoku( filename: std::path::PathBuf, puz: &String, sol: &String, ) -> Result<(), Box> { let fh = File::create(filename)?; let mut writer = EmitterConfig::new().perform_indent(true).create_writer(fh); let mut event: WrXmlEvent = WrXmlEvent::start_element("ksudoku").into(); writer.write(event)?; event = WrXmlEvent::start_element("game") .attr("had-help", "0") .attr("msecs-elapsed", "0") .into(); writer.write(event)?; event = WrXmlEvent::start_element("puzzle").into(); writer.write(event)?; event = WrXmlEvent::start_element("graph") .attr("specific-type", "Plain") .attr("type", "sudoku") .attr("order", "9") .into(); writer.write(event)?; event = WrXmlEvent::end_element().into(); writer.write(event)?; // values (puzzle) event = WrXmlEvent::start_element("values").into(); writer.write(event)?; event = WrXmlEvent::characters(puz).into(); writer.write(event)?; event = WrXmlEvent::end_element().into(); writer.write(event)?; // solution event = WrXmlEvent::start_element("solution").into(); writer.write(event)?; event = WrXmlEvent::characters(sol).into(); writer.write(event)?; event = WrXmlEvent::end_element().into(); writer.write(event)?; // Apparently, the events are consumed... event = WrXmlEvent::end_element().into(); writer.write(event)?; event = WrXmlEvent::end_element().into(); writer.write(event)?; event = WrXmlEvent::end_element().into(); writer.write(event)?; /* let pStr: String = puzzle.save_to_tld(start_ch: 'b', blank: '_'); let solStr: solution.save_to_tld(start_ch: 'b', blank: '_'); let ksudoko = Ksudoku { game: Game { help: 0, elapsed: 0, puzzle: Puzzle { graph: Graph { order: 9, game_type: String::from("sudoku"), specific_type: String::from("Plain"), }, values: puz.to_string(), solution: sol.to_string(), }, }, }; let fh = File::create(filename)?; let _res = to_writer(fh, &ksudoko)?; */ Ok(()) }