quick-xml-0.17.2/.gitattributes010064400017500001750000000001261332055316700146330ustar0000000000000000# Unit tests assume that all xml files have unix style line endings *.xml text eol=cr quick-xml-0.17.2/.gitignore010064400017500001750000000000331332055316700137250ustar0000000000000000target .project Cargo.lock quick-xml-0.17.2/.travis.yml010064400017500001750000000006331357061306100140510ustar0000000000000000language: rust cache: cargo sudo: false rust: - nightly - beta - stable before_script: - export PATH=$HOME/.cargo/bin:$HOME/.local/bin:$PATH - if [[ $(rustup show active-toolchain) == stable* ]]; then rustup component add rustfmt; fi; script: - if [[ $(rustup show active-toolchain) == stable* ]]; then cargo fmt -- --check; fi; - cargo test --all-features - cargo test --no-default-features quick-xml-0.17.2/Cargo.toml.orig010064400017500001750000000014451357610663100146370ustar0000000000000000[package] name = "quick-xml" version = "0.17.2" authors = ["Johann Tuffe "] description = "High performance xml reader and writer" documentation = "https://docs.rs/quick-xml" repository = "https://github.com/tafia/quick-xml" readme = "README.md" keywords = ["xml", "reader", "parser", "writer", "html"] categories = ["encoding", "parsing", "text-processing"] license = "MIT" [badges] travis-ci = { repository = "tafia/quick-xml" } [dependencies] encoding_rs = { version = "0.8.17", optional = true } serde = { version = "1.0", optional = true } memchr = "2.2.1" [dev-dependencies] serde = { version = "1.0", features = ["derive"] } [lib] bench = false [features] default = [] encoding = ["encoding_rs"] serialize = ["serde"] [package.metadata.docs.rs] features = ["serialize"] quick-xml-0.17.2/Cargo.toml0000644000000024710000000000000110760ustar00# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] name = "quick-xml" version = "0.17.2" authors = ["Johann Tuffe "] description = "High performance xml reader and writer" documentation = "https://docs.rs/quick-xml" readme = "README.md" keywords = ["xml", "reader", "parser", "writer", "html"] categories = ["encoding", "parsing", "text-processing"] license = "MIT" repository = "https://github.com/tafia/quick-xml" [package.metadata.docs.rs] features = ["serialize"] [lib] bench = false [dependencies.encoding_rs] version = "0.8.17" optional = true [dependencies.memchr] version = "2.2.1" [dependencies.serde] version = "1.0" optional = true [dev-dependencies.serde] version = "1.0" features = ["derive"] [features] default = [] encoding = ["encoding_rs"] serialize = ["serde"] [badges.travis-ci] repository = "tafia/quick-xml" quick-xml-0.17.2/Cargo.toml.orig0000644000000014450000000000000120350ustar00[package] name = "quick-xml" version = "0.17.2" authors = ["Johann Tuffe "] description = "High performance xml reader and writer" documentation = "https://docs.rs/quick-xml" repository = "https://github.com/tafia/quick-xml" readme = "README.md" keywords = ["xml", "reader", "parser", "writer", "html"] categories = ["encoding", "parsing", "text-processing"] license = "MIT" [badges] travis-ci = { repository = "tafia/quick-xml" } [dependencies] encoding_rs = { version = "0.8.17", optional = true } serde = { version = "1.0", optional = true } memchr = "2.2.1" [dev-dependencies] serde = { version = "1.0", features = ["derive"] } [lib] bench = false [features] default = [] encoding = ["encoding_rs"] serialize = ["serde"] [package.metadata.docs.rs] features = ["serialize"] quick-xml-0.17.2/Changelog.md010064400017500001750000000161341357610661700141660ustar0000000000000000> Legend: - feat: A new feature - fix: A bug fix - docs: Documentation only changes - style: White-space, formatting, missing semi-colons, etc - refactor: A code change that neither fixes a bug nor adds a feature - perf: A code change that improves performance - test: Adding missing tests - chore: Changes to the build process or auxiliary tools/libraries/documentation ## 0.17.2 - feat: add Seq to serializer - docs: update readme with example for `$value` ## 0.17.1 - feat: add new `serialize` feature to support serde serialize/deserialize ## 0.17.0 - perf: speed up (un)escape a little - feat: remove failure completely (breaking change) and implement `std::error::Error` for `Error` - feat: improve `Debug`s for `Attribute`, `BytesStart`, `BytesEnd`, `BytesText` ## 0.16.1 - refactor: remove derive_more dependency (used only in 2 structs) - refactor: move xml-rs bench dependency into another local crate ## 0.16.0 - feat: (breaking change) set failure and encoding_rs crates as optional. You should now use respectively `use-failure` and `encoding` features to get the old behavior - perf: improve perf using memchr3 iterator. Reading is 18% better on benches ## 0.15.0 - feat: remove Seek bound - style: rustfmt ## 0.14.0 - feat: make failure error crate optional. To revert back to old behavior, use the `--failure` feature. ## 0.13.3 - feat: allow changing name without deallocating `BytesStart` buffer - feat: add standard error type conversion ## 0.13.2 - fix: allow whitespace in End events - feat: bump dependencies ## 0.13.1 - feat: Add into_underlying_reader method for `Reader` ## 0.13.0 - feat: rename `resolve_namespace` into `attribute_namespace` - feat: add a `event_namespace` fn ## 0.12.4 - fix: Fix minor bug for parsing comment tag ## 0.12.3 - feat: add `BytesStart::{owned_name, borrowed_name}` ## 0.12.2 - refactor: bump dependencies - test: fix travis ## 0.12.1 - feat: enable `into_owned` for all events ## 0.12.0 - feat: rename BytesText fn to better clarify escape intents - docs: various improvements! ## 0.11.0 - feat: migrate from error-chain to failure - feat: allow html style attribute iterators - feat: add optional identation on writer - refactor: remove unecessary derive impl ## 0.10.1 - fix: overflow possibility when parsing Ascii codes ## 0.10.0 - feat: update dependencies - doc: add doc for attribute creation functions - fix: escape attributes - fix: avoid double escapes ## 0.9.4 - fix: bound tests in `read_bang` fn. ## 0.9.3 - fix: escape was panicking at the 3rd character escaped. ## 0.9.2 - perf: update to encoding_rs 0.7.0, supposedly faster for utf8 - style: rustfmt-nightly ## 0.9.1 - perf: use memchr crate and rewrite some loops with iterators - docs: remove duplicate `Reader` doc in lib.rs ## 0.9.0 - feat: add getter for encoding to reader - feat: escape Text events on write (breaking change) ## 0.8.1 - feat: allow `Writer` to borrow `Event` (using `AsRef`) ## 0.8.0 - fix: make the reader borrow the namespace buffer so it can be used repetitively - refactor: bump dependencies ## 0.7.3 - fix: fix Event::Text slice always starting at the beginning of the buffer ## 0.7.2 - perf: faster unescape method - docs: update readme - refactor bump encoding_rs to 0.6.6 ## 0.7.1 - style: rustfmt - refactor: remove from_ascii crate dependency ## 0.7.0 - style: rustfmt - fix: {with,extend}_attributes usage - feat: add naive `local_name` function ## 0.6.2 - fix: another overflow bug found with cargo-fuzz - refactor: update dependencies ## 0.6.1 - fix: fix an overflow found with cargo-fuzz ## 0.6.0 Major refactoring. Breaks most of existing functionalities - refactor: replace `XmlReader` with a non allocating `Reader` (uses an external buffer) - refactor: replace `XmlnsReader` iterator by a simpler `Reader::read_namespaced_event` function - refactor: replace `UnescapedAttribute` with a new `Attribute` struct with `unescape` functions - feat: support xml decodings - refactor: remove the `AsStr` trait: user must use `unescape_and_decode` fns when necessary (alternatively, run `unescape` and/or `Reader::decode`) - refactor: module hierarchies - refactor: replace `Element`s with several per event structs `BytesStart` - perf: unescape: use from-ascii crate instead to get ascii codes without string validation - refactor: rename `XmlWriter` to `Writer` and provide a way to write `&[u8]` directly - refactor: adds @vandenoever changes to save some namespaces allocations - refactor: adds error-chain and remove `ResultPos` (user can still use `Reader::buffer_position` if needed) ## 0.5.0 - feat: apply default namespaces (`xmlns="..."`) to unqualified elements - fix: scope for namespace resolution on empty elements - fix: parsing of `>` in attribute values ## 0.4.2 - feat: add `into_unescaped_string` - refactor: remove RustyXML benches - docs: redirect to docs.rs for documentation - docs: add examples in lib.rs ## 0.4.1 - feat: add `read_text_unescaped` - fix: fix tests ## 0.4.0 - fix: fix attributes with `=` character in their value - perf: inline some local functions ## 0.3.1 - feat: set default to `expand_empty_elements = true` - fix: fix all broken tests because of `Empty` events ## 0.2.5 - 0.3.0 (yanked) - feat: Add support for `Empty` event ## 0.2.4 - test: add most tests from xml-rs crate ## 0.2.3 - fix: do not write attributes on `Event::End` ## 0.2.2 - refactor: code refactoring, split largest functions into smaller ones - refactor: use `Range` instead of `usize`s in `Element` definition - docs: fix typo ## 0.2.1 - feat: add `Clone` to more structs - style: apply rustfmt ## 0.2.0 - refactor: change `from_str` into impl `From<&str>` - feat: support `Event::DocType` - feat: add `.check_comments` to check for invalid double dashes (`--`) in comments - fix: check that all attributes are distincts ## v0.1.9 - feat: return more precise index when erroring - feat: have `Attributes` iterate ResultPos instead of `Result` - feat: provide functions to unescape `&...;` characters (`.escaped_content` and `.escaped_attributes`) - fix: have namespace resolution start one level higher ## v0.1.8 - feat: add `XmlnsReader` to iterate event and resolve namespaces! - docs: better documentation (in particular regarding `Element` structure and design) - test: add benchmarks, with xml-rs for a reference ## 0.1.7 - feat/fix: add `Event::PI` to manage processing instructions (``) - test: add test with a sample file ## 0.1.6 - feat: parse `Event::Decl` for xml declaration so we can have `version`, `encoding` ... - refactor: rename `position` into `buffer_position` because it sometimes conflicted with `Iterator::position` - test: add test for buffer_position ## 0.1.5 - feat: add buffer position when erroring to help debuging (return `ResultPos` instead of `Result`) - test: add travis CI - docs: add merrit badge and travis status ## 0.1.4 - feat: improve Element API with new, with_attributes, push_attribute - feat: always return raw `&[u8]` and add a `AsStr` trait for conversion ## 0.1.3 - feat: add helper functions - feat: add `XmlWriter` to write/modify xmls - feat: use `AsRef<[u8]>` when possible ## 0.1.2 - 0.1.1 - test: add tests - feat: add `with_check` quick-xml-0.17.2/LICENSE-MIT.md010064400017500001750000000020721332055316700137750ustar0000000000000000The MIT License (MIT) Copyright (c) 2016 Johann Tuffe Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. quick-xml-0.17.2/README.md010064400017500001750000000214501357605244400132270ustar0000000000000000# quick-xml [![Build Status](https://travis-ci.org/tafia/quick-xml.svg?branch=master)](https://travis-ci.org/tafia/quick-xml) [![Crate](http://meritbadge.herokuapp.com/quick-xml)](https://crates.io/crates/quick-xml) High performance xml pull reader/writer. The reader: - is almost zero-copy (use of `Cow` whenever possible) - is easy on memory allocation (the API provides a way to reuse buffers) - support various encoding (with `encoding` feature), namespaces resolution, special characters. [docs.rs](https://docs.rs/quick-xml) Syntax is inspired by [xml-rs](https://github.com/netvl/xml-rs). ## Example ### Reader ```rust use quick_xml::Reader; use quick_xml::events::Event; let xml = r#" Test Test 2 "#; let mut reader = Reader::from_str(xml); reader.trim_text(true); let mut count = 0; let mut txt = Vec::new(); let mut buf = Vec::new(); // The `Reader` does not implement `Iterator` because it outputs borrowed data (`Cow`s) loop { match reader.read_event(&mut buf) { Ok(Event::Start(ref e)) => { match e.name() { b"tag1" => println!("attributes values: {:?}", e.attributes().map(|a| a.unwrap().value).collect::>()), b"tag2" => count += 1, _ => (), } }, Ok(Event::Text(e)) => txt.push(e.unescape_and_decode(&reader).unwrap()), Ok(Event::Eof) => break, // exits the loop when reaching end of file Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e), _ => (), // There are several other `Event`s we do not consider here } // if we don't keep a borrow elsewhere, we can clear the buffer to keep memory usage low buf.clear(); } ``` ### Writer ```rust use quick_xml::Writer; use quick_xml::Reader; use quick_xml::events::{Event, BytesEnd, BytesStart}; use std::io::Cursor; use std::iter; let xml = r#"text"#; let mut reader = Reader::from_str(xml); reader.trim_text(true); let mut writer = Writer::new(Cursor::new(Vec::new())); let mut buf = Vec::new(); loop { match reader.read_event(&mut buf) { Ok(Event::Start(ref e)) if e.name() == b"this_tag" => { // crates a new element ... alternatively we could reuse `e` by calling // `e.into_owned()` let mut elem = BytesStart::owned(b"my_elem".to_vec(), "my_elem".len()); // collect existing attributes elem.extend_attributes(e.attributes().map(|attr| attr.unwrap())); // copy existing attributes, adds a new my-key="some value" attribute elem.push_attribute(("my-key", "some value")); // writes the event to the writer assert!(writer.write_event(Event::Start(elem)).is_ok()); }, Ok(Event::End(ref e)) if e.name() == b"this_tag" => { assert!(writer.write_event(Event::End(BytesEnd::borrowed(b"my_elem"))).is_ok()); }, Ok(Event::Eof) => break, // you can use either `e` or `&e` if you don't want to move the event Ok(e) => assert!(writer.write_event(&e).is_ok()), Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e), } buf.clear(); } let result = writer.into_inner().into_inner(); let expected = r#"text"#; assert_eq!(result, expected.as_bytes()); ``` ## Serde When using the `serialize` feature, quick-xml can be used with serde's `Serialize`/`Deserialize` traits. Here is an example deserializing crates.io source: ```rust // Cargo.toml // [dependencies] // serde = { version = "1.0", features = [ "derive" ] } // quick_xml = { version = "0.17", features = [ "serialize" ] } extern crate serde; extern crate quick_xml; use serde::Deserialize; use quick_xml::de::{from_str, DeError}; #[derive(Debug, Deserialize, PartialEq)] struct Link { rel: String, href: String, sizes: Option, } #[derive(Debug, Deserialize, PartialEq)] #[serde(rename_all = "lowercase")] enum Lang { En, Fr, De, } #[derive(Debug, Deserialize, PartialEq)] struct Head { title: String, #[serde(rename = "link", default)] links: Vec, } #[derive(Debug, Deserialize, PartialEq)] struct Script { src: String, integrity: String, } #[derive(Debug, Deserialize, PartialEq)] struct Body { #[serde(rename = "script", default)] scripts: Vec }"; let html: Html = from_str(xml)?; assert_eq!(&html.head.title, "crates.io: Rust Package Registr"); Ok(html) } ``` ### Credits This has largely been inspired by [serde-xml-rs](https://github.com/RReverser/serde-xml-rs). quick-xml follows its convention for deserialization, including the [`$value`](https://github.com/RReverser/serde-xml-rs#parsing-the-value-of-a-tag) special name. ### Parsing the "value" of a tag If you have an input of the form `bar`, and you want to get at the `bar`, you can use the special name `$value`: ```rust,ignore struct Foo { pub abc: String, #[serde(rename = "$value")] pub body: String, } ``` ### Performance Note that despite not focusing on performance (there are several unecessary copies), it remains about 10x faster than serde-xml-rs. # Features - `encoding`: support non utf8 xmls - `serialize`: support serde `Serialize`/`Deserialize` ## Performance Benchmarking is hard and the results depend on your input file and your machine. Here on my particular file, quick-xml is around **50 times faster** than [xml-rs](https://crates.io/crates/xml-rs) crate. ``` // quick-xml benches test bench_quick_xml ... bench: 198,866 ns/iter (+/- 9,663) test bench_quick_xml_escaped ... bench: 282,740 ns/iter (+/- 61,625) test bench_quick_xml_namespaced ... bench: 389,977 ns/iter (+/- 32,045) // same bench with xml-rs test bench_xml_rs ... bench: 14,468,930 ns/iter (+/- 321,171) // serde-xml-rs vs serialize feature test bench_serde_quick_xml ... bench: 1,181,198 ns/iter (+/- 138,290) test bench_serde_xml_rs ... bench: 15,039,564 ns/iter (+/- 783,485) ``` For a feature and performance comparison, you can also have a look at RazrFalcon's [choose-your-xml-rs](https://github.com/RazrFalcon/choose-your-xml-rs). ## Contribute Any PR is welcomed! ## License MIT quick-xml-0.17.2/examples/issue68.rs010064400017500001750000000072351332061054700154360ustar0000000000000000#![allow(unused)] extern crate quick_xml; use quick_xml::events::Event; use quick_xml::Reader; use std::io::Read; struct Resource { etag: String, calendar_data: String, } struct Prop { namespace: String, local_name: String, value: String, } impl Prop { fn new() -> Prop { Prop { namespace: String::new(), local_name: String::new(), value: String::new(), } } } struct PropStat { status: String, props: Vec, } impl PropStat { fn new() -> PropStat { PropStat { status: String::new(), props: Vec::::new(), } } } struct Response { href: String, propstats: Vec, } impl Response { fn new() -> Response { Response { href: String::new(), propstats: Vec::::new(), } } } fn parse_report(xml_data: &str) -> Vec { let result = Vec::::new(); let mut reader = Reader::from_str(xml_data); reader.trim_text(true); let mut count = 0; let mut buf = Vec::new(); let mut ns_buffer = Vec::new(); #[derive(Clone, Copy)] enum State { Root, MultiStatus, Response, Success, Error, }; let mut responses = Vec::::new(); let mut current_response = Response::new(); let mut current_prop = Prop::new(); let mut depth = 0; let mut state = State::MultiStatus; loop { match reader.read_namespaced_event(&mut buf, &mut ns_buffer) { Ok((namespace_value, Event::Start(e))) => { let namespace_value = namespace_value.unwrap_or_default(); match (depth, state, namespace_value, e.local_name()) { (0, State::Root, b"DAV:", b"multistatus") => state = State::MultiStatus, (1, State::MultiStatus, b"DAV:", b"response") => { state = State::Response; current_response = Response::new(); } (2, State::Response, b"DAV:", b"href") => { current_response.href = e.unescape_and_decode(&reader).unwrap(); } _ => {} } depth += 1; } Ok((namespace_value, Event::End(e))) => { let namespace_value = namespace_value.unwrap_or_default(); let local_name = e.local_name(); match (depth, state, &*namespace_value, local_name) { (1, State::MultiStatus, b"DAV:", b"multistatus") => state = State::Root, (2, State::MultiStatus, b"DAV:", b"multistatus") => state = State::MultiStatus, _ => {} } depth -= 1; } Ok((_, Event::Eof)) => break, Err(e) => break, _ => (), } } result } fn main() { let test_data = r#" /caldav/v2/johndoh%40gmail.com/events/07b7it7uonpnlnvjldr0l1ckg8%40google.com.ics HTTP/1.1 200 OK "63576798396" BEGIN:VCALENDAR "#; parse_report(test_data); } quick-xml-0.17.2/examples/read_texts.rs010064400017500001750000000017621332061054700162710ustar0000000000000000extern crate quick_xml; fn main() { use quick_xml::events::Event; use quick_xml::Reader; let xml = "text1text2\ text3text4"; let mut reader = Reader::from_str(xml); reader.trim_text(true); let mut txt = Vec::new(); let mut buf = Vec::new(); loop { match reader.read_event(&mut buf) { Ok(Event::Start(ref e)) if e.name() == b"tag2" => { txt.push( reader .read_text(b"tag2", &mut Vec::new()) .expect("Cannot decode text value"), ); println!("{:?}", txt); } Ok(Event::Eof) => break, // exits the loop when reaching end of file Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e), _ => (), // There are several other `Event`s we do not consider here } buf.clear(); } } quick-xml-0.17.2/src/de/escape.rs010064400017500001750000000152311357406636400147410ustar0000000000000000//! Serde `Deserializer` module use crate::{errors::serialize::DeError, errors::Error, escape::unescape, reader::Decoder}; use serde::de::{self, Visitor}; use serde::{self, forward_to_deserialize_any}; use std::borrow::Cow; /// A deserializer for a xml escaped and encoded value /// /// # Note /// /// Escaping the value is actually not always necessary, for instance /// when converting to float, we don't expect any escapable character /// anyway #[derive(Clone)] pub(crate) struct EscapedDeserializer { decoder: Decoder, escaped_value: Vec, escaped: bool, } impl EscapedDeserializer { pub fn new(escaped_value: Vec, decoder: Decoder, escaped: bool) -> Self { EscapedDeserializer { decoder, escaped_value, escaped, } } fn unescaped(&self) -> Result, DeError> { if self.escaped { unescape(&self.escaped_value).map_err(|e| DeError::Xml(Error::EscapeError(e))) } else { Ok(Cow::Borrowed(&self.escaped_value)) } } } macro_rules! deserialize_num { ($method:ident, $visit:ident) => { fn $method(self, visitor: V) -> Result where V: Visitor<'de>, { #[cfg(not(feature = "encoding"))] let value = self.decoder.decode(&self.escaped_value)?.parse()?; #[cfg(feature = "encoding")] let value = self.decoder.decode(&self.escaped_value).parse()?; visitor.$visit(value) } } } impl<'de> serde::Deserializer<'de> for EscapedDeserializer { type Error = DeError; fn deserialize_any(self, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_str(visitor) } fn deserialize_str(self, visitor: V) -> Result where V: Visitor<'de>, { let unescaped = self.unescaped()?; #[cfg(not(feature = "encoding"))] let value = self.decoder.decode(&unescaped)?; #[cfg(feature = "encoding")] let value = self.decoder.decode(&unescaped); visitor.visit_str(&value) } fn deserialize_bytes(self, visitor: V) -> Result where V: Visitor<'de>, { let v = self.unescaped()?; visitor.visit_bytes(&v) } fn deserialize_byte_buf(self, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_bytes(visitor) } fn deserialize_string(self, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_str(visitor) } fn deserialize_bool(self, visitor: V) -> Result where V: Visitor<'de>, { #[cfg(feature = "encoding")] { #[cfg(feature = "encoding")] let value = self.decoder.decode(&self.escaped_value); match value.as_ref() { "true" | "1" | "True" | "TRUE" | "t" | "Yes" | "YES" | "yes" | "y" => { visitor.visit_bool(true) } "false" | "0" | "False" | "FALSE" | "f" | "No" | "NO" | "no" | "n" => { visitor.visit_bool(false) } _ => Err(DeError::InvalidBoolean(value.into())), } } #[cfg(not(feature = "encoding"))] { match &*self.escaped_value { b"true" | b"1" | b"True" | b"TRUE" | b"t" | b"Yes" | b"YES" | b"yes" | b"y" => { visitor.visit_bool(true) } b"false" | b"0" | b"False" | b"FALSE" | b"f" | b"No" | b"NO" | b"no" | b"n" => { visitor.visit_bool(false) } e => Err(DeError::InvalidBoolean(self.decoder.decode(e)?.into())), } } } fn deserialize_char(self, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_str(visitor) } fn deserialize_unit(self, visitor: V) -> Result where V: Visitor<'de>, { if self.escaped_value.is_empty() { visitor.visit_unit() } else { Err(DeError::InvalidUnit( "Expecting unit, got non empty attribute".into(), )) } } fn deserialize_option(self, visitor: V) -> Result where V: Visitor<'de>, { if self.escaped_value.is_empty() { visitor.visit_none() } else { visitor.visit_some(self) } } fn deserialize_enum>( self, _name: &str, _variants: &'static [&'static str], visitor: V, ) -> Result { visitor.visit_enum(self) } fn deserialize_newtype_struct( self, _name: &'static str, visitor: V, ) -> Result where V: Visitor<'de>, { visitor.visit_newtype_struct(self) } deserialize_num!(deserialize_i64, visit_i64); deserialize_num!(deserialize_i32, visit_i32); deserialize_num!(deserialize_i16, visit_i16); deserialize_num!(deserialize_i8, visit_i8); deserialize_num!(deserialize_u64, visit_u64); deserialize_num!(deserialize_u32, visit_u32); deserialize_num!(deserialize_u16, visit_u16); deserialize_num!(deserialize_u8, visit_u8); deserialize_num!(deserialize_f64, visit_f64); deserialize_num!(deserialize_f32, visit_f32); forward_to_deserialize_any! { unit_struct seq tuple tuple_struct map struct identifier ignored_any } } impl<'de> de::EnumAccess<'de> for EscapedDeserializer { type Error = DeError; type Variant = Self; fn variant_seed>( self, seed: V, ) -> Result<(V::Value, Self), DeError> { let name = seed.deserialize(self.clone())?; Ok((name, self)) } } impl<'de> de::VariantAccess<'de> for EscapedDeserializer { type Error = DeError; fn unit_variant(self) -> Result<(), DeError> { Ok(()) } fn newtype_variant_seed>( self, seed: T, ) -> Result { seed.deserialize(self) } fn tuple_variant>( self, _len: usize, _visitor: V, ) -> Result { unimplemented!() } fn struct_variant>( self, _fields: &'static [&'static str], _visitor: V, ) -> Result { unimplemented!() } } quick-xml-0.17.2/src/de/map.rs010064400017500001750000000061771357406636400142670ustar0000000000000000//! Serde `Deserializer` module use crate::{ de::{escape::EscapedDeserializer, Deserializer, INNER_VALUE}, errors::serialize::DeError, events::{attributes::Attribute, BytesStart, Event}, }; use serde::de::{self, DeserializeSeed, IntoDeserializer}; use std::io::BufRead; enum MapValue { Empty, Attribute { value: Vec }, Nested, InnerValue, } /// A deserializer for `Attributes` pub(crate) struct MapAccess<'a, R: BufRead> { start: BytesStart<'static>, de: &'a mut Deserializer, position: usize, value: MapValue, } impl<'a, R: BufRead> MapAccess<'a, R> { /// Create a new MapAccess pub fn new(de: &'a mut Deserializer, start: BytesStart<'static>) -> Result { let position = start.attributes().position; Ok(MapAccess { de, start, position, value: MapValue::Empty, }) } fn next_attr(&mut self) -> Result, DeError> { let mut attributes = self.start.attributes(); attributes.position = self.position; let next_att = attributes.next(); self.position = attributes.position; Ok(next_att.transpose()?) } } impl<'a, 'de, R: BufRead> de::MapAccess<'de> for MapAccess<'a, R> { type Error = DeError; fn next_key_seed>( &mut self, seed: K, ) -> Result, Self::Error> { let attr_key_val = self .next_attr()? .map(|a| (a.key.to_owned(), a.value.into_owned())); let decoder = self.de.reader.decoder(); let has_value_field = self.de.has_value_field; if let Some((key, value)) = attr_key_val { // try getting map from attributes (key= "value") self.value = MapValue::Attribute { value }; seed.deserialize(EscapedDeserializer::new(key, decoder, false)) .map(Some) } else { // try getting from events (value) match self.de.peek()? { Some(Event::Text(_)) | Some(Event::Start(_)) if has_value_field => { self.value = MapValue::InnerValue; seed.deserialize(INNER_VALUE.into_deserializer()).map(Some) } Some(Event::Start(e)) => { let name = e.local_name().to_owned(); self.value = MapValue::Nested; seed.deserialize(EscapedDeserializer::new(name, decoder, false)) .map(Some) } _ => Ok(None), } } } fn next_value_seed>( &mut self, seed: K, ) -> Result { match std::mem::replace(&mut self.value, MapValue::Empty) { MapValue::Attribute { value } => seed.deserialize(EscapedDeserializer::new( value, self.de.reader.decoder(), true, )), MapValue::Nested | MapValue::InnerValue => seed.deserialize(&mut *self.de), MapValue::Empty => Err(DeError::EndOfAttributes), } } } quick-xml-0.17.2/src/de/mod.rs010064400017500001750000000443321357406636400142640ustar0000000000000000//! Serde `Deserializer` module //! //! # Examples //! //! Here is a simple example parsing [crates.io](https://crates.io/) source code. //! //! ``` //! // Cargo.toml //! // [dependencies] //! // serde = { version = "1.0", features = [ "derive" ] } //! // quick_xml = { version = "0.17", features = [ "serialize" ] } //! extern crate serde; //! extern crate quick_xml; //! //! use serde::Deserialize; //! use quick_xml::de::{from_str, DeError}; //! //! #[derive(Debug, Deserialize, PartialEq)] //! struct Link { //! rel: String, //! href: String, //! sizes: Option, //! } //! //! #[derive(Debug, Deserialize, PartialEq)] //! #[serde(rename_all = "lowercase")] //! enum Lang { //! En, //! Fr, //! De, //! } //! //! #[derive(Debug, Deserialize, PartialEq)] //! struct Head { //! title: String, //! #[serde(rename = "link", default)] //! links: Vec, //! } //! //! #[derive(Debug, Deserialize, PartialEq)] //! struct Script { //! src: String, //! integrity: String, //! } //! //! #[derive(Debug, Deserialize, PartialEq)] //! struct Body { //! #[serde(rename = "script", default)] //! scripts: Vec //! //! //! //! //! //! }"; //! let html: Html = from_str(xml)?; //! assert_eq!(&html.head.title, "crates.io: Rust Package Registr"); //! Ok(html) //! } //! ``` mod escape; mod map; mod seq; mod var; pub use crate::errors::serialize::DeError; use crate::{ events::{BytesStart, BytesText, Event}, Reader, }; use serde::de::{self, DeserializeOwned}; use serde::forward_to_deserialize_any; use std::io::BufRead; const INNER_VALUE: &str = "$value"; /// An xml deserializer pub struct Deserializer { reader: Reader, peek: Option>, has_value_field: bool, } /// Deserialize a xml string pub fn from_str(s: &str) -> Result { from_reader(s.as_bytes()) } /// Deserialize from a reader pub fn from_reader(reader: R) -> Result { let mut de = Deserializer::from_reader(reader); T::deserialize(&mut de) } impl Deserializer { /// Get a new deserializer pub fn new(reader: Reader) -> Self { Deserializer { reader, peek: None, has_value_field: false, } } /// Get a new deserializer from a regular BufRead pub fn from_reader(reader: R) -> Self { let mut reader = Reader::from_reader(reader); reader .expand_empty_elements(true) .check_end_names(true) .trim_text(true); Self::new(reader) } fn peek(&mut self) -> Result>, DeError> { if self.peek.is_none() { self.peek = Some(self.next(&mut Vec::new())?); } Ok(self.peek.as_ref()) } fn next<'a>(&mut self, buf: &'a mut Vec) -> Result, DeError> { if let Some(e) = self.peek.take() { return Ok(e); } loop { let e = self.reader.read_event(buf)?; match e { Event::Start(_) | Event::End(_) | Event::Text(_) | Event::Eof | Event::CData(_) => { return Ok(e.into_owned()) } _ => buf.clear(), } } } fn next_start(&mut self, buf: &mut Vec) -> Result>, DeError> { loop { let e = self.next(buf)?; match e { Event::Start(e) => return Ok(Some(e)), Event::End(_) => return Err(DeError::End), Event::Eof => return Ok(None), _ => buf.clear(), // ignore texts } } } fn next_text<'a>(&mut self) -> Result, DeError> { match self.next(&mut Vec::new())? { Event::Text(e) | Event::CData(e) => Ok(e), Event::Eof => Err(DeError::Eof), Event::Start(e) => { // allow one nested level let inner = self.next(&mut Vec::new())?; let t = match inner { Event::Text(t) | Event::CData(t) => t, Event::Start(_) => return Err(DeError::Start), Event::End(end) if end.name() == e.name() => { return Ok(BytesText::from_escaped(&[] as &[u8])); } Event::End(_) => return Err(DeError::End), Event::Eof => return Err(DeError::Eof), _ => unreachable!(), }; self.read_to_end(e.name())?; Ok(t) } Event::End(e) => { self.peek = Some(Event::End(e)); Ok(BytesText::from_escaped(&[] as &[u8])) } _ => unreachable!(), } } fn read_to_end(&mut self, name: &[u8]) -> Result<(), DeError> { let mut buf = Vec::new(); match self.next(&mut buf)? { Event::Start(e) => self.reader.read_to_end(e.name(), &mut Vec::new())?, Event::End(e) if e.name() == name => return Ok(()), _ => buf.clear(), } Ok(self.reader.read_to_end(name, &mut buf)?) } } macro_rules! deserialize_type { ($deserialize:ident => $visit:ident) => { fn $deserialize>(self, visitor: V) -> Result { let txt = self.next_text()?; #[cfg(not(feature = "encoding"))] let value = self.reader.decode(&*txt)?.parse()?; #[cfg(feature = "encoding")] let value = self.reader.decode(&*txt).parse()?; visitor.$visit(value) } } } impl<'de, 'a, R: BufRead> de::Deserializer<'de> for &'a mut Deserializer { type Error = DeError; forward_to_deserialize_any! { newtype_struct identifier } fn deserialize_struct>( self, _name: &'static str, fields: &'static [&'static str], visitor: V, ) -> Result { if let Some(e) = self.next_start(&mut Vec::new())? { let name = e.name().to_vec(); self.has_value_field = fields.contains(&INNER_VALUE); let map = map::MapAccess::new(self, e)?; let value = visitor.visit_map(map)?; self.has_value_field = false; self.read_to_end(&name)?; Ok(value) } else { Err(DeError::Start) } } deserialize_type!(deserialize_i8 => visit_i8); deserialize_type!(deserialize_i16 => visit_i16); deserialize_type!(deserialize_i32 => visit_i32); deserialize_type!(deserialize_i64 => visit_i64); deserialize_type!(deserialize_u8 => visit_u8); deserialize_type!(deserialize_u16 => visit_u16); deserialize_type!(deserialize_u32 => visit_u32); deserialize_type!(deserialize_u64 => visit_u64); deserialize_type!(deserialize_f32 => visit_f32); deserialize_type!(deserialize_f64 => visit_f64); fn deserialize_bool>(self, visitor: V) -> Result { let txt = self.next_text()?; #[cfg(feature = "encoding")] { #[cfg(feature = "encoding")] let value = self.reader.decode(&*txt); match value.as_ref() { "true" | "1" | "True" | "TRUE" | "t" | "Yes" | "YES" | "yes" | "y" => { visitor.visit_bool(true) } "false" | "0" | "False" | "FALSE" | "f" | "No" | "NO" | "no" | "n" => { visitor.visit_bool(false) } _ => Err(DeError::InvalidBoolean(value.into())), } } #[cfg(not(feature = "encoding"))] { match txt.as_ref() { b"true" | b"1" | b"True" | b"TRUE" | b"t" | b"Yes" | b"YES" | b"yes" | b"y" => { visitor.visit_bool(true) } b"false" | b"0" | b"False" | b"FALSE" | b"f" | b"No" | b"NO" | b"no" | b"n" => { visitor.visit_bool(false) } e => Err(DeError::InvalidBoolean(self.reader.decode(e)?.into())), } } } fn deserialize_string>(self, visitor: V) -> Result { let value = self.next_text()?.unescape_and_decode(&self.reader)?; visitor.visit_string(value) } fn deserialize_char>(self, visitor: V) -> Result { self.deserialize_string(visitor) } fn deserialize_str>(self, visitor: V) -> Result { self.deserialize_string(visitor) } fn deserialize_bytes>(self, visitor: V) -> Result { self.deserialize_string(visitor) } fn deserialize_byte_buf>(self, visitor: V) -> Result { self.deserialize_string(visitor) } fn deserialize_unit>(self, visitor: V) -> Result { let mut buf = Vec::new(); match self.next(&mut buf)? { Event::Start(s) => { self.read_to_end(s.name())?; visitor.visit_unit() } e => Err(DeError::InvalidUnit(format!("{:?}", e))), } } fn deserialize_unit_struct>( self, _name: &'static str, visitor: V, ) -> Result { self.deserialize_unit(visitor) } fn deserialize_tuple>( self, len: usize, visitor: V, ) -> Result { visitor.visit_seq(seq::SeqAccess::new(self, Some(len))?) } fn deserialize_tuple_struct>( self, _name: &'static str, len: usize, visitor: V, ) -> Result { self.deserialize_tuple(len, visitor) } fn deserialize_enum>( self, _name: &'static str, _variants: &'static [&'static str], visitor: V, ) -> Result { let value = visitor.visit_enum(var::EnumAccess::new(self))?; Ok(value) } fn deserialize_seq>(self, visitor: V) -> Result { visitor.visit_seq(seq::SeqAccess::new(self, None)?) } fn deserialize_map>(self, visitor: V) -> Result { self.deserialize_struct("", &[], visitor) } fn deserialize_option>(self, visitor: V) -> Result { match self.peek()? { Some(Event::Text(t)) if t.is_empty() => visitor.visit_none(), None | Some(Event::Eof) => visitor.visit_none(), _ => visitor.visit_some(self), } } fn deserialize_ignored_any>(self, visitor: V) -> Result { match self.next(&mut Vec::new())? { Event::Start(e) => self.read_to_end(e.name())?, Event::End(_) => return Err(DeError::End), _ => (), } visitor.visit_unit() } fn deserialize_any>(self, visitor: V) -> Result { match self.peek()?.ok_or(DeError::Eof)? { Event::Start(_) => self.deserialize_map(visitor), Event::End(_) => self.deserialize_unit(visitor), _ => self.deserialize_string(visitor), } } } #[cfg(test)] mod tests { use super::*; use serde::Deserialize; #[derive(Debug, Deserialize, PartialEq)] struct Item { name: String, source: String, } #[test] fn simple_struct_from_attributes() { let s = r##" "##; let item: Item = from_str(s).unwrap(); assert_eq!( item, Item { name: "hello".to_string(), source: "world.rs".to_string(), } ); } #[test] fn multiple_roots_attributes() { let s = r##" "##; let item: Vec = from_str(s).unwrap(); assert_eq!( item, vec![ Item { name: "hello".to_string(), source: "world.rs".to_string(), }, Item { name: "hello".to_string(), source: "world.rs".to_string(), }, ] ); } #[test] fn simple_struct_from_attribute_and_child() { let s = r##" world.rs "##; let item: Item = from_str(s).unwrap(); assert_eq!( item, Item { name: "hello".to_string(), source: "world.rs".to_string(), } ); } #[derive(Debug, Deserialize, PartialEq)] struct Project { name: String, #[serde(rename = "item", default)] items: Vec, } #[test] fn nested_collection() { let s = r##" "##; let project: Project = from_str(s).unwrap(); assert_eq!( project, Project { name: "my_project".to_string(), items: vec![ Item { name: "hello1".to_string(), source: "world1.rs".to_string(), }, Item { name: "hello2".to_string(), source: "world2.rs".to_string(), }, ], } ); } #[derive(Debug, Deserialize, PartialEq)] enum MyEnum { A(String), B { name: String, flag: bool }, C, } #[derive(Debug, Deserialize, PartialEq)] struct MyEnums { #[serde(rename = "$value")] items: Vec, } #[test] fn collection_of_enums() { let s = r##" test "##; let project: MyEnums = from_str(s).unwrap(); assert_eq!( project, MyEnums { items: vec![ MyEnum::A("test".to_string()), MyEnum::B { name: "hello".to_string(), flag: true, }, MyEnum::C, ], } ); } } quick-xml-0.17.2/src/de/seq.rs010064400017500001750000000044441357061306100142610ustar0000000000000000use crate::de::{DeError, Deserializer}; use crate::{ events::{BytesStart, Event}, reader::Decoder, }; use serde::de; use std::io::BufRead; #[derive(Debug)] enum Names { Unknown, Peek(String), } impl Names { fn is_valid(&self, decoder: Decoder, start: &BytesStart) -> Result { #[cfg(not(feature = "encoding"))] let name = decoder.decode(start.name())?; #[cfg(feature = "encoding")] let name = decoder.decode(start.name()); let res = match self { Names::Unknown => true, Names::Peek(n) => &**n == &*name, }; Ok(res) } } /// A SeqAccess pub struct SeqAccess<'a, R: BufRead> { de: &'a mut Deserializer, max_size: Option, names: Names, } impl<'a, R: BufRead> SeqAccess<'a, R> { /// Get a new SeqAccess pub fn new(de: &'a mut Deserializer, max_size: Option) -> Result { let decoder = de.reader.decoder(); let names = if de.has_value_field { Names::Unknown } else { if let Some(Event::Start(e)) = de.peek()? { #[cfg(not(feature = "encoding"))] let name = decoder.decode(e.name())?.to_owned(); #[cfg(feature = "encoding")] let name = decoder.decode(e.name()).into_owned(); Names::Peek(name) } else { Names::Unknown } }; Ok(SeqAccess { de, max_size, names, }) } } impl<'de, 'a, R: 'a + BufRead> de::SeqAccess<'de> for SeqAccess<'a, R> { type Error = DeError; fn size_hint(&self) -> Option { self.max_size } fn next_element_seed>( &mut self, seed: T, ) -> Result, DeError> { if let Some(s) = self.max_size.as_mut() { if *s == 0 { return Ok(None); } *s -= 1; } let decoder = self.de.reader.decoder(); match self.de.peek()? { None | Some(Event::Eof) | Some(Event::End(_)) => Ok(None), Some(Event::Start(e)) if !self.names.is_valid(decoder, e)? => Ok(None), _ => seed.deserialize(&mut *self.de).map(Some), } } } quick-xml-0.17.2/src/de/var.rs010064400017500001750000000042641357406636400142750ustar0000000000000000use crate::{ de::{escape::EscapedDeserializer, Deserializer}, errors::serialize::DeError, events::Event, }; use serde::de::{self, Deserializer as SerdeDeserializer}; use std::io::BufRead; /// An enum access pub struct EnumAccess<'a, R: BufRead> { de: &'a mut Deserializer, } impl<'a, R: BufRead> EnumAccess<'a, R> { pub fn new(de: &'a mut Deserializer) -> Self { EnumAccess { de } } } impl<'de, 'a, R: 'a + BufRead> de::EnumAccess<'de> for EnumAccess<'a, R> { type Error = DeError; type Variant = VariantAccess<'a, R>; fn variant_seed>( self, seed: V, ) -> Result<(V::Value, VariantAccess<'a, R>), DeError> { let decoder = self.de.reader.decoder(); let de = match self.de.peek()? { Some(Event::Text(t)) => EscapedDeserializer::new(t.to_vec(), decoder, true), Some(Event::Start(e)) => EscapedDeserializer::new(e.name().to_vec(), decoder, false), Some(e) => return Err(DeError::InvalidEnum(e.to_owned())), None => return Err(DeError::Eof), }; let name = seed.deserialize(de)?; Ok((name, VariantAccess { de: self.de })) } } pub struct VariantAccess<'a, R: BufRead> { de: &'a mut Deserializer, } impl<'de, 'a, R: BufRead> de::VariantAccess<'de> for VariantAccess<'a, R> { type Error = DeError; fn unit_variant(self) -> Result<(), DeError> { match self.de.next(&mut Vec::new())? { Event::Start(e) => self.de.read_to_end(e.name()), Event::Text(_) => Ok(()), _ => unreachable!(), } } fn newtype_variant_seed>( self, seed: T, ) -> Result { seed.deserialize(&mut *self.de) } fn tuple_variant>( self, len: usize, visitor: V, ) -> Result { self.de.deserialize_tuple(len, visitor) } fn struct_variant>( self, fields: &'static [&'static str], visitor: V, ) -> Result { self.de.deserialize_struct("", fields, visitor) } } quick-xml-0.17.2/src/errors.rs010064400017500001750000000161061357406636400144270ustar0000000000000000//! Error management module /// The error type used by this crate. #[derive(Debug)] pub enum Error { /// IO error Io(::std::io::Error), /// Utf8 error Utf8(::std::str::Utf8Error), /// Unexpected End of File UnexpectedEof(String), /// End event mismatch EndEventMismatch { /// Expected end event expected: String, /// Found end event found: String, }, /// Unexpected token UnexpectedToken(String), /// Unexpected UnexpectedBang, /// Text not found, expected `Event::Text` TextNotFound, /// `Event::XmlDecl` must start with *version* attribute XmlDeclWithoutVersion(Option), /// Attribute Name contains quote NameWithQuote(usize), /// Attribute key not followed by with `=` NoEqAfterName(usize), /// Attribute value not quoted UnquotedValue(usize), /// Duplicate attribute DuplicatedAttribute(usize, usize), /// Escape error EscapeError(::escape::EscapeError), } impl From<::std::io::Error> for Error { /// Creates a new `Error::Io` from the given error #[inline] fn from(error: ::std::io::Error) -> Error { Error::Io(error) } } impl From<::std::str::Utf8Error> for Error { /// Creates a new `Error::Utf8` from the given error #[inline] fn from(error: ::std::str::Utf8Error) -> Error { Error::Utf8(error) } } /// A specialized `Result` type where the error is hard-wired to [`Error`]. /// /// [`Error`]: enum.Error.html pub type Result = ::std::result::Result; impl std::fmt::Display for Error { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { match self { Error::Io(e) => write!(f, "I/O error: {}", e), Error::Utf8(e) => write!(f, "UTF8 error: {}", e), Error::UnexpectedEof(e) => write!(f, "Unexpected EOF during reading {}.", e), Error::EndEventMismatch { expected, found } => { write!(f, "Expecting found ", expected, found) } Error::UnexpectedToken(e) => write!(f, "Unexpected token '{}'", e), Error::UnexpectedBang => write!( f, "Only Comment, CDATA and DOCTYPE nodes can start with a '!'" ), Error::TextNotFound => write!(f, "Cannot read text, expecting Event::Text"), Error::XmlDeclWithoutVersion(e) => write!( f, "XmlDecl must start with 'version' attribute, found {:?}", e ), Error::NameWithQuote(e) => write!( f, "error while parsing attribute at position {}: \ Attribute key cannot contain quote.", e ), Error::NoEqAfterName(e) => write!( f, "error while parsing attribute at position {}: \ Attribute key must be directly followed by = or space", e ), Error::UnquotedValue(e) => write!( f, "error while parsing attribute at position {}: \ Attribute value must start with a quote.", e ), Error::DuplicatedAttribute(pos1, pos2) => write!( f, "error while parsing attribute at position {0}: \ Duplicate attribute at position {1} and {0}", pos1, pos2 ), Error::EscapeError(e) => write!(f, "{}", e), } } } impl std::error::Error for Error { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match self { Error::Io(e) => Some(e), Error::Utf8(e) => Some(e), Error::EscapeError(e) => Some(e), _ => None, } } } #[cfg(feature = "serialize")] pub mod serialize { //! A module to handle serde (de)serialization errors use super::Error; use std::fmt; /// (De)serialization error #[derive(Debug)] pub enum DeError { /// Serde custom error Custom(String), /// Cannot parse to integer Int(std::num::ParseIntError), /// Cannot parse to float Float(std::num::ParseFloatError), /// Xml parsing error Xml(Error), /// Unexpected end of attributes EndOfAttributes, /// Unexpected end of file Eof, /// Invalid value for a boolean InvalidBoolean(String), /// Invalid unit value InvalidUnit(String), /// Invalid event for Enum InvalidEnum(crate::events::Event<'static>), /// Expecting Text event Text, /// Expecting Start event Start, /// Expecting End event End, /// Unsupported operation Unsupported(&'static str), } impl fmt::Display for DeError { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { match self { DeError::Custom(s) => write!(f, "{}", s), DeError::Xml(e) => write!(f, "{}", e), DeError::Int(e) => write!(f, "{}", e), DeError::Float(e) => write!(f, "{}", e), DeError::EndOfAttributes => write!(f, "Unexpected end of attributes"), DeError::Eof => write!(f, "Unexpected `Event::Eof`"), DeError::InvalidBoolean(v) => write!(f, "Invalid boolean value '{}'", v), DeError::InvalidUnit(v) => { write!(f, "Invalid unit value '{}', expected empty string", v) } DeError::InvalidEnum(e) => write!( f, "Invalid event for Enum, expecting Text or Start, got: {:?}", e ), DeError::Text => write!(f, "Expecting Text event"), DeError::Start => write!(f, "Expecting Start event"), DeError::End => write!(f, "Expecting End event"), DeError::Unsupported(s) => write!(f, "Unsupported operation {}", s), } } } impl ::std::error::Error for DeError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match self { DeError::Int(e) => Some(e), DeError::Float(e) => Some(e), DeError::Xml(e) => Some(e), _ => None, } } } impl serde::de::Error for DeError { fn custom(msg: T) -> Self { DeError::Custom(msg.to_string()) } } impl serde::ser::Error for DeError { fn custom(msg: T) -> Self { DeError::Custom(msg.to_string()) } } impl From for DeError { fn from(e: Error) -> Self { DeError::Xml(e) } } impl From for DeError { fn from(e: std::num::ParseIntError) -> Self { DeError::Int(e) } } impl From for DeError { fn from(e: std::num::ParseFloatError) -> Self { DeError::Float(e) } } } quick-xml-0.17.2/src/escape.rs010064400017500001750000000177541357035401500143520ustar0000000000000000//! Manage xml character escapes use memchr; use std::borrow::Cow; #[derive(Debug)] pub enum EscapeError { /// Entity with Null character EntityWithNull(::std::ops::Range), /// Unrecognized escape symbol UnrecognizedSymbol( ::std::ops::Range, ::std::result::Result, ), /// Cannot find `;` after `&` UnterminatedEntity(::std::ops::Range), /// Cannot convert Hexa to utf8 TooLongHexadecimal, /// Character is not a valid hexadecimal value InvalidHexadecimal(char), /// Cannot convert decimal to hexa TooLongDecimal, /// Character is not a valid decimal value InvalidDecimal(char), } impl std::fmt::Display for EscapeError { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { match self { EscapeError::EntityWithNull(e) => write!( f, "Error while escaping character at range {:?}: Null character entity not allowed", e ), EscapeError::UnrecognizedSymbol(rge, res) => write!( f, "Error while escaping character at range {:?}: Unrecognized escape symbol: {:?}", rge, res ), EscapeError::UnterminatedEntity(e) => write!( f, "Error while escaping character at range {:?}: Cannot find ';' after '&'", e ), EscapeError::TooLongHexadecimal => write!(f, "Cannot convert hexadecimal to utf8"), EscapeError::InvalidHexadecimal(e) => { write!(f, "'{}' is not a valid hexadecimal character", e) } EscapeError::TooLongDecimal => write!(f, "Cannot convert decimal to utf8"), EscapeError::InvalidDecimal(e) => write!(f, "'{}' is not a valid decimal character", e), } } } impl std::error::Error for EscapeError {} // UTF-8 ranges and tags for encoding characters const TAG_CONT: u8 = 0b1000_0000; const TAG_TWO_B: u8 = 0b1100_0000; const TAG_THREE_B: u8 = 0b1110_0000; const TAG_FOUR_B: u8 = 0b1111_0000; const MAX_ONE_B: u32 = 0x80; const MAX_TWO_B: u32 = 0x800; const MAX_THREE_B: u32 = 0x10000; /// helper function to escape a `&[u8]` and replace all /// xml special characters (<, >, &, ', ") with their corresponding /// xml escaped value. pub fn escape(raw: &[u8]) -> Cow<[u8]> { fn to_escape(b: u8) -> bool { match b { b'<' | b'>' | b'\'' | b'&' | b'"' => true, _ => false, } } let mut escaped = None; let mut bytes = raw.iter(); let mut pos = 0; while let Some(i) = bytes.position(|&b| to_escape(b)) { if escaped.is_none() { escaped = Some(Vec::with_capacity(raw.len())); } let escaped = escaped.as_mut().expect("initialized"); let new_pos = pos + i; escaped.extend_from_slice(&raw[pos..new_pos]); match raw[new_pos] { b'<' => escaped.extend_from_slice(b"<"), b'>' => escaped.extend_from_slice(b">"), b'\'' => escaped.extend_from_slice(b"'"), b'&' => escaped.extend_from_slice(b"&"), b'"' => escaped.extend_from_slice(b"""), _ => unreachable!("Only '<', '>','\', '&' and '\"' are escaped"), } pos = new_pos + 1; } if let Some(mut escaped) = escaped { if let Some(raw) = raw.get(pos..) { escaped.extend_from_slice(raw); } Cow::Owned(escaped) } else { Cow::Borrowed(raw) } } /// helper function to unescape a `&[u8]` and replace all /// xml escaped characters ('&...;') into their corresponding value pub fn unescape(raw: &[u8]) -> Result, EscapeError> { let mut unescaped = None; let mut last_end = 0; let mut iter = memchr::memchr2_iter(b'&', b';', raw); while let Some(start) = iter.by_ref().find(|p| raw[*p] == b'&') { match iter.next() { Some(end) if raw[end] == b';' => { // append valid data if unescaped.is_none() { unescaped = Some(Vec::with_capacity(raw.len())); } let unescaped = unescaped.as_mut().expect("initialized"); unescaped.extend_from_slice(&raw[last_end..start]); // search for character correctness match &raw[start + 1..end] { b"lt" => unescaped.push(b'<'), b"gt" => unescaped.push(b'>'), b"amp" => unescaped.push(b'&'), b"apos" => unescaped.push(b'\''), b"quot" => unescaped.push(b'\"'), bytes => { let code = if bytes.starts_with(b"#x") { parse_hexadecimal(&bytes[2..]) } else if bytes.starts_with(b"#") { parse_decimal(&bytes[1..]) } else { Err(EscapeError::UnrecognizedSymbol( start + 1..end, String::from_utf8(bytes.to_vec()), )) }?; if code == 0 { return Err(EscapeError::EntityWithNull(start..end)); } push_utf8(unescaped, code); } } last_end = end + 1; } _ => return Err(EscapeError::UnterminatedEntity(start..raw.len())), } } if let Some(mut unescaped) = unescaped { if let Some(raw) = raw.get(last_end..) { unescaped.extend_from_slice(raw); } Ok(Cow::Owned(unescaped)) } else { Ok(Cow::Borrowed(raw)) } } fn push_utf8(buf: &mut Vec, code: u32) { if code < MAX_ONE_B { buf.push(code as u8); } else if code < MAX_TWO_B { buf.push((code >> 6 & 0x1F) as u8 | TAG_TWO_B); buf.push((code & 0x3F) as u8 | TAG_CONT); } else if code < MAX_THREE_B { buf.push((code >> 12 & 0x0F) as u8 | TAG_THREE_B); buf.push((code >> 6 & 0x3F) as u8 | TAG_CONT); buf.push((code & 0x3F) as u8 | TAG_CONT); } else { buf.push((code >> 18 & 0x07) as u8 | TAG_FOUR_B); buf.push((code >> 12 & 0x3F) as u8 | TAG_CONT); buf.push((code >> 6 & 0x3F) as u8 | TAG_CONT); buf.push((code & 0x3F) as u8 | TAG_CONT); } } fn parse_hexadecimal(bytes: &[u8]) -> Result { // maximum code is 0x10FFFF => 6 characters if bytes.len() > 6 { return Err(EscapeError::TooLongHexadecimal); } let mut code = 0; for &b in bytes { code <<= 4; code += match b { b'0'..=b'9' => b - b'0', b'a'..=b'f' => b - b'a' + 10, b'A'..=b'F' => b - b'A' + 10, b => return Err(EscapeError::InvalidHexadecimal(b as char)), } as u32; } Ok(code) } fn parse_decimal(bytes: &[u8]) -> Result { // maximum code is 0x10FFFF = 1114111 => 7 characters if bytes.len() > 7 { return Err(EscapeError::TooLongDecimal); } let mut code = 0; for &b in bytes { code *= 10; code += match b { b'0'..=b'9' => b - b'0', b => return Err(EscapeError::InvalidDecimal(b as char)), } as u32; } Ok(code) } #[test] fn test_unescape() { assert_eq!(&*unescape(b"test").unwrap(), b"test"); assert_eq!(&*unescape(b"<test>").unwrap(), b""); assert_eq!(&*unescape(b"0").unwrap(), b"0"); assert_eq!(&*unescape(b"0").unwrap(), b"0"); } #[test] fn test_escape() { assert_eq!(&*escape(b"test"), b"test"); assert_eq!(&*escape(b""), b"<test>"); assert_eq!(&*escape(b"\"a\"bc"), b""a"bc"); assert_eq!(&*escape(b"\"a\"b&c"), b""a"b&c"); assert_eq!( &*escape(b"prefix_\"a\"b&<>c"), "prefix_"a"b&<>c".as_bytes() ); } quick-xml-0.17.2/src/events/attributes.rs010064400017500001750000000304251357061306100165710ustar0000000000000000//! Xml Attributes module //! //! Provides an iterator over attributes key/value pairs use errors::{Error, Result}; use escape::{escape, unescape}; use reader::{is_whitespace, Reader}; use std::borrow::Cow; use std::io::BufRead; use std::ops::Range; /// Iterator over XML attributes. /// /// Yields `Result`. An `Err` will be yielded if an attribute is malformed or duplicated. /// The duplicate check can be turned off by calling [`with_checks(false)`]. /// /// [`with_checks(false)`]: #method.with_checks #[derive(Clone)] pub struct Attributes<'a> { /// slice of `Element` corresponding to attributes bytes: &'a [u8], /// current position of the iterator pub(crate) position: usize, /// if true, checks for duplicate names with_checks: bool, /// allows attribute without quote or `=` html: bool, /// if `with_checks`, contains the ranges corresponding to the /// attribute names already parsed in this `Element` consumed: Vec>, } impl<'a> Attributes<'a> { /// Creates a new attribute iterator from a buffer. pub fn new(buf: &'a [u8], pos: usize) -> Attributes<'a> { Attributes { bytes: buf, position: pos, html: false, with_checks: true, consumed: Vec::new(), } } /// Creates a new attribute iterator from a buffer, allowing HTML attribute syntax. pub fn html(buf: &'a [u8], pos: usize) -> Attributes<'a> { Attributes { bytes: buf, position: pos, html: true, with_checks: true, consumed: Vec::new(), } } /// Changes whether attributes should be checked for uniqueness. /// /// The XML specification requires attribute keys in the same element to be unique. This check /// can be disabled to improve performance slightly. /// /// (`true` by default) pub fn with_checks(&mut self, val: bool) -> &mut Attributes<'a> { self.with_checks = val; self } } /// A struct representing a key/value XML attribute. /// /// Field `value` stores raw bytes, possibly containing escape-sequences. Most users will likely /// want to access the value using one of the [`unescaped_value`] and [`unescape_and_decode_value`] /// functions. /// /// [`unescaped_value`]: #method.unescaped_value /// [`unescape_and_decode_value`]: #method.unescape_and_decode_value #[derive(Clone, PartialEq)] pub struct Attribute<'a> { /// The key to uniquely define the attribute. /// /// If [`Attributes::with_checks`] is turned off, the key might not be unique. /// /// [`Attributes::with_checks`]: struct.Attributes.html#method.with_checks pub key: &'a [u8], /// The raw value of the attribute. pub value: Cow<'a, [u8]>, } impl<'a> Attribute<'a> { /// Returns the unescaped value. /// /// This is normally the value you are interested in. Escape sequences such as `>` are /// replaced with their unescaped equivalents such as `>`. /// /// This will allocate if the value contains any escape sequences. pub fn unescaped_value(&self) -> Result> { unescape(&*self.value).map_err(Error::EscapeError) } /// Returns the unescaped and decoded string value. /// /// This allocates a `String` in all cases. For performance reasons it might be a better idea to /// instead use one of: /// /// * [`unescaped_value()`], as it doesn't allocate when no escape sequences are used. /// * [`Reader::decode()`], as it only allocates when the decoding can't be performed otherwise. /// /// [`unescaped_value()`]: #method.unescaped_value /// [`Reader::decode()`]: ../../reader/struct.Reader.html#method.decode #[cfg(feature = "encoding_rs")] pub fn unescape_and_decode_value(&self, reader: &Reader) -> Result { self.unescaped_value() .map(|e| reader.decode(&*e).into_owned()) } /// Returns the unescaped and decoded string value. /// /// This allocates a `String` in all cases. For performance reasons it might be a better idea to /// instead use one of: /// /// * [`unescaped_value()`], as it doesn't allocate when no escape sequences are used. /// * [`Reader::decode()`], as it only allocates when the decoding can't be performed otherwise. /// /// [`unescaped_value()`]: #method.unescaped_value /// [`Reader::decode()`]: ../../reader/struct.Reader.html#method.decode #[cfg(not(feature = "encoding_rs"))] pub fn unescape_and_decode_value(&self, reader: &Reader) -> Result { self.unescaped_value() .and_then(|e| reader.decode(&*e).map(|s| s.to_owned())) } } impl<'a> std::fmt::Debug for Attribute<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { use crate::utils::write_byte_string; write!(f, "Attribute {{ key: ")?; write_byte_string(f, self.key)?; write!(f, ", value: ")?; write_byte_string(f, &self.value)?; write!(f, " }}") } } impl<'a> From<(&'a [u8], &'a [u8])> for Attribute<'a> { /// Creates new attribute from raw bytes. /// Does not apply any transformation to both key and value. /// /// # Examples /// /// ``` /// use quick_xml::events::attributes::Attribute; /// /// let features = Attribute::from(("features".as_bytes(), "Bells & whistles".as_bytes())); /// assert_eq!(features.value, "Bells & whistles".as_bytes()); /// ``` fn from(val: (&'a [u8], &'a [u8])) -> Attribute<'a> { Attribute { key: val.0, value: Cow::from(val.1), } } } impl<'a> From<(&'a str, &'a str)> for Attribute<'a> { /// Creates new attribute from text representation. /// Key is stored as-is, but the value will be escaped. /// /// # Examples /// /// ``` /// use quick_xml::events::attributes::Attribute; /// /// let features = Attribute::from(("features", "Bells & whistles")); /// assert_eq!(features.value, "Bells & whistles".as_bytes()); /// ``` fn from(val: (&'a str, &'a str)) -> Attribute<'a> { Attribute { key: val.0.as_bytes(), value: escape(val.1.as_bytes()), } } } impl<'a> Iterator for Attributes<'a> { type Item = Result>; fn next(&mut self) -> Option { let len = self.bytes.len(); macro_rules! err { ($err:expr) => {{ self.position = len; return Some(Err($err.into())); }}; } macro_rules! attr { ($key:expr) => {{ self.position = len; if self.html { attr!($key, 0..0) } else { return None; }; }}; ($key:expr, $val:expr) => { return Some(Ok(Attribute { key: &self.bytes[$key], value: Cow::Borrowed(&self.bytes[$val]), })); }; } if len <= self.position { return None; } let mut bytes = self.bytes.iter().enumerate().skip(self.position); // key starts after the whitespace let start_key = match bytes .by_ref() .skip_while(|&(_, &b)| !is_whitespace(b)) .find(|&(_, &b)| !is_whitespace(b)) { Some((i, _)) => i, None => attr!(self.position..len), }; // key ends with either whitespace or = let end_key = match bytes .by_ref() .find(|&(_, &b)| b == b'=' || is_whitespace(b)) { Some((i, &b'=')) => i, Some((i, &b'\'')) | Some((i, &b'"')) if self.with_checks => { err!(Error::NameWithQuote(i)); } Some((i, _)) => { // consume until `=` or return if html match bytes.by_ref().find(|&(_, &b)| !is_whitespace(b)) { Some((_, &b'=')) => i, Some((j, _)) if self.html => { self.position = j - 1; attr!(start_key..i, 0..0); } Some((j, _)) => err!(Error::NoEqAfterName(j)), None if self.html => { self.position = len; attr!(start_key..len, 0..0); } None => err!(Error::NoEqAfterName(len)), } } None => attr!(start_key..len), }; if self.with_checks { if let Some(start) = self .consumed .iter() .filter(|r| r.len() == end_key - start_key) .find(|r| &self.bytes[(*r).clone()] == &self.bytes[start_key..end_key]) .map(|ref r| r.start) { err!(Error::DuplicatedAttribute(start_key, start)); } self.consumed.push(start_key..end_key); } // value has quote if not html match bytes.by_ref().find(|&(_, &b)| !is_whitespace(b)) { Some((i, quote @ &b'\'')) | Some((i, quote @ &b'"')) => { match bytes.by_ref().find(|&(_, &b)| b == *quote) { Some((j, _)) => { self.position = j + 1; attr!(start_key..end_key, i + 1..j) } None => err!(Error::UnquotedValue(i)), } } Some((i, _)) if self.html => { let j = bytes .by_ref() .find(|&(_, &b)| is_whitespace(b)) .map_or(len, |(j, _)| j); self.position = j; attr!(start_key..end_key, i..j) } Some((i, _)) => err!(Error::UnquotedValue(i)), None => attr!(start_key..end_key), } } } #[cfg(test)] mod tests { use super::*; #[test] fn regular() { let event = b"name a='a' b = 'b'"; let mut attributes = Attributes::new(event, 0); attributes.with_checks(true); let a = attributes.next().unwrap().unwrap(); assert_eq!(a.key, b"a"); assert_eq!(&*a.value, b"a"); let a = attributes.next().unwrap().unwrap(); assert_eq!(a.key, b"b"); assert_eq!(&*a.value, b"b"); assert!(attributes.next().is_none()); } #[test] fn mixed_quote() { let event = b"name a='a' b = \"b\" c='cc\"cc'"; let mut attributes = Attributes::new(event, 0); attributes.with_checks(true); let a = attributes.next().unwrap().unwrap(); assert_eq!(a.key, b"a"); assert_eq!(&*a.value, b"a"); let a = attributes.next().unwrap().unwrap(); assert_eq!(a.key, b"b"); assert_eq!(&*a.value, b"b"); let a = attributes.next().unwrap().unwrap(); assert_eq!(a.key, b"c"); assert_eq!(&*a.value, b"cc\"cc"); assert!(attributes.next().is_none()); } #[test] fn html_fail() { let event = b"name a='a' b=b c"; let mut attributes = Attributes::new(event, 0); attributes.with_checks(true); let a = attributes.next().unwrap().unwrap(); assert_eq!(a.key, b"a"); assert_eq!(&*a.value, b"a"); assert!(attributes.next().unwrap().is_err()); } #[test] fn html_ok() { let event = b"name a='a' e b=b c d ee=ee"; let mut attributes = Attributes::html(event, 0); attributes.with_checks(true); let a = attributes.next().unwrap().unwrap(); assert_eq!(a.key, b"a"); assert_eq!(&*a.value, b"a"); let a = attributes.next().unwrap().unwrap(); assert_eq!(a.key, b"e"); assert_eq!(&*a.value, b""); let a = attributes.next().unwrap().unwrap(); assert_eq!(a.key, b"b"); assert_eq!(&*a.value, b"b"); let a = attributes.next().unwrap().unwrap(); assert_eq!(a.key, b"c"); assert_eq!(&*a.value, b""); let a = attributes.next().unwrap().unwrap(); assert_eq!(a.key, b"d"); assert_eq!(&*a.value, b""); let a = attributes.next().unwrap().unwrap(); assert_eq!(a.key, b"ee"); assert_eq!(&*a.value, b"ee"); assert!(attributes.next().is_none()); } } quick-xml-0.17.2/src/events/mod.rs010064400017500001750000000517211357061306100151640ustar0000000000000000//! Defines zero-copy XML events used throughout this library. pub mod attributes; #[cfg(feature = "encoding_rs")] use encoding_rs::Encoding; use std::borrow::Cow; use std::io::BufRead; use std::ops::Deref; use std::str::from_utf8; use self::attributes::{Attribute, Attributes}; use errors::{Error, Result}; use escape::{escape, unescape}; use reader::Reader; use memchr; /// Opening tag data (`Event::Start`), with optional attributes. /// /// ``. /// /// The name can be accessed using the [`name`], [`local_name`] or [`unescaped`] methods. An /// iterator over the attributes is returned by the [`attributes`] method. /// /// [`name`]: #method.name /// [`local_name`]: #method.local_name /// [`unescaped`]: #method.unescaped /// [`attributes`]: #method.attributes #[derive(Clone)] pub struct BytesStart<'a> { /// content of the element, before any utf8 conversion buf: Cow<'a, [u8]>, /// end of the element name, the name starts at that the start of `buf` name_len: usize, } impl<'a> BytesStart<'a> { /// Creates a new `BytesStart` from the given content (name + attributes). /// /// # Warning /// /// `&content[..name_len]` is not checked to be a valid name #[inline] pub fn borrowed(content: &'a [u8], name_len: usize) -> Self { BytesStart { buf: Cow::Borrowed(content), name_len: name_len, } } /// Creates a new `BytesStart` from the given name. /// /// # Warning /// /// `&content` is not checked to be a valid name #[inline] pub fn borrowed_name(name: &'a [u8]) -> BytesStart<'a> { Self::borrowed(name, name.len()) } /// Creates a new `BytesStart` from the given content (name + attributes) /// /// Owns its contents. #[inline] pub fn owned>>(content: C, name_len: usize) -> BytesStart<'static> { BytesStart { buf: Cow::Owned(content.into()), name_len: name_len, } } /// Creates a new `BytesStart` from the given name /// /// Owns its contents. #[inline] pub fn owned_name>>(name: C) -> BytesStart<'static> { let content = name.into(); BytesStart { name_len: content.len(), buf: Cow::Owned(content), } } /// Converts the event into an owned event. pub fn into_owned(self) -> BytesStart<'static> { Self::owned(self.buf.into_owned(), self.name_len) } /// Converts the event into an owned event without taking ownershiph of Event pub fn to_owned(&self) -> BytesStart<'static> { Self::owned(self.buf.to_owned(), self.name_len) } /// Consumes `self` and yield a new `BytesStart` with additional attributes from an iterator. /// /// The yielded items must be convertible to [`Attribute`] using `Into`. /// /// [`Attribute`]: attributes/struct.Attributes.html pub fn with_attributes<'b, I>(mut self, attributes: I) -> Self where I: IntoIterator, I::Item: Into>, { self.extend_attributes(attributes); self } /// Gets the undecoded raw tag name as a `&[u8]`. #[inline] pub fn name(&self) -> &[u8] { &self.buf[..self.name_len] } /// Gets the undecoded raw local tag name (excluding namespace) as a `&[u8]`. /// /// All content up to and including the first `:` character is removed from the tag name. #[inline] pub fn local_name(&self) -> &[u8] { let name = self.name(); memchr::memchr(b':', name).map_or(name, |i| &name[i + 1..]) } /// Gets the unescaped tag name. /// /// XML escape sequences like "`<`" will be replaced by their unescaped characters like /// "`<`". #[inline] pub fn unescaped(&self) -> Result> { unescape(&*self.buf).map_err(Error::EscapeError) } /// Returns an iterator over the attributes of this tag. pub fn attributes(&self) -> Attributes { Attributes::new(self, self.name_len) } /// Returns an iterator over the HTML-like attributes of this tag (no mandatory quotes or `=`). pub fn html_attributes(&self) -> Attributes { Attributes::html(self, self.name_len) } /// Add additional attributes to this tag using an iterator. /// /// The yielded items must be convertible to [`Attribute`] using `Into`. /// /// [`Attribute`]: attributes/struct.Attributes.html pub fn extend_attributes<'b, I>(&mut self, attributes: I) -> &mut BytesStart<'a> where I: IntoIterator, I::Item: Into>, { for attr in attributes { self.push_attribute(attr); } self } /// Returns the unescaped and decoded string value. /// /// This allocates a `String` in all cases. For performance reasons it might be a better idea to /// instead use one of: /// /// * [`unescaped()`], as it doesn't allocate when no escape sequences are used. /// * [`Reader::decode()`], as it only allocates when the decoding can't be performed otherwise. /// /// [`unescaped()`]: #method.unescaped /// [`Reader::decode()`]: ../reader/struct.Reader.html#method.decode #[cfg(feature = "encoding_rs")] #[inline] pub fn unescape_and_decode(&self, reader: &Reader) -> Result { self.unescaped().map(|e| reader.decode(&*e).into_owned()) } /// Returns the unescaped and decoded string value. /// /// This allocates a `String` in all cases. For performance reasons it might be a better idea to /// instead use one of: /// /// * [`unescaped()`], as it doesn't allocate when no escape sequences are used. /// * [`Reader::decode()`], as it only allocates when the decoding can't be performed otherwise. /// /// [`unescaped()`]: #method.unescaped /// [`Reader::decode()`]: ../reader/struct.Reader.html#method.decode #[cfg(not(feature = "encoding_rs"))] #[inline] pub fn unescape_and_decode(&self, reader: &Reader) -> Result { self.unescaped() .and_then(|e| reader.decode(&*e).map(|s| s.to_owned())) } /// Adds an attribute to this element. pub fn push_attribute<'b, A: Into>>(&mut self, attr: A) { let a = attr.into(); let bytes = self.buf.to_mut(); bytes.push(b' '); bytes.extend_from_slice(a.key); bytes.extend_from_slice(b"=\""); bytes.extend_from_slice(&*a.value); bytes.push(b'"'); } /// Edit the name of the BytesStart in-place /// /// # Warning /// /// `name` is not checked to be a valid name pub fn set_name(&mut self, name: &[u8]) -> &mut BytesStart<'a> { let bytes = self.buf.to_mut(); bytes.splice(..self.name_len, name.iter().cloned()); self.name_len = name.len(); self } /// Remove all attributes from the ByteStart pub fn clear_attributes(&mut self) -> &mut BytesStart<'a> { self.buf.to_mut().truncate(self.name_len); self } } impl<'a> std::fmt::Debug for BytesStart<'a> { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { use crate::utils::write_byte_string; write!(f, "BytesStart {{ buf: ")?; write_byte_string(f, &self.buf)?; write!(f, ", name_len: {} }}", self.name_len) } } /// An XML declaration (`Event::Decl`). /// /// [W3C XML 1.1 Prolog and Document Type Declaration](http://w3.org/TR/xml11/#sec-prolog-dtd) #[derive(Clone, Debug)] pub struct BytesDecl<'a> { element: BytesStart<'a>, } impl<'a> BytesDecl<'a> { /// Creates a `BytesDecl` from a `BytesStart` pub fn from_start(start: BytesStart<'a>) -> BytesDecl<'a> { BytesDecl { element: start } } /// Gets xml version, including quotes (' or ") pub fn version(&self) -> Result> { // The version *must* be the first thing in the declaration. match self.element.attributes().next() { Some(Err(e)) => Err(e), Some(Ok(Attribute { key: b"version", value: v, })) => Ok(v), Some(Ok(a)) => { let found = from_utf8(a.key).map_err(Error::Utf8)?.to_string(); Err(Error::XmlDeclWithoutVersion(Some(found))) } None => Err(Error::XmlDeclWithoutVersion(None)), } } /// Gets xml encoding, including quotes (' or ") pub fn encoding(&self) -> Option>> { for a in self.element.attributes() { match a { Err(e) => return Some(Err(e)), Ok(Attribute { key: b"encoding", value: v, }) => return Some(Ok(v)), _ => (), } } None } /// Gets xml standalone, including quotes (' or ") pub fn standalone(&self) -> Option>> { for a in self.element.attributes() { match a { Err(e) => return Some(Err(e)), Ok(Attribute { key: b"standalone", value: v, }) => return Some(Ok(v)), _ => (), } } None } /// Constructs a new `XmlDecl` from the (mandatory) _version_ (should be `1.0` or `1.1`), /// the optional _encoding_ (e.g., `UTF-8`) and the optional _standalone_ (`yes` or `no`) /// attribute. /// /// Does not escape any of its inputs. Always uses double quotes to wrap the attribute values. /// The caller is responsible for escaping attribute values. Shouldn't usually be relevant since /// the double quote character is not allowed in any of the attribute values. pub fn new( version: &[u8], encoding: Option<&[u8]>, standalone: Option<&[u8]>, ) -> BytesDecl<'static> { // Compute length of the buffer based on supplied attributes // ' encoding=""' => 12 let encoding_attr_len = if let Some(xs) = encoding { 12 + xs.len() } else { 0 }; // ' standalone=""' => 14 let standalone_attr_len = if let Some(xs) = standalone { 14 + xs.len() } else { 0 }; // 'xml version=""' => 14 let mut buf = Vec::with_capacity(14 + encoding_attr_len + standalone_attr_len); buf.extend_from_slice(b"xml version=\""); buf.extend_from_slice(version); if let Some(encoding_val) = encoding { buf.extend_from_slice(b"\" encoding=\""); buf.extend_from_slice(encoding_val); } if let Some(standalone_val) = standalone { buf.extend_from_slice(b"\" standalone=\""); buf.extend_from_slice(standalone_val); } buf.push(b'"'); BytesDecl { element: BytesStart::owned(buf, 3), } } /// Gets the decoder struct #[cfg(feature = "encoding_rs")] pub fn encoder(&self) -> Option<&'static Encoding> { self.encoding() .and_then(|e| e.ok()) .and_then(|e| Encoding::for_label(&*e)) } /// Converts the event into an owned event. pub fn into_owned(self) -> BytesDecl<'static> { BytesDecl { element: self.element.into_owned(), } } } /// A struct to manage `Event::End` events #[derive(Clone)] pub struct BytesEnd<'a> { name: Cow<'a, [u8]>, } impl<'a> BytesEnd<'a> { /// Creates a new `BytesEnd` borrowing a slice #[inline] pub fn borrowed(name: &'a [u8]) -> BytesEnd<'a> { BytesEnd { name: Cow::Borrowed(name), } } /// Creates a new `BytesEnd` owning its name #[inline] pub fn owned(name: Vec) -> BytesEnd<'static> { BytesEnd { name: Cow::Owned(name), } } /// Converts the event into an owned event. pub fn into_owned(self) -> BytesEnd<'static> { BytesEnd { name: Cow::Owned(self.name.into_owned()), } } /// Gets `BytesEnd` event name #[inline] pub fn name(&self) -> &[u8] { &*self.name } /// local name (excluding namespace) as &[u8] (without eventual attributes) /// returns the name() with any leading namespace removed (all content up to /// and including the first ':' character) #[inline] pub fn local_name(&self) -> &[u8] { if let Some(i) = self.name().iter().position(|b| *b == b':') { &self.name()[i + 1..] } else { self.name() } } } impl<'a> std::fmt::Debug for BytesEnd<'a> { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { use crate::utils::write_byte_string; write!(f, "BytesEnd {{ name: ")?; write_byte_string(f, &self.name)?; write!(f, " }}") } } /// Data from various events (most notably, `Event::Text`). #[derive(Clone)] pub struct BytesText<'a> { // Invariant: The content is always escaped. content: Cow<'a, [u8]>, } impl<'a> BytesText<'a> { /// Creates a new `BytesText` from an escaped byte sequence. #[inline] pub fn from_escaped>>(content: C) -> BytesText<'a> { BytesText { content: content.into(), } } /// Creates a new `BytesText` from a byte sequence. The byte sequence is /// expected not to be escaped. #[inline] pub fn from_plain(content: &'a [u8]) -> BytesText<'a> { BytesText { content: escape(content), } } /// Creates a new `BytesText` from an escaped string. #[inline] pub fn from_escaped_str>>(content: C) -> BytesText<'a> { Self::from_escaped(match content.into() { Cow::Owned(o) => Cow::Owned(o.into_bytes()), Cow::Borrowed(b) => Cow::Borrowed(b.as_bytes()), }) } /// Creates a new `BytesText` from a string. The string is expected not to /// be escaped. #[inline] pub fn from_plain_str(content: &'a str) -> BytesText<'a> { Self::from_plain(content.as_bytes()) } /// Ensures that all data is owned to extend the object's lifetime if /// necessary. #[inline] pub fn into_owned(self) -> BytesText<'static> { BytesText { content: self.content.into_owned().into(), } } /// gets escaped content /// /// Searches for '&' into content and try to escape the coded character if possible /// returns Malformed error with index within element if '&' is not followed by ';' pub fn unescaped(&self) -> Result> { unescape(self).map_err(Error::EscapeError) } /// helper method to unescape then decode self using the reader encoding /// /// for performance reasons (could avoid allocating a `String`), /// it might be wiser to manually use /// 1. BytesText::unescaped() /// 2. Reader::decode(...) #[cfg(feature = "encoding_rs")] pub fn unescape_and_decode(&self, reader: &Reader) -> Result { self.unescaped().map(|e| reader.decode(&*e).into_owned()) } /// helper method to unescape then decode self using the reader encoding /// /// for performance reasons (could avoid allocating a `String`), /// it might be wiser to manually use /// 1. BytesText::unescaped() /// 2. Reader::decode(...) #[cfg(not(feature = "encoding_rs"))] pub fn unescape_and_decode(&self, reader: &Reader) -> Result { self.unescaped() .and_then(|e| reader.decode(&*e).map(|s| s.to_owned())) } /// Gets escaped content. pub fn escaped(&self) -> &[u8] { self.content.as_ref() } } impl<'a> std::fmt::Debug for BytesText<'a> { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { use crate::utils::write_byte_string; write!(f, "BytesText {{ content: ")?; write_byte_string(f, &self.content)?; write!(f, " }}") } } /// Event emitted by [`Reader::read_event`]. /// /// [`Reader::read_event`]: ../reader/struct.Reader.html#method.read_event #[derive(Clone, Debug)] pub enum Event<'a> { /// Start tag (with attributes) ``. Start(BytesStart<'a>), /// End tag ``. End(BytesEnd<'a>), /// Empty element tag (with attributes) ``. Empty(BytesStart<'a>), /// Character data between `Start` and `End` element. Text(BytesText<'a>), /// Comment ``. Comment(BytesText<'a>), /// CData ``. CData(BytesText<'a>), /// XML declaration ``. Decl(BytesDecl<'a>), /// Processing instruction ``. PI(BytesText<'a>), /// Doctype ``. DocType(BytesText<'a>), /// End of XML document. Eof, } impl<'a> Event<'a> { /// Converts the event to an owned version, untied to the lifetime of /// buffer used when reading but incurring a new, seperate allocation. pub fn into_owned(self) -> Event<'static> { match self { Event::Start(e) => Event::Start(e.into_owned()), Event::End(e) => Event::End(e.into_owned()), Event::Empty(e) => Event::Empty(e.into_owned()), Event::Text(e) => Event::Text(e.into_owned()), Event::Comment(e) => Event::Comment(e.into_owned()), Event::CData(e) => Event::CData(e.into_owned()), Event::Decl(e) => Event::Decl(e.into_owned()), Event::PI(e) => Event::PI(e.into_owned()), Event::DocType(e) => Event::DocType(e.into_owned()), Event::Eof => Event::Eof, } } } impl<'a> Deref for BytesStart<'a> { type Target = [u8]; fn deref(&self) -> &[u8] { &*self.buf } } impl<'a> Deref for BytesDecl<'a> { type Target = [u8]; fn deref(&self) -> &[u8] { &*self.element } } impl<'a> Deref for BytesEnd<'a> { type Target = [u8]; fn deref(&self) -> &[u8] { &*self.name } } impl<'a> Deref for BytesText<'a> { type Target = [u8]; fn deref(&self) -> &[u8] { &*self.content } } impl<'a> Deref for Event<'a> { type Target = [u8]; fn deref(&self) -> &[u8] { match *self { Event::Start(ref e) | Event::Empty(ref e) => &*e, Event::End(ref e) => &*e, Event::Text(ref e) => &*e, Event::Decl(ref e) => &*e, Event::PI(ref e) => &*e, Event::CData(ref e) => &*e, Event::Comment(ref e) => &*e, Event::DocType(ref e) => &*e, Event::Eof => &[], } } } impl<'a> AsRef> for Event<'a> { fn as_ref(&self) -> &Event<'a> { self } } #[cfg(test)] mod test { use super::*; #[test] fn local_name() { use std::str::from_utf8; let xml = r#" foobusbar foobusbar <:foo attr='bar'>foobusbar foobusbar "#; let mut rdr = Reader::from_str(xml); let mut buf = Vec::new(); let mut parsed_local_names = Vec::new(); loop { match rdr.read_event(&mut buf).expect("unable to read xml event") { Event::Start(ref e) => parsed_local_names.push( from_utf8(e.local_name()) .expect("unable to build str from local_name") .to_string(), ), Event::End(ref e) => parsed_local_names.push( from_utf8(e.local_name()) .expect("unable to build str from local_name") .to_string(), ), Event::Eof => break, _ => {} } } assert_eq!(parsed_local_names[0], "bus".to_string()); assert_eq!(parsed_local_names[1], "bus".to_string()); assert_eq!(parsed_local_names[2], "".to_string()); assert_eq!(parsed_local_names[3], "".to_string()); assert_eq!(parsed_local_names[4], "foo".to_string()); assert_eq!(parsed_local_names[5], "foo".to_string()); assert_eq!(parsed_local_names[6], "bus:baz".to_string()); assert_eq!(parsed_local_names[7], "bus:baz".to_string()); } #[test] fn bytestart_create() { let b = BytesStart::owned_name("test"); assert_eq!(b.len(), 4); assert_eq!(b.name(), b"test"); } #[test] fn bytestart_set_name() { let mut b = BytesStart::owned_name("test"); assert_eq!(b.len(), 4); assert_eq!(b.name(), b"test"); b.push_attribute(("x", "a")); assert_eq!(b.len(), 10); b.set_name(b"g"); assert_eq!(b.len(), 7); assert_eq!(b.name(), b"g"); } #[test] fn bytestart_clear_attributes() { let mut b = BytesStart::owned_name("test"); b.push_attribute(("x", "y\"z")); b.push_attribute(("x", "y\"z")); b.clear_attributes(); assert!(b.attributes().next().is_none()); assert_eq!(b.len(), 4); assert_eq!(b.name(), b"test"); } } quick-xml-0.17.2/src/lib.rs010064400017500001750000000110561357507455200136570ustar0000000000000000//! High performance XML reader/writer. //! //! ## Description //! //! - `Reader`: a low level xml pull-reader where buffer allocation/clearing is left to user //! - `Writer`: a xml writer. Can be nested with readers if you want to transform xmls //! //! ## Examples //! //! ### Reader //! //! ```rust //! use quick_xml::Reader; //! use quick_xml::events::Event; //! //! let xml = r#" //! Test //! //! Test 2 //! //! "#; //! //! let mut reader = Reader::from_str(xml); //! reader.trim_text(true); //! //! let mut count = 0; //! let mut txt = Vec::new(); //! let mut buf = Vec::new(); //! //! // The `Reader` does not implement `Iterator` because it outputs borrowed data (`Cow`s) //! loop { //! match reader.read_event(&mut buf) { //! // for triggering namespaced events, use this instead: //! // match reader.read_namespaced_event(&mut buf) { //! Ok(Event::Start(ref e)) => { //! // for namespaced: //! // Ok((ref namespace_value, Event::Start(ref e))) //! match e.name() { //! b"tag1" => println!("attributes values: {:?}", //! e.attributes().map(|a| a.unwrap().value) //! .collect::>()), //! b"tag2" => count += 1, //! _ => (), //! } //! }, //! // unescape and decode the text event using the reader encoding //! Ok(Event::Text(e)) => txt.push(e.unescape_and_decode(&reader).unwrap()), //! Ok(Event::Eof) => break, // exits the loop when reaching end of file //! Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e), //! _ => (), // There are several other `Event`s we do not consider here //! } //! //! // if we don't keep a borrow elsewhere, we can clear the buffer to keep memory usage low //! buf.clear(); //! } //! ``` //! //! ### Writer //! //! ```rust //! use quick_xml::Writer; //! use quick_xml::events::{Event, BytesEnd, BytesStart}; //! use quick_xml::Reader; //! use std::io::Cursor; //! use std::iter; //! //! let xml = r#"text"#; //! let mut reader = Reader::from_str(xml); //! reader.trim_text(true); //! let mut writer = Writer::new(Cursor::new(Vec::new())); //! let mut buf = Vec::new(); //! loop { //! match reader.read_event(&mut buf) { //! Ok(Event::Start(ref e)) if e.name() == b"this_tag" => { //! //! // crates a new element ... alternatively we could reuse `e` by calling //! // `e.into_owned()` //! let mut elem = BytesStart::owned(b"my_elem".to_vec(), "my_elem".len()); //! //! // collect existing attributes //! elem.extend_attributes(e.attributes().map(|attr| attr.unwrap())); //! //! // copy existing attributes, adds a new my-key="some value" attribute //! elem.push_attribute(("my-key", "some value")); //! //! // writes the event to the writer //! assert!(writer.write_event(Event::Start(elem)).is_ok()); //! }, //! Ok(Event::End(ref e)) if e.name() == b"this_tag" => { //! assert!(writer.write_event(Event::End(BytesEnd::borrowed(b"my_elem"))).is_ok()); //! }, //! Ok(Event::Eof) => break, //! Ok(e) => assert!(writer.write_event(e).is_ok()), //! // or using the buffer //! // Ok(e) => assert!(writer.write(&buf).is_ok()), //! Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e), //! } //! buf.clear(); //! } //! //! let result = writer.into_inner().into_inner(); //! let expected = r#"text"#; //! assert_eq!(result, expected.as_bytes()); //! ``` //! //! # Features //! //! quick-xml supports 2 additional features, non activated by default: //! - `encoding`: support non utf8 xmls //! - `serialize`: support serde `Serialize`/`Deserialize` #![deny(missing_docs)] #![recursion_limit = "1024"] #[cfg(feature = "encoding_rs")] extern crate encoding_rs; extern crate memchr; #[cfg(feature = "serialize")] extern crate serde; #[cfg(feature = "serialize")] pub mod de; mod errors; mod escape; pub mod events; mod reader; #[cfg(feature = "serialize")] pub mod se; mod utils; mod writer; // reexports #[cfg(feature = "serialize")] pub use errors::serialize::DeError; pub use errors::{Error, Result}; pub use reader::Reader; pub use writer::Writer; quick-xml-0.17.2/src/reader.rs010064400017500001750000001257021357406636400143600ustar0000000000000000//! A module to handle `Reader` #[cfg(feature = "encoding")] use std::borrow::Cow; use std::fs::File; use std::io::{self, BufRead, BufReader}; use std::path::Path; use std::str::from_utf8; #[cfg(feature = "encoding")] use encoding_rs::Encoding; use errors::{Error, Result}; use events::{attributes::Attribute, BytesDecl, BytesEnd, BytesStart, BytesText, Event}; use memchr; enum TagState { Opened, Closed, Empty, /// Either Eof or Errored Exit, } /// A low level encoding-agnostic XML event reader. /// /// Consumes a `BufRead` and streams XML `Event`s. /// /// # Examples /// /// ``` /// use quick_xml::Reader; /// use quick_xml::events::Event; /// /// let xml = r#" /// Test /// Test 2 /// "#; /// let mut reader = Reader::from_str(xml); /// reader.trim_text(true); /// let mut count = 0; /// let mut txt = Vec::new(); /// let mut buf = Vec::new(); /// loop { /// match reader.read_event(&mut buf) { /// Ok(Event::Start(ref e)) => { /// match e.name() { /// b"tag1" => println!("attributes values: {:?}", /// e.attributes().map(|a| a.unwrap().value) /// .collect::>()), /// b"tag2" => count += 1, /// _ => (), /// } /// }, /// Ok(Event::Text(e)) => txt.push(e.unescape_and_decode(&reader).unwrap()), /// Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e), /// Ok(Event::Eof) => break, /// _ => (), /// } /// buf.clear(); /// } /// ``` pub struct Reader { /// reader reader: B, /// current buffer position, useful for debuging errors buf_position: usize, /// current state Open/Close tag_state: TagState, /// expand empty element into an opening and closing element expand_empty_elements: bool, /// trims Text events, skip the element if text is empty trim_text: bool, /// trims trailing whitespaces from markup names in closing tags `` trim_markup_names_in_closing_tags: bool, /// check if End nodes match last Start node check_end_names: bool, /// check if comments contains `--` (false per default) check_comments: bool, /// all currently Started elements which didn't have a matching /// End element yet opened_buffer: Vec, /// opened name start indexes opened_starts: Vec, /// a buffer to manage namespaces ns_buffer: NamespaceBufferIndex, #[cfg(feature = "encoding")] /// the encoding specified in the xml, defaults to utf8 encoding: &'static Encoding, } impl Reader { /// Creates a `Reader` that reads from a reader implementing `BufRead`. pub fn from_reader(reader: B) -> Reader { Reader { reader: reader, opened_buffer: Vec::new(), opened_starts: Vec::new(), tag_state: TagState::Closed, expand_empty_elements: false, trim_text: false, trim_markup_names_in_closing_tags: true, check_end_names: true, buf_position: 0, check_comments: false, ns_buffer: NamespaceBufferIndex::default(), #[cfg(feature = "encoding")] encoding: ::encoding_rs::UTF_8, } } /// Changes whether empty elements should be split into an `Open` and a `Close` event. /// /// When set to `true`, all [`Empty`] events produced by a self-closing tag like `` are /// expanded into a [`Start`] event followed by a [`End`] event. When set to `false` (the /// default), those tags are represented by an [`Empty`] event instead. /// /// (`false` by default) /// /// [`Empty`]: events/enum.Event.html#variant.Empty /// [`Start`]: events/enum.Event.html#variant.Start /// [`End`]: events/enum.Event.html#variant.End pub fn expand_empty_elements(&mut self, val: bool) -> &mut Reader { self.expand_empty_elements = val; self } /// Changes whether whitespace before and after character data should be removed. /// /// When set to `true`, all [`Text`] events are trimmed. If they are empty, no event will be /// pushed. /// /// (`false` by default) /// /// [`Text`]: events/enum.Event.html#variant.Text pub fn trim_text(&mut self, val: bool) -> &mut Reader { self.trim_text = val; self } /// Changes wether trailing whitespaces after the markup name are trimmed in closing tags /// ``. /// /// If true the emitted [`End`] event is stripped of trailing whitespace after the markup name. /// /// Note that if set to `false` and `check_end_names` is true the comparison of markup names is /// going to fail erronously if a closing tag contains trailing whitespaces. /// /// (`true` by default) /// /// [`End`]: events/enum.Event.html#variant.End pub fn trim_markup_names_in_closing_tags(&mut self, val: bool) -> &mut Reader { self.trim_markup_names_in_closing_tags = val; self } /// Changes whether mismatched closing tag names should be detected. /// /// When set to `false`, it won't check if a closing tag matches the corresponding opening tag. /// For example, `` will be permitted. /// /// If the XML is known to be sane (already processed, etc.) this saves extra time. /// /// Note that the emitted [`End`] event will not be modified if this is disabled, ie. it will /// contain the data of the mismatched end tag. /// /// (`true` by default) /// /// [`End`]: events/enum.Event.html#variant.End pub fn check_end_names(&mut self, val: bool) -> &mut Reader { self.check_end_names = val; self } /// Changes whether comments should be validated. /// /// When set to `true`, every [`Comment`] event will be checked for not containing `--`, which /// is not allowed in XML comments. Most of the time we don't want comments at all so we don't /// really care about comment correctness, thus the default value is `false` to improve /// performance. /// /// (`false` by default) /// /// [`Comment`]: events/enum.Event.html#variant.Comment pub fn check_comments(&mut self, val: bool) -> &mut Reader { self.check_comments = val; self } /// Gets the current byte position in the input data. /// /// Useful when debugging errors. pub fn buffer_position(&self) -> usize { // when internal state is Opened, we have actually read until '<', // which we don't want to show let offset = if let TagState::Opened = self.tag_state { 1 } else { 0 }; self.buf_position - offset } /// private function to read until '<' is found /// return a `Text` event fn read_until_open<'a, 'b>(&'a mut self, buf: &'b mut Vec) -> Result> { self.tag_state = TagState::Opened; let buf_start = buf.len(); match read_until(&mut self.reader, b'<', buf) { Ok(0) => Ok(Event::Eof), Ok(n) => { self.buf_position += n; let (start, len) = if self.trim_text { match buf.iter().skip(buf_start).position(|&b| !is_whitespace(b)) { Some(start) => ( buf_start + start, buf.iter() .rposition(|&b| !is_whitespace(b)) .map_or_else(|| buf.len(), |p| p + 1), ), None => return self.read_event(buf), } } else { (buf_start, buf.len()) }; Ok(Event::Text(BytesText::from_escaped(&buf[start..len]))) } Err(e) => Err(e), } } /// private function to read until '>' is found fn read_until_close<'a, 'b>(&'a mut self, buf: &'b mut Vec) -> Result> { self.tag_state = TagState::Closed; // need to read 1 character to decide whether pay special attention to attribute values let buf_start = buf.len(); let start = loop { match self.reader.fill_buf() { Ok(n) if n.is_empty() => return Ok(Event::Eof), Ok(n) => { // We intentionally don't `consume()` the byte, otherwise we would have to // handle things like '<>' here already. break n[0]; } Err(ref e) if e.kind() == io::ErrorKind::Interrupted => continue, Err(e) => return Err(Error::Io(e)), } }; if start != b'/' && start != b'!' && start != b'?' { match read_elem_until(&mut self.reader, b'>', buf) { Ok(0) => Ok(Event::Eof), Ok(n) => { self.buf_position += n; // we already *know* that we are in this case self.read_start(&buf[buf_start..]) } Err(e) => Err(e), } } else { match read_until(&mut self.reader, b'>', buf) { Ok(0) => Ok(Event::Eof), Ok(n) => { self.buf_position += n; match start { b'/' => self.read_end(&buf[buf_start..]), b'!' => self.read_bang(buf_start, buf), b'?' => self.read_question_mark(&buf[buf_start..]), _ => unreachable!( "We checked that `start` must be one of [/!?], was {:?} \ instead.", start ), } } Err(e) => Err(e), } } } /// reads `BytesElement` starting with a `/`, /// if `self.check_end_names`, checks that element matches last opened element /// return `End` event fn read_end<'a, 'b>(&'a mut self, buf: &'b [u8]) -> Result> { let len = buf.len(); // XML standard permits whitespaces after the markup name in closing tags. // Let's strip them from the buffer before comparing tag names. let name = if self.trim_markup_names_in_closing_tags { if let Some(pos_end_name) = buf[1..].iter().rposition(|&b| !b.is_ascii_whitespace()) { let (name, _) = buf[1..].split_at(pos_end_name + 1); name } else { &buf[1..] } } else { &buf[1..] }; if self.check_end_names { let mismatch_err = |expected: &[u8], found: &[u8], buf_position: &mut usize| { *buf_position -= len; Err(Error::EndEventMismatch { expected: from_utf8(expected).unwrap_or("").to_owned(), found: from_utf8(found).unwrap_or("").to_owned(), }) }; match self.opened_starts.pop() { Some(start) => { if name != &self.opened_buffer[start..] { let expected = &self.opened_buffer[start..]; mismatch_err(expected, name, &mut self.buf_position) } else { self.opened_buffer.truncate(start); Ok(Event::End(BytesEnd::borrowed(name))) } } None => mismatch_err(b"", &buf[1..], &mut self.buf_position), } } else { Ok(Event::End(BytesEnd::borrowed(name))) } } /// reads `BytesElement` starting with a `!`, /// return `Comment`, `CData` or `DocType` event /// /// Note: depending on the start of the Event, we may need to read more /// data, thus we need a mutable buffer fn read_bang<'a, 'b>( &'a mut self, buf_start: usize, buf: &'b mut Vec, ) -> Result> { let len = buf.len(); if len >= buf_start + 3 && &buf[buf_start + 1..buf_start + 3] == b"--" { let mut len = buf.len(); while (len - buf_start) < 5 || &buf[len - 2..] != b"--" { buf.push(b'>'); match read_until(&mut self.reader, b'>', buf) { Ok(0) => { self.buf_position -= len; return Err(Error::UnexpectedEof("Comment".to_string())); } Ok(n) => self.buf_position += n, Err(e) => return Err(e.into()), } len = buf.len(); } if self.check_comments { let mut offset = len - 3; for w in buf[buf_start + 3..len - 1].windows(2) { if &*w == b"--" { self.buf_position -= offset; return Err(Error::UnexpectedToken("--".to_string())); } offset -= 1; } } Ok(Event::Comment(BytesText::from_escaped( &buf[buf_start + 3..len - 2], ))) } else if len >= buf_start + 8 { match &buf[buf_start + 1..buf_start + 8] { b"[CDATA[" => { let mut len = buf.len(); while len < 10 || &buf[len - 2..] != b"]]" { buf.push(b'>'); match read_until(&mut self.reader, b'>', buf) { Ok(0) => { self.buf_position -= len; return Err(Error::UnexpectedEof("CData".to_string())); } Ok(n) => self.buf_position += n, Err(e) => return Err(e), } len = buf.len(); } Ok(Event::CData(BytesText::from_escaped( &buf[buf_start + 8..len - 2], ))) } b"DOCTYPE" => { let mut count = buf.iter().skip(buf_start).filter(|&&b| b == b'<').count(); while count > 0 { buf.push(b'>'); match read_until(&mut self.reader, b'>', buf) { Ok(0) => { self.buf_position -= buf.len(); return Err(Error::UnexpectedEof("DOCTYPE".to_string())); } Ok(n) => { self.buf_position += n; let start = buf.len() - n; count += buf.iter().skip(start).filter(|&&b| b == b'<').count(); count -= 1; } Err(e) => return Err(e), } } let len = buf.len(); Ok(Event::DocType(BytesText::from_escaped( &buf[buf_start + 8..len], ))) } _ => return Err(Error::UnexpectedBang), } } else { self.buf_position -= buf.len(); return Err(Error::UnexpectedBang); } } /// reads `BytesElement` starting with a `?`, /// return `Decl` or `PI` event #[cfg(feature = "encoding")] fn read_question_mark<'a, 'b>(&'a mut self, buf: &'b [u8]) -> Result> { let len = buf.len(); if len > 2 && buf[len - 1] == b'?' { if len > 5 && &buf[1..4] == b"xml" && is_whitespace(buf[4]) { let event = BytesDecl::from_start(BytesStart::borrowed(&buf[1..len - 1], 3)); // Try getting encoding from the declaration event if let Some(enc) = event.encoder() { self.encoding = enc; } Ok(Event::Decl(event)) } else { Ok(Event::PI(BytesText::from_escaped(&buf[1..len - 1]))) } } else { self.buf_position -= len; Err(Error::UnexpectedEof("XmlDecl".to_string())) } } /// reads `BytesElement` starting with a `?`, /// return `Decl` or `PI` event #[cfg(not(feature = "encoding"))] fn read_question_mark<'a, 'b>(&'a mut self, buf: &'b [u8]) -> Result> { let len = buf.len(); if len > 2 && buf[len - 1] == b'?' { if len > 5 && &buf[1..4] == b"xml" && is_whitespace(buf[4]) { let event = BytesDecl::from_start(BytesStart::borrowed(&buf[1..len - 1], 3)); Ok(Event::Decl(event)) } else { Ok(Event::PI(BytesText::from_escaped(&buf[1..len - 1]))) } } else { self.buf_position -= len; Err(Error::UnexpectedEof("XmlDecl".to_string())) } } #[inline] fn close_expanded_empty(&mut self) -> Result> { self.tag_state = TagState::Closed; let name = self .opened_buffer .split_off(self.opened_starts.pop().unwrap()); Ok(Event::End(BytesEnd::owned(name))) } /// reads `BytesElement` starting with any character except `/`, `!` or ``?` /// return `Start` or `Empty` event fn read_start<'a, 'b>(&'a mut self, buf: &'b [u8]) -> Result> { // TODO: do this directly when reading bufreader ... let len = buf.len(); let name_end = buf.iter().position(|&b| is_whitespace(b)).unwrap_or(len); if let Some(&b'/') = buf.last() { let end = if name_end < len { name_end } else { len - 1 }; if self.expand_empty_elements { self.tag_state = TagState::Empty; self.opened_starts.push(self.opened_buffer.len()); self.opened_buffer.extend(&buf[..end]); Ok(Event::Start(BytesStart::borrowed(&buf[..len - 1], end))) } else { Ok(Event::Empty(BytesStart::borrowed(&buf[..len - 1], end))) } } else { if self.check_end_names { self.opened_starts.push(self.opened_buffer.len()); self.opened_buffer.extend(&buf[..name_end]); } Ok(Event::Start(BytesStart::borrowed(buf, name_end))) } } /// Reads the next `Event`. /// /// This is the main entry point for reading XML `Event`s. /// /// `Event`s borrow `buf` and can be converted to own their data if needed (uses `Cow` /// internally). /// /// Having the possibility to control the internal buffers gives you some additional benefits /// such as: /// /// - Reduce the number of allocations by reusing the same buffer. For constrained systems, /// you can call `buf.clear()` once you are done with processing the event (typically at the /// end of your loop). /// - Reserve the buffer length if you know the file size (using `Vec::with_capacity`). /// /// # Examples /// /// ``` /// use quick_xml::Reader; /// use quick_xml::events::Event; /// /// let xml = r#" /// Test /// Test 2 /// "#; /// let mut reader = Reader::from_str(xml); /// reader.trim_text(true); /// let mut count = 0; /// let mut buf = Vec::new(); /// let mut txt = Vec::new(); /// loop { /// match reader.read_event(&mut buf) { /// Ok(Event::Start(ref e)) => count += 1, /// Ok(Event::Text(e)) => txt.push(e.unescape_and_decode(&reader).expect("Error!")), /// Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e), /// Ok(Event::Eof) => break, /// _ => (), /// } /// buf.clear(); /// } /// println!("Found {} start events", count); /// println!("Text events: {:?}", txt); /// ``` pub fn read_event<'a, 'b>(&'a mut self, buf: &'b mut Vec) -> Result> { let event = match self.tag_state { TagState::Opened => self.read_until_close(buf), TagState::Closed => self.read_until_open(buf), TagState::Empty => self.close_expanded_empty(), TagState::Exit => return Ok(Event::Eof), }; match event { Err(_) | Ok(Event::Eof) => self.tag_state = TagState::Exit, _ => {} } event } /// Resolves a potentially qualified **event name** into (namespace name, local name). /// /// *Qualified* attribute names have the form `prefix:local-name` where the`prefix` is defined /// on any containing XML element via `xmlns:prefix="the:namespace:uri"`. The namespace prefix /// can be defined on the same element as the attribute in question. /// /// *Unqualified* event inherits the current *default namespace*. #[inline] pub fn event_namespace<'a, 'b, 'c>( &'a self, qname: &'b [u8], namespace_buffer: &'c [u8], ) -> (Option<&'c [u8]>, &'b [u8]) { self.ns_buffer .resolve_namespace(qname, namespace_buffer, true) } /// Resolves a potentially qualified **attribute name** into (namespace name, local name). /// /// *Qualified* attribute names have the form `prefix:local-name` where the`prefix` is defined /// on any containing XML element via `xmlns:prefix="the:namespace:uri"`. The namespace prefix /// can be defined on the same element as the attribute in question. /// /// *Unqualified* attribute names do *not* inherit the current *default namespace*. #[inline] pub fn attribute_namespace<'a, 'b, 'c>( &'a self, qname: &'b [u8], namespace_buffer: &'c [u8], ) -> (Option<&'c [u8]>, &'b [u8]) { self.ns_buffer .resolve_namespace(qname, namespace_buffer, false) } /// Reads the next event and resolves its namespace (if applicable). /// /// # Examples /// /// ``` /// use std::str::from_utf8; /// use quick_xml::Reader; /// use quick_xml::events::Event; /// /// let xml = r#" /// Test /// Test 2 /// "#; /// let mut reader = Reader::from_str(xml); /// reader.trim_text(true); /// let mut count = 0; /// let mut buf = Vec::new(); /// let mut ns_buf = Vec::new(); /// let mut txt = Vec::new(); /// loop { /// match reader.read_namespaced_event(&mut buf, &mut ns_buf) { /// Ok((ref ns, Event::Start(ref e))) => { /// count += 1; /// match (*ns, e.local_name()) { /// (Some(b"www.xxxx"), b"tag1") => (), /// (Some(b"www.yyyy"), b"tag2") => (), /// (ns, n) => panic!("Namespace and local name mismatch"), /// } /// println!("Resolved namespace: {:?}", ns.and_then(|ns| from_utf8(ns).ok())); /// } /// Ok((_, Event::Text(e))) => { /// txt.push(e.unescape_and_decode(&reader).expect("Error!")) /// }, /// Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e), /// Ok((_, Event::Eof)) => break, /// _ => (), /// } /// buf.clear(); /// } /// println!("Found {} start events", count); /// println!("Text events: {:?}", txt); /// ``` pub fn read_namespaced_event<'a, 'b, 'c>( &'a mut self, buf: &'b mut Vec, namespace_buffer: &'c mut Vec, ) -> Result<(Option<&'c [u8]>, Event<'b>)> { self.ns_buffer.pop_empty_namespaces(namespace_buffer); match self.read_event(buf) { Ok(Event::Eof) => Ok((None, Event::Eof)), Ok(Event::Start(e)) => { self.ns_buffer.push_new_namespaces(&e, namespace_buffer); Ok(( self.ns_buffer .find_namespace_value(e.name(), &**namespace_buffer), Event::Start(e), )) } Ok(Event::Empty(e)) => { // For empty elements we need to 'artificially' keep the namespace scope on the // stack until the next `next()` call occurs. // Otherwise the caller has no chance to use `resolve` in the context of the // namespace declarations that are 'in scope' for the empty element alone. // Ex: self.ns_buffer.push_new_namespaces(&e, namespace_buffer); // notify next `read_namespaced_event()` invocation that it needs to pop this // namespace scope self.ns_buffer.pending_pop = true; Ok(( self.ns_buffer .find_namespace_value(e.name(), &**namespace_buffer), Event::Empty(e), )) } Ok(Event::End(e)) => { // notify next `read_namespaced_event()` invocation that it needs to pop this // namespace scope self.ns_buffer.pending_pop = true; Ok(( self.ns_buffer .find_namespace_value(e.name(), &**namespace_buffer), Event::End(e), )) } Ok(e) => Ok((None, e)), Err(e) => Err(e), } } /// Returns the `Reader`s encoding. /// /// The used encoding may change after parsing the XML declaration. /// /// This encoding will be used by [`decode`]. /// /// [`decode`]: #method.decode #[cfg(feature = "encoding")] pub fn encoding(&self) -> &'static Encoding { self.encoding } /// Decodes a slice using the encoding specified in the XML declaration. /// /// Decode `bytes` with BOM sniffing and with malformed sequences replaced with the /// `U+FFFD REPLACEMENT CHARACTER`. /// /// If no encoding is specified, defaults to UTF-8. #[inline] #[cfg(feature = "encoding")] pub fn decode<'b, 'c>(&'b self, bytes: &'c [u8]) -> Cow<'c, str> { self.encoding.decode(bytes).0 } /// Decodes a UTF8 slice regarless of XML declaration. /// /// Decode `bytes` with BOM sniffing and with malformed sequences replaced with the /// `U+FFFD REPLACEMENT CHARACTER`. /// /// # Note /// /// If you instead want to use XML declared encoding, use the `encoding` feature #[inline] #[cfg(not(feature = "encoding"))] pub fn decode<'c>(&self, bytes: &'c [u8]) -> Result<&'c str> { from_utf8(bytes).map_err(Error::Utf8) } /// Get utf8 decoder #[cfg(feature = "encoding")] pub fn decoder(&self) -> Decoder { Decoder { encoding: self.encoding, } } /// Get utf8 decoder #[cfg(not(feature = "encoding"))] pub fn decoder(&self) -> Decoder { Decoder } /// Reads until end element is found /// /// Manages nested cases where parent and child elements have the same name pub fn read_to_end>(&mut self, end: K, buf: &mut Vec) -> Result<()> { let mut depth = 0; let end = end.as_ref(); loop { match self.read_event(buf) { Ok(Event::End(ref e)) if e.name() == end => { if depth == 0 { return Ok(()); } depth -= 1; } Ok(Event::Start(ref e)) if e.name() == end => depth += 1, Err(e) => return Err(e), Ok(Event::Eof) => { return Err(Error::UnexpectedEof(format!("", from_utf8(end)))); } _ => (), } buf.clear(); } } /// Reads optional text between start and end tags. /// /// If the next event is a [`Text`] event, returns the decoded and unescaped content as a /// `String`. If the next event is an [`End`] event, returns the empty string. In all other /// cases, returns an error. /// /// Any text will be decoded using the XML encoding specified in the XML declaration (or UTF-8 /// if none is specified). /// /// # Examples /// /// ``` /// use quick_xml::Reader; /// use quick_xml::events::Event; /// /// let mut xml = Reader::from_reader(b" /// <b> /// /// " as &[u8]); /// xml.trim_text(true); /// /// let expected = ["", ""]; /// for &content in expected.iter() { /// match xml.read_event(&mut Vec::new()) { /// Ok(Event::Start(ref e)) => { /// assert_eq!(&xml.read_text(e.name(), &mut Vec::new()).unwrap(), content); /// }, /// e => panic!("Expecting Start event, found {:?}", e), /// } /// } /// ``` /// /// [`Text`]: events/enum.Event.html#variant.Text /// [`End`]: events/enum.Event.html#variant.End pub fn read_text>(&mut self, end: K, buf: &mut Vec) -> Result { let s = match self.read_event(buf) { Ok(Event::Text(e)) => e.unescape_and_decode(self), Ok(Event::End(ref e)) if e.name() == end.as_ref() => return Ok("".to_string()), Err(e) => return Err(e), Ok(Event::Eof) => return Err(Error::UnexpectedEof("Text".to_string())), _ => return Err(Error::TextNotFound), }; self.read_to_end(end, buf)?; s } /// Consumes `Reader` returning the underlying reader /// /// Can be used to compute line and column of a parsing error position /// /// # Examples /// /// ``` /// use std::{str, io::Cursor}; /// use quick_xml::Reader; /// use quick_xml::events::Event; /// /// let xml = r#" /// Test /// Test 2 /// "#; /// let mut reader = Reader::from_reader(Cursor::new(xml.as_bytes())); /// let mut buf = Vec::new(); /// /// fn into_line_and_column(reader: Reader>) -> (usize, usize) { /// let end_pos = reader.buffer_position(); /// let mut cursor = reader.into_underlying_reader(); /// let s = String::from_utf8(cursor.into_inner()[0..end_pos].to_owned()) /// .expect("can't make a string"); /// let mut line = 1; /// let mut column = 0; /// for c in s.chars() { /// if c == '\n' { /// line += 1; /// column = 0; /// } else { /// column += 1; /// } /// } /// (line, column) /// } /// /// loop { /// match reader.read_event(&mut buf) { /// Ok(Event::Start(ref e)) => match e.name() { /// b"tag1" | b"tag2" => (), /// tag => { /// assert_eq!(b"tag3", tag); /// assert_eq!((3, 22), into_line_and_column(reader)); /// break; /// } /// }, /// Ok(Event::Eof) => unreachable!(), /// _ => (), /// } /// buf.clear(); /// } /// ``` pub fn into_underlying_reader(self) -> B { self.reader } } impl Reader> { /// Creates an XML reader from a file path. pub fn from_file>(path: P) -> Result>> { let file = File::open(path).map_err(Error::Io)?; let reader = BufReader::new(file); Ok(Reader::from_reader(reader)) } } impl<'a> Reader<&'a [u8]> { /// Creates an XML reader from a string slice. pub fn from_str(s: &'a str) -> Reader<&'a [u8]> { Reader::from_reader(s.as_bytes()) } } /// read until `byte` is found or end of file /// return the position of byte #[inline] fn read_until(r: &mut R, byte: u8, buf: &mut Vec) -> Result { let mut read = 0; let mut done = false; while !done { let used = { let available = match r.fill_buf() { Ok(n) if n.is_empty() => return Ok(read), Ok(n) => n, Err(ref e) if e.kind() == io::ErrorKind::Interrupted => continue, Err(e) => return Err(Error::Io(e)), }; match memchr::memchr(byte, available) { Some(i) => { buf.extend_from_slice(&available[..i]); done = true; i + 1 } None => { buf.extend_from_slice(available); available.len() } } }; r.consume(used); read += used; } Ok(read) } /// Derived from `read_until`, but modified to handle XML attributes using a minimal state machine. /// [W3C Extensible Markup Language (XML) 1.1 (2006)](https://www.w3.org/TR/xml11) /// /// Attribute values are defined as follows: /// ```plain /// AttValue := '"' (([^<&"]) | Reference)* '"' /// | "'" (([^<&']) | Reference)* "'" /// ``` /// (`Reference` is something like `"`, but we don't care about escaped characters at this /// level) #[inline] fn read_elem_until(r: &mut R, end_byte: u8, buf: &mut Vec) -> Result { #[derive(Clone, Copy)] enum State { /// The initial state (inside element, but outside of attribute value) Elem, /// Inside a single-quoted attribute value SingleQ, /// Inside a double-quoted attribute value DoubleQ, } let mut state = State::Elem; let mut read = 0; let mut done = false; while !done { let used = { let available = match r.fill_buf() { Ok(n) if n.is_empty() => return Ok(read), Ok(n) => n, Err(ref e) if e.kind() == io::ErrorKind::Interrupted => continue, Err(e) => return Err(Error::Io(e)), }; let mut memiter = memchr::memchr3_iter(end_byte, b'\'', b'"', available); let used: usize; loop { match memiter.next() { Some(i) => { state = match (state, available[i]) { (State::Elem, b) if b == end_byte => { // only allowed to match `end_byte` while we are in state `Elem` buf.extend_from_slice(&available[..i]); done = true; used = i + 1; break; } (State::Elem, b'\'') => State::SingleQ, (State::Elem, b'\"') => State::DoubleQ, // the only end_byte that gets us out if the same character (State::SingleQ, b'\'') | (State::DoubleQ, b'\"') => State::Elem, // all other bytes: no state change _ => state, }; } None => { buf.extend_from_slice(available); used = available.len(); break; } } } used }; r.consume(used); read += used; } Ok(read) } /// A function to check whether the byte is a whitespace (blank, new line, carriage return or tab) #[inline] pub(crate) fn is_whitespace(b: u8) -> bool { match b { b' ' | b'\r' | b'\n' | b'\t' => true, _ => false, } } /// A namespace declaration. Can either bind a namespace to a prefix or define the current default /// namespace. #[derive(Debug)] struct Namespace { /// Index of the namespace in the buffer start: usize, /// Length of the prefix /// * if bigger than start, then binds this namespace to the corresponding slice. /// * else defines the current default namespace. prefix_len: usize, /// The namespace name (the URI) of this namespace declaration. /// /// The XML standard specifies that an empty namespace value 'removes' a namespace declaration /// for the extent of its scope. For prefix declarations that's not very interesting, but it is /// vital for default namespace declarations. With `xmlns=""` you can revert back to the default /// behaviour of leaving unqualified element names unqualified. value_len: usize, /// Level of nesting at which this namespace was declared. The declaring element is included, /// i.e., a declaration on the document root has `level = 1`. /// This is used to pop the namespace when the element gets closed. level: i32, } impl Namespace { /// Gets the value slice out of namespace buffer /// /// Returns `None` if `value_len == 0` #[inline] fn opt_value<'a, 'b>(&'a self, ns_buffer: &'b [u8]) -> Option<&'b [u8]> { if self.value_len == 0 { None } else { let start = self.start + self.prefix_len; Some(&ns_buffer[start..start + self.value_len]) } } /// Check if the namespace matches the potentially qualified name #[inline] fn is_match(&self, ns_buffer: &[u8], qname: &[u8]) -> bool { if self.prefix_len == 0 { !qname.contains(&b':') } else { qname.get(self.prefix_len).map_or(false, |n| *n == b':') && qname.starts_with(&ns_buffer[self.start..self.start + self.prefix_len]) } } } /// A namespace management buffer. /// /// Holds all internal logic to push/pop namespaces with their levels. #[derive(Debug, Default)] struct NamespaceBufferIndex { /// a buffer of namespace ranges slices: Vec, /// The number of open tags at the moment. We need to keep track of this to know which namespace /// declarations to remove when we encounter an `End` event. nesting_level: i32, /// For `Empty` events keep the 'scope' of the element on the stack artificially. That way, the /// consumer has a chance to use `resolve` in the context of the empty element. We perform the /// pop as the first operation in the next `next()` call. pending_pop: bool, } impl NamespaceBufferIndex { #[inline] fn find_namespace_value<'a, 'b, 'c>( &'a self, element_name: &'b [u8], buffer: &'c [u8], ) -> Option<&'c [u8]> { self.slices .iter() .rfind(|n| n.is_match(buffer, element_name)) .and_then(|n| n.opt_value(buffer)) } fn pop_empty_namespaces(&mut self, buffer: &mut Vec) { if !self.pending_pop { return; } self.pending_pop = false; self.nesting_level -= 1; let current_level = self.nesting_level; // from the back (most deeply nested scope), look for the first scope that is still valid match self.slices.iter().rposition(|n| n.level <= current_level) { // none of the namespaces are valid, remove all of them None => { buffer.clear(); self.slices.clear(); } // drop all namespaces past the last valid namespace Some(last_valid_pos) => { if let Some(len) = self.slices.get(last_valid_pos + 1).map(|n| n.start) { buffer.truncate(len); self.slices.truncate(last_valid_pos + 1); } } } } fn push_new_namespaces(&mut self, e: &BytesStart, buffer: &mut Vec) { self.nesting_level += 1; let level = self.nesting_level; // adds new namespaces for attributes starting with 'xmlns:' and for the 'xmlns' // (default namespace) attribute. for a in e.attributes().with_checks(false) { if let Ok(Attribute { key: k, value: v }) = a { if k.starts_with(b"xmlns") { match k.get(5) { None => { let start = buffer.len(); buffer.extend_from_slice(&*v); self.slices.push(Namespace { start: start, prefix_len: 0, value_len: v.len(), level: level, }); } Some(&b':') => { let start = buffer.len(); buffer.extend_from_slice(&k[6..]); buffer.extend_from_slice(&*v); self.slices.push(Namespace { start: start, prefix_len: k.len() - 6, value_len: v.len(), level: level, }); } _ => break, } } } else { break; } } } /// Resolves a potentially qualified **attribute name** into (namespace name, local name). /// /// *Qualified* attribute names have the form `prefix:local-name` where the`prefix` is defined /// on any containing XML element via `xmlns:prefix="the:namespace:uri"`. The namespace prefix /// can be defined on the same element as the attribute in question. /// /// *Unqualified* attribute names do *not* inherit the current *default namespace*. #[inline] fn resolve_namespace<'a, 'b, 'c>( &'a self, qname: &'b [u8], buffer: &'c [u8], use_default: bool, ) -> (Option<&'c [u8]>, &'b [u8]) { self.slices .iter() .rfind(|n| n.is_match(buffer, qname)) .map_or((None, qname), |n| { let len = n.prefix_len; if len > 0 { (n.opt_value(buffer), &qname[len + 1..]) } else if use_default { (n.opt_value(buffer), qname) } else { (None, qname) } }) } } /// Utf8 Decoder #[cfg(not(feature = "encoding"))] #[derive(Clone, Copy)] pub struct Decoder; /// Utf8 Decoder #[cfg(feature = "encoding")] #[derive(Clone, Copy)] pub struct Decoder { encoding: &'static Encoding, } impl Decoder { #[cfg(not(feature = "encoding"))] pub fn decode<'c>(&self, bytes: &'c [u8]) -> Result<&'c str> { from_utf8(bytes).map_err(Error::Utf8) } #[cfg(feature = "encoding")] pub fn decode<'c>(&self, bytes: &'c [u8]) -> Cow<'c, str> { self.encoding.decode(bytes).0 } } quick-xml-0.17.2/src/se/mod.rs010064400017500001750000000232521357605207100142710ustar0000000000000000//! Module to handle custom serde `Serializer` mod var; use self::var::{Map, Seq, Struct}; use crate::{ errors::serialize::DeError, events::{BytesEnd, BytesStart, BytesText, Event}, writer::Writer, }; use serde::ser::{self, Impossible, Serialize}; use std::io::Write; /// Serialize struct into a `Write`r pub fn to_writer(writer: W, value: &S) -> Result<(), DeError> { let mut serializer = Serializer::new(writer); value.serialize(&mut serializer) } /// Serialize struct into a `String` pub fn to_string(value: &S) -> Result { let mut writer = Vec::new(); to_writer(&mut writer, value)?; let s = String::from_utf8(writer).map_err(|e| crate::errors::Error::Utf8(e.utf8_error()))?; Ok(s) } /// A Serializer pub struct Serializer { writer: Writer, } impl Serializer { /// Creates a new `Serializer` pub fn new(writer: W) -> Self { Serializer { writer: Writer::new(writer), } } fn write_primitive( &mut self, value: P, escaped: bool, ) -> Result<(), DeError> { let value = value.to_string().into_bytes(); let event = if escaped { BytesText::from_escaped(value) } else { BytesText::from_plain(&value) }; self.writer.write_event(Event::Text(event))?; Ok(()) } } impl<'w, W: Write> ser::Serializer for &'w mut Serializer { type Ok = (); type Error = DeError; type SerializeSeq = Seq<'w, W>; type SerializeTuple = Impossible; type SerializeTupleStruct = Impossible; type SerializeTupleVariant = Impossible; type SerializeMap = Map<'w, W>; type SerializeStruct = Struct<'w, W>; type SerializeStructVariant = Impossible; fn serialize_bool(self, v: bool) -> Result { self.write_primitive(if v { "true" } else { "false" }, true) } fn serialize_i8(self, v: i8) -> Result { self.write_primitive(v, true) } fn serialize_i16(self, v: i16) -> Result { self.write_primitive(v, true) } fn serialize_i32(self, v: i32) -> Result { self.write_primitive(v, true) } fn serialize_i64(self, v: i64) -> Result { self.write_primitive(v, true) } fn serialize_u8(self, v: u8) -> Result { self.write_primitive(v, true) } fn serialize_u16(self, v: u16) -> Result { self.write_primitive(v, true) } fn serialize_u32(self, v: u32) -> Result { self.write_primitive(v, true) } fn serialize_u64(self, v: u64) -> Result { self.write_primitive(v, true) } fn serialize_f32(self, v: f32) -> Result { self.write_primitive(v, true) } fn serialize_f64(self, v: f64) -> Result { self.write_primitive(v, true) } fn serialize_char(self, v: char) -> Result { self.write_primitive(v, false) } fn serialize_str(self, value: &str) -> Result { self.write_primitive(value, false) } fn serialize_bytes(self, _value: &[u8]) -> Result { // TODO: I imagine you'd want to use base64 here. // Not sure how to roundtrip effectively though... Err(DeError::Unsupported("serialize_bytes")) } fn serialize_none(self) -> Result { Ok(()) } fn serialize_some(self, value: &T) -> Result { value.serialize(self) } fn serialize_unit(self) -> Result { self.serialize_none() } fn serialize_unit_struct(self, name: &'static str) -> Result { self.writer .write_event(Event::Empty(BytesStart::borrowed_name(name.as_bytes())))?; Ok(()) } fn serialize_unit_variant( self, _name: &'static str, _variant_index: u32, _variant: &'static str, ) -> Result { Err(DeError::Unsupported("serialize_unit_variant")) } fn serialize_newtype_struct( self, _name: &'static str, _value: &T, ) -> Result { Err(DeError::Unsupported("serialize_newtype_struct")) } fn serialize_newtype_variant( self, _name: &'static str, _variant_index: u32, variant: &'static str, value: &T, ) -> Result { self.writer .write_event(Event::Start(BytesStart::borrowed_name(variant.as_bytes())))?; value.serialize(&mut *self)?; self.writer .write_event(Event::End(BytesEnd::borrowed(variant.as_bytes())))?; Ok(()) } fn serialize_seq(self, _len: Option) -> Result { Ok(Seq::new(self)) } fn serialize_tuple(self, _len: usize) -> Result { Err(DeError::Unsupported("serialize_tuple")) } fn serialize_tuple_struct( self, _name: &'static str, _len: usize, ) -> Result { Err(DeError::Unsupported("serialize_tuple_struct")) } fn serialize_tuple_variant( self, _name: &'static str, _variant_index: u32, _variant: &'static str, _len: usize, ) -> Result { Err(DeError::Unsupported("serialize_tuple_variant")) } fn serialize_map(self, _len: Option) -> Result { Ok(Map::new(self)) } fn serialize_struct( self, name: &'static str, _len: usize, ) -> Result { self.writer .write_event(Event::Start(BytesStart::borrowed_name(name.as_bytes())))?; Ok(Struct::new(self, name)) } fn serialize_struct_variant( self, _name: &'static str, _variant_index: u32, _variant: &'static str, _len: usize, ) -> Result { Err(DeError::Unsupported("serialize_struct_variant")) } } #[cfg(test)] mod tests { use super::*; use serde::ser::{SerializeMap, SerializeStruct}; use serde::{Serialize, Serializer as SerSerializer}; #[test] fn test_serialize_bool() { let inputs = vec![(true, "true"), (false, "false")]; for (src, should_be) in inputs { let mut buffer = Vec::new(); { let mut ser = Serializer::new(&mut buffer); ser.serialize_bool(src).unwrap(); } let got = String::from_utf8(buffer).unwrap(); assert_eq!(got, should_be); } } #[test] fn test_start_serialize_struct() { let mut buffer = Vec::new(); { let mut ser = Serializer::new(&mut buffer); let _ = ser.serialize_struct("foo", 0).unwrap(); } let got = String::from_utf8(buffer).unwrap(); assert_eq!(got, ""); } #[test] fn test_serialize_struct_field() { let mut buffer = Vec::new(); { let mut ser = Serializer::new(&mut buffer); let mut struct_ser = Struct::new(&mut ser, "baz"); struct_ser.serialize_field("foo", "bar").unwrap(); } let got = String::from_utf8(buffer).unwrap(); assert_eq!(got, "bar"); } #[test] fn test_serialize_struct() { #[derive(Serialize)] struct Person { name: String, age: u32, } let bob = Person { name: "Bob".to_string(), age: 42, }; let should_be = "Bob42"; let mut buffer = Vec::new(); { let mut ser = Serializer::new(&mut buffer); bob.serialize(&mut ser).unwrap(); } let got = String::from_utf8(buffer).unwrap(); assert_eq!(got, should_be); } #[test] fn test_serialize_map_entries() { let should_be = "Bob5"; let mut buffer = Vec::new(); { let mut ser = Serializer::new(&mut buffer); let mut map = Map::new(&mut ser); map.serialize_entry("name", "Bob").unwrap(); map.serialize_entry("age", "5").unwrap(); } let got = String::from_utf8(buffer).unwrap(); assert_eq!(got, should_be); } #[test] fn test_serialize_enum() { #[derive(Serialize)] #[allow(dead_code)] enum Node { Boolean(bool), Number(f64), String(String), } let mut buffer = Vec::new(); let should_be = "true"; { let mut ser = Serializer::new(&mut buffer); let node = Node::Boolean(true); node.serialize(&mut ser).unwrap(); } let got = String::from_utf8(buffer).unwrap(); assert_eq!(got, should_be); } #[test] #[ignore] fn serialize_a_list() { let inputs = vec![1, 2, 3, 4]; let mut buffer = Vec::new(); { let mut ser = Serializer::new(&mut buffer); inputs.serialize(&mut ser).unwrap(); } let got = String::from_utf8(buffer).unwrap(); println!("{}", got); panic!(); } } quick-xml-0.17.2/src/se/var.rs010064400017500001750000000071171357605207100143040ustar0000000000000000use crate::{ errors::{serialize::DeError, Error}, events::{BytesEnd, BytesStart, Event}, se::Serializer, }; use serde::ser::{self, Serialize}; use std::io::Write; /// An implementation of `SerializeMap` for serializing to XML. pub struct Map<'w, W> where W: 'w + Write, { parent: &'w mut Serializer, } impl<'w, W> Map<'w, W> where W: 'w + Write, { /// Create a new Map pub fn new(parent: &'w mut Serializer) -> Map<'w, W> { Map { parent } } } impl<'w, W> ser::SerializeMap for Map<'w, W> where W: 'w + Write, { type Ok = (); type Error = DeError; fn serialize_key(&mut self, _: &T) -> Result<(), DeError> { Err(DeError::Unsupported( "impossible to serialize the key on its own, please use serialize_entry()", )) } fn serialize_value(&mut self, value: &T) -> Result<(), DeError> { value.serialize(&mut *self.parent) } fn end(self) -> Result { Ok(()) } fn serialize_entry( &mut self, key: &K, value: &V, ) -> Result<(), DeError> { // TODO: Is it possible to ensure our key is never a composite type? // Anything which isn't a "primitive" would lead to malformed XML here... write!(self.parent.writer.inner(), "<").map_err(Error::Io)?; key.serialize(&mut *self.parent)?; write!(self.parent.writer.inner(), ">").map_err(Error::Io)?; value.serialize(&mut *self.parent)?; write!(self.parent.writer.inner(), "").map_err(Error::Io)?; Ok(()) } } /// An implementation of `SerializeStruct` for serializing to XML. pub struct Struct<'w, W> where W: 'w + Write, { parent: &'w mut Serializer, name: &'w str, } impl<'w, W> Struct<'w, W> where W: 'w + Write, { /// Create a new `Struct` pub fn new(parent: &'w mut Serializer, name: &'w str) -> Struct<'w, W> { Struct { parent, name } } } impl<'w, W> ser::SerializeStruct for Struct<'w, W> where W: 'w + Write, { type Ok = (); type Error = DeError; fn serialize_field( &mut self, key: &'static str, value: &T, ) -> Result<(), DeError> { let key = key.as_bytes(); self.parent .writer .write_event(Event::Start(BytesStart::borrowed_name(key)))?; value.serialize(&mut *self.parent)?; self.parent .writer .write_event(Event::End(BytesEnd::borrowed(key)))?; Ok(()) } fn end(self) -> Result { self.parent .writer .write_event(Event::End(BytesEnd::borrowed(self.name.as_bytes())))?; Ok(()) } } /// An implementation of `SerializeSeq' for serializing to XML. pub struct Seq<'w, W> where W: 'w + Write, { parent: &'w mut Serializer, } impl<'w, W> Seq<'w, W> where W: 'w + Write, { /// Create a new `Seq` pub fn new(parent: &'w mut Serializer) -> Seq<'w, W> { Seq { parent } } } impl<'w, W> ser::SerializeSeq for Seq<'w, W> where W: 'w + Write, { type Ok = (); type Error = DeError; fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> where T: Serialize, { value.serialize(&mut *self.parent)?; Ok(()) } fn end(self) -> Result { Ok(()) } } quick-xml-0.17.2/src/utils.rs010064400017500001750000000026571357035401500142460ustar0000000000000000pub fn write_byte_string(f: &mut std::fmt::Formatter<'_>, byte_string: &[u8]) -> std::fmt::Result { write!(f, "\"")?; for b in byte_string { match *b { 32..=33 | 35..=126 => write!(f, "{}", *b as char)?, 34 => write!(f, "\\\"")?, _ => write!(f, "{:#02X}", b)?, } } write!(f, "\"")?; Ok(()) } #[cfg(test)] mod tests { use super::*; struct ByteString(Vec); impl std::fmt::Debug for ByteString { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write_byte_string(f, &self.0) } } #[test] fn write_byte_string0() { let bytes = ByteString(vec![10, 32, 32, 32, 32, 32, 32, 32, 32]); assert_eq!(format!("{:?}", bytes), "\"0xA \"".to_owned()); } #[test] fn write_byte_string1() { let bytes = ByteString(vec![ 104, 116, 116, 112, 58, 47, 47, 119, 119, 119, 46, 119, 51, 46, 111, 114, 103, 47, 50, 48, 48, 50, 47, 48, 55, 47, 111, 119, 108, 35, ]); assert_eq!( format!("{:?}", bytes), r##""http://www.w3.org/2002/07/owl#""##.to_owned() ); } #[test] fn write_byte_string3() { let bytes = ByteString(vec![ 67, 108, 97, 115, 115, 32, 73, 82, 73, 61, 34, 35, 66, 34, ]); assert_eq!(format!("{:?}", bytes), r##""Class IRI=\"#B\"""##.to_owned()); } } quick-xml-0.17.2/src/writer.rs010064400017500001750000000133401357406636400144240ustar0000000000000000//! A module to handle `Writer` use std::io::Write; use errors::{Error, Result}; use events::Event; /// XML writer. /// /// Writes XML `Event`s to a `Write` implementor. /// /// # Examples /// /// ```rust /// # extern crate quick_xml; /// # fn main() { /// use quick_xml::{Reader, Writer}; /// use quick_xml::events::{Event, BytesEnd, BytesStart}; /// use std::io::Cursor; /// /// let xml = r#"text"#; /// let mut reader = Reader::from_str(xml); /// reader.trim_text(true); /// let mut writer = Writer::new(Cursor::new(Vec::new())); /// let mut buf = Vec::new(); /// loop { /// match reader.read_event(&mut buf) { /// Ok(Event::Start(ref e)) if e.name() == b"this_tag" => { /// /// // crates a new element ... alternatively we could reuse `e` by calling /// // `e.into_owned()` /// let mut elem = BytesStart::owned(b"my_elem".to_vec(), "my_elem".len()); /// /// // collect existing attributes /// elem.extend_attributes(e.attributes().map(|attr| attr.unwrap())); /// /// // copy existing attributes, adds a new my-key="some value" attribute /// elem.push_attribute(("my-key", "some value")); /// /// // writes the event to the writer /// assert!(writer.write_event(Event::Start(elem)).is_ok()); /// }, /// Ok(Event::End(ref e)) if e.name() == b"this_tag" => { /// assert!(writer.write_event(Event::End(BytesEnd::borrowed(b"my_elem"))).is_ok()); /// }, /// Ok(Event::Eof) => break, /// // we can either move or borrow the event to write, depending on your use-case /// Ok(e) => assert!(writer.write_event(&e).is_ok()), /// Err(e) => panic!("{}", e), /// } /// buf.clear(); /// } /// /// let result = writer.into_inner().into_inner(); /// let expected = r#"text"#; /// assert_eq!(result, expected.as_bytes()); /// # } /// ``` #[derive(Clone)] pub struct Writer { /// underlying writer writer: W, indent: Option, } impl Writer { /// Creates a Writer from a generic Write pub fn new(inner: W) -> Writer { Writer { writer: inner, indent: None, } } /// Creates a Writer with configured whitespace indents from a generic Write pub fn new_with_indent(inner: W, indent_char: u8, indent_size: usize) -> Writer { Writer { writer: inner, indent: Some(Indentation::new(indent_char, indent_size)), } } /// Consumes this `Writer`, returning the underlying writer. pub fn into_inner(self) -> W { self.writer } /// Get inner writer, keeping ownership pub fn inner(&mut self) -> &mut W { &mut self.writer } /// Writes the given event to the underlying writer. pub fn write_event<'a, E: AsRef>>(&mut self, event: E) -> Result { let mut next_should_line_break = true; let result = match *event.as_ref() { Event::Start(ref e) => { let result = self.write_wrapped(b"<", e, b">"); if let Some(i) = self.indent.as_mut() { i.grow(); } result } Event::End(ref e) => { if let Some(i) = self.indent.as_mut() { i.shrink(); } self.write_wrapped(b"") } Event::Empty(ref e) => self.write_wrapped(b"<", e, b"/>"), Event::Text(ref e) => { next_should_line_break = false; self.write(&e.escaped()) } Event::Comment(ref e) => self.write_wrapped(b""), Event::CData(ref e) => self.write_wrapped(b""), Event::Decl(ref e) => self.write_wrapped(b""), Event::PI(ref e) => self.write_wrapped(b""), Event::DocType(ref e) => self.write_wrapped(b""), Event::Eof => Ok(0), }; if let Some(i) = self.indent.as_mut() { i.should_line_break = next_should_line_break; } result } /// Writes bytes #[inline] pub fn write(&mut self, value: &[u8]) -> Result { self.writer.write(value).map_err(Error::Io) } #[inline] fn write_wrapped(&mut self, before: &[u8], value: &[u8], after: &[u8]) -> Result { let mut wrote = 0; if let Some(ref i) = self.indent { if i.should_line_break { wrote = self.writer.write(b"\n").map_err(Error::Io)? + self .writer .write(&i.indents[..i.indents_len]) .map_err(Error::Io)?; } } Ok(wrote + self.write(before)? + self.write(value)? + self.write(after)?) } } #[derive(Clone)] struct Indentation { should_line_break: bool, indent_char: u8, indent_size: usize, indents: Vec, indents_len: usize, } impl Indentation { fn new(indent_char: u8, indent_size: usize) -> Indentation { Indentation { should_line_break: false, indent_char, indent_size, indents: vec![indent_char; 128], indents_len: 0, } } fn grow(&mut self) { self.indents_len = self.indents_len + self.indent_size; if self.indents_len > self.indents.len() { self.indents.resize(self.indents_len, self.indent_char); } } fn shrink(&mut self) { self.indents_len = self.indents_len - self.indent_size; } } quick-xml-0.17.2/tests/aws_test.rs010064400017500001750000000025471357605207100153130ustar0000000000000000#![cfg(feature = "serialize")] extern crate quick_xml; extern crate serde; use quick_xml::{de::from_str, se::to_string}; use serde::{Deserialize, Serialize}; type Err = Box; #[derive(Serialize, Deserialize, Debug, PartialEq)] struct GetBucketTaggingOutput { #[serde(rename = "TagSet")] tag_set: TagSet, } #[derive(Serialize, Deserialize, Debug, PartialEq)] struct TagSet { #[serde(rename = "Tag")] tags: Vec, } #[derive(Serialize, Deserialize, Debug, PartialEq)] struct Tag { /// Name of the tag. #[serde(rename = "Key")] key: String, /// Value of the tag. #[serde(rename = "Value")] value: String, } #[test] fn get_bucket_tagging() -> Result<(), Err> { let src = " string string "; let bucket_tagging: GetBucketTaggingOutput = from_str(src).unwrap(); assert_eq!( bucket_tagging, GetBucketTaggingOutput { tag_set: TagSet { tags: vec![Tag { key: "string".to_string(), value: "string".to_string(), }], } } ); // roundtrip to_string(&from_str(src)?)?; Ok(()) } quick-xml-0.17.2/tests/documents/opennews_all.rss010064400017500001750000000541461332055316700203350ustar0000000000000000 OpenNews.opennet.ru: ïÂÝÁÑ ÌÅÎÔÁ ÎÏ×ÏÓÔÅÊ OpenNews - îÏ×ÏÓÔÉ ÍÉÒÁ ÏÔËÒÙÔÙÈ ÓÉÓÔÅÍ (ïÂÝÁÑ ÌÅÎÔÁ ÎÏ×ÏÓÔÅÊ) http://www.opennet.ru/opennews/ ÷ÙÐÕÓË Python 2.7.13 http://www.opennet.ru/opennews/art.shtml?num=45712 Sun, 18 Dec 2016 21:08:16 +0600 äÏÓÔÕÐÅÎ ËÏÒÒÅËÔÉÒÕÀÝÉÊ ×ÙÐÕÓË Python 2.7.13, × ËÏÔÏÒÏÍ ÏÔÒÁÖÅÎÙ ×ÎÅÓ£ÎÎÙÅ ÚÁ ÐÏÓÌÅÄÎÉÅ ÐÏÌÇÏÄÁ ÉÓÐÒÁ×ÌÅÎÉÑ ÏÛÉÂÏË. ðÏÄÄÅÒÖËÁ ×ÅÔËÉ Python 2.7 ÂÕÄÅÔ ÏÓÕÝÅÓÔ×ÌÑÔØÓÑ ÄÏ 2020 ÇÏÄÁ. óÌÅÄÕÀÝÉÊ ×ÙÐÕÓË Python 2.7.14 ÚÁÐÌÁÎÉÒÏ×ÁÎ ÎÁ ÓÅÒÅÄÉÎÕ 2017 ÇÏÄÁ. ÷ÙÐÕÓË ÓÌÅÄÕÀÝÅÊ ÚÎÁÞÉÔÅÌØÎÏÊ ×ÅÔËÉ Python 3.6 ÏÖÉÄÁÅÔÓÑ 23 ÄÅËÁÂÒÑ. http://www.opennet.ru/opennews/art.shtml?num=45712 ÷ÙÐÕÓË óõâä PouchDB 6.1, ÒÅÁÌÉÚÁÃÉÉ CouchDB ÎÁ JavaScript http://www.opennet.ru/opennews/art.shtml?num=45710 Sun, 18 Dec 2016 11:41:17 +0600 ðÒÅÄÓÔÁ×ÌÅÎ ÒÅÌÉÚ ÄÏËÕÍÅÎÔ-ÏÒÉÅÎÔÉÒÏ×ÁÎÎÏÊ âä PouchDB 6.1, ÐÒÅÄÓÔÁ×ÌÑÀÝÅÊ ÓÏÂÏÊ ÒÁÓÛÉÒÅÎÎÙÊ ×ÁÒÉÁÎÔ óõâä Apache CouchDB, ÎÁÐÉÓÁÎÎÙÊ ÎÁ ÑÚÙËÅ JavaScript É ÒÁÂÏÔÁÀÝÉÊ ×ÎÕÔÒÉ ÂÒÁÕÚÅÒÁ. íÏÄÅÌØ ÈÒÁÎÅÎÉÑ ÐÏ×ÔÏÒÑÅÔ CouchDB É ÏÂÅÓÐÅÞÉ×ÁÅÔ ÓÒÅÄÓÔ×Á ÒÁÚÒÅÛÅÎÉÑ ËÏÎÆÌÉËÔÏ×. PouchDB ÓÏ×ÍÅÓÔÉÍ Ó CouchDB ÎÁ ÕÒÏ×ÎÅ API ÄÌÑ ÈÒÁÎÅÎÉÑ É ×ÙÂÏÒËÉ ÄÁÎÎÙÈ. ëÏÄ ÒÁÓÐÒÏÓÔÒÁÎÑÅÔÓÑ ÐÏÄ ÌÉÃÅÎÚÉÅÊ Apache 2.0. òÁÚÍÅÒ ÓÖÁÔÏÇÏ ÁÒÈÉ×Á Ó PouchDB ÚÁÎÉÍÁÅÔ ×ÓÅÇÏ 45 ëÂ. http://www.opennet.ru/opennews/art.shtml?num=45710 äÏÓÔÕÐÎÁ ÏÔËÒÙÔÁÑ óõâä CrateDB 1.0 http://www.opennet.ru/opennews/art.shtml?num=45707 Sat, 17 Dec 2016 23:09:55 +0600 ðÏÓÌÅ ÔÒ£È ÌÅÔ ÒÁÚÒÁÂÏÔËÉ ÓÏÓÔÏÑÌÓÑ ÒÅÌÉÚ ÐÒÏÅËÔÁ CrateDB 1.0, × ÒÁÍËÁÈ ËÏÔÏÒÏÇÏ ÒÁÚ×É×ÁÅÔÓÑ ÏÔËÒÙÔÁÑ, ÂÙÓÔÒÁÑ É ÍÁÓÛÔÁÂÉÒÕÅÍÁÑ óõâä Ó ÐÏÄÄÅÒÖËÏÊ ×ÙÐÏÌÎÅÎÉÑ SQL-ÚÁÐÒÏÓÏ× É ×ÓÔÒÏÅÎÎÙÍÉ ×ÏÚÍÏÖÎÏÓÔÑÍÉ ÐÏÌÎÏÔÅËÓÔÏ×ÏÇÏ ÐÏÉÓËÁ. ÷ÅÒÓÉÑ 1.0 ÐÏÚÉÃÉÏÎÉÒÕÅÔÓÑ ËÁË ÐÅÒ×ÙÊ ×ÙÐÕÓË, ÄÏÓÔÉÇÛÉÊ ÄÏÌÖÎÏÇÏ ÕÒÏ×ÎÑ ÓÔÁÂÉÌØÎÏÓÔÉ É ÐÒÉÇÏÄÎÙÊ ÄÌÑ ÐÒÏÍÙÛÌÅÎÎÏÇÏ ÉÓÐÏÌØÚÏ×ÁÎÉÑ. éÓÈÏÄÎÙÅ ÔÅËÓÔÙ CrateDB ÎÁÐÉÓÁÎÙ ÎÁ ÑÚÙËÅ Java É. http://www.opennet.ru/opennews/art.shtml?num=45707 ÷ÙÐÕÓË ÄÉÓÔÒÉÂÕÔÉ×Á MX-16 Linux http://www.opennet.ru/opennews/art.shtml?num=45705 Sat, 17 Dec 2016 08:37:10 +0600 ïÐÕÂÌÉËÏ×ÁÎ ÒÅÌÉÚ ÌÅÇËÏ×ÅÓÎÏÇÏ ÄÉÓÔÒÉÂÕÔÉ×Á MX-16 Linux, ÓÏÚÄÁÎÎÏÇÏ × ÒÅÚÕÌØÔÁÔÅ ÓÏ×ÍÅÓÔÎÏÊ ÒÁÂÏÔÙ ÓÏÏÂÝÅÓÔ×, ÏÂÒÁÚÏ×Á×ÛÉÈÓÑ ×ÏËÒÕÇ ÐÒÏÅËÔÏ× antiX É MEPIS. ÷ÙÐÕÓË ÏÓÎÏ×ÁÎ ÐÁËÅÔÎÏÊ ÂÁÚÅ ÎÁ Debian Jessie (8.6) Ó ÕÌÕÞÛÅÎÉÑÍÉ ÏÔ ÐÒÏÅËÔÁ antiX É ÍÎÏÇÏÞÉÓÌÅÎÎÙÍÉ ÓÏÂÓÔ×ÅÎÎÙÍÉ ÐÒÉÌÏÖÅÎÉÑÍÉ, ÏÂÌÅÇÞÁÀÝÉÍÉ ÎÁÓÔÒÏÊËÕ É ÕÓÔÁÎÏ×ËÕ ðï. ðÏ ÕÍÏÌÞÁÎÉÀ ÐÒÅÄÌÁÇÁÅÔÓÑ ÒÁÂÏÞÉÊ ÓÔÏÌ Xfce 4.12.2. äÌÑ ÚÁÇÒÕÚËÉ ÄÏÓÔÕÐÎÙ 32- É 64-ÒÁÚÒÑÄÎÙÅ ÓÂÏÒËÉ, ÒÁÚÍÅÒÏÍ 1.2 GB. http://www.opennet.ru/opennews/art.shtml?num=45705 ÷ÙÐÕÓË ÄÉÓÔÒÉÂÕÔÉ×Á GoboLinux 016 Ó ÓÁÍÏÂÙÔÎÏÊ ÉÅÒÁÒÈÉÅÊ ÆÁÊÌÏ×ÏÊ ÓÉÓÔÅÍÙ http://www.opennet.ru/opennews/art.shtml?num=45704 Fri, 16 Dec 2016 22:10:38 +0600 ðÏÓÌÅ Ä×ÕÈ Ó ÐÏÌÏ×ÉÎÏÊ ÌÅÔ Ó ÍÏÍÅÎÔÁ ÐÒÏÛÌÏÇÏ ×ÙÐÕÓËÁ ÓÆÏÒÍÉÒÏ×ÁÎ ÒÅÌÉÚ ÄÉÓÔÒÉÂÕÔÉ×Á GoboLinux 016. ÷ GoboLinux ×ÍÅÓÔÏ ÔÒÁÄÉÃÉÏÎÎÏÊ ÄÌÑ Unix-ÓÉÓÔÅÍ ÉÅÒÁÒÈÉÉ ÆÁÊÌÏ× ÉÓÐÏÌØÚÕÅÔÓÑ ÓÔÅËÏ×ÁÑ ÍÏÄÅÌØ ÆÏÒÍÉÒÏ×ÁÎÉÑ ÄÅÒÅ×Á ËÁÔÁÌÏÇÏ×, ÐÒÉ ËÏÔÏÒÏÊ ËÁÖÄÁÑ ÐÒÏÇÒÁÍÍÁ ÕÓÔÁÎÁ×ÌÉ×ÁÅÔÓÑ × ÏÔÄÅÌØÎÕÀ ÄÉÒÅËÔÏÒÉÀ. òÁÚÍÅÒ ÕÓÔÁÎÏ×ÏÞÎÏÇÏ ÏÂÒÁÚÁ 950 íÂ, ËÏÔÏÒÙÊ ÔÁËÖÅ ÍÏÖÅÔ ÐÒÉÍÅÎÑÔØÓÑ ÄÌÑ ÏÚÎÁËÏÍÌÅÎÉÑ Ó ×ÏÚÍÏÖÎÏÓÔÑÍÉ ÄÉÓÔÒÉÂÕÔÉ×Á × Live-ÒÅÖÉÍÅ. http://www.opennet.ru/opennews/art.shtml?num=45704 Ubuntu ÐÅÒÅÈÏÄÉÔ ÎÁ ÉÓÐÏÌØÚÏ×ÁÎÉÅ ÆÁÊÌÁ ÐÏÄËÁÞËÉ ×ÍÅÓÔÏ ÒÁÚÄÅÌÁ ÐÏÄËÁÞËÉ http://www.opennet.ru/opennews/art.shtml?num=45703 Fri, 16 Dec 2016 19:30:42 +0600 òÁÚÒÁÂÏÔÞÉËÉ Ubuntu Linux ÐÒÉÎÑÌÉ ÒÅÛÅÎÉÅ ÏÔËÁÚÁÔØÓÑ ÏÔ ÉÓÐÏÌØÚÏ×ÁÎÉÑ ÏÔÄÅÌØÎÙÈ ÒÁÚÄÅÌÏ× ÐÏÄËÁÞËÉ ÄÌÑ ÕÓÔÁÎÏ×ÏË ÄÉÓÔÒÉÂÕÔÉ×Á, ÎÅ ÉÓÐÏÌØÚÕÀÝÉÈ ÓÉÓÔÅÍÕ ÌÏÇÉÞÅÓËÉÈ ÔÏÍÏ× LVM, × ÐÏÌØÚÕ ÒÁÚÍÅÝÅÎÉÑ SWAP × ÆÁÊÌÅ. éÚÍÅÎÅÎÉÅ ÂÕÄÅÔ ÐÒÉÍÅÎÅÎÏ × ÂÌÉÖÁÊÛÅÍ ×ÙÐÕÓËÅ Ubuntu 17.04. òÁÚÍÅÒ ÆÁÊÌÁ ÐÏÄËÁÞËÉ ÐÏ ÕÍÏÌÞÁÎÉÀ ÂÕÄÅÔ ÕÓÔÁÎÁ×ÌÉ×ÁÔØÓÑ × 5% ÏÔ Ó×ÏÂÏÄÎÏÇÏ ÍÅÓÔÁ × æó, ÎÏ ÎÅ ÂÏÌØÛÅ, ÞÅÍ 2 çÂ. éÓÐÏÌØÚÏ×ÁÎÉÅ ÆÁÊÌÁ × ÓÕÝÅÓÔ×ÕÀÝÅÊ æó ÄÌÑ ÈÒÁÎÅÎÉÑ ÄÁÎÎÙÈ ÐÏÄËÁÞËÉ ÓÕÝÅÓÔ×ÅÎÎÏ Õ×ÅÌÉÞÉ×ÁÅÔ ÇÉÂËÏÓÔØ É ÐÏÚ×ÏÌÑÅÔ × ÌÀÂÏÅ ×ÒÅÍÑ ÉÚÍÅÎÉÔØ ÒÁÚÍÅÒ ÐÏÄËÁÞËÉ. http://www.opennet.ru/opennews/art.shtml?num=45703 ÷ÙÐÕÓË ÒÁÂÏÞÅÇÏ ÓÔÏÌÁ Budgie 10.2.9 http://www.opennet.ru/opennews/art.shtml?num=45702 Fri, 16 Dec 2016 19:19:08 +0600 òÁÚÒÁÂÏÔÞÉËÉ ÄÉÓÔÒÉÂÕÔÉ×Á Solus ÐÒÅÄÓÔÁ×ÉÌÉ ÒÅÌÉÚ ÒÁÂÏÞÅÇÏ ÓÔÏÌÁ Budgie Desktop 10.2.9. Budgie ÏÓÎÏ×ÁÎ ÎÁ ÔÅÈÎÏÌÏÇÉÑÈ GNOME, ÎÏ ÉÓÐÏÌØÚÕÅÔ ÓÏÂÓÔ×ÅÎÎÙÅ ÒÅÁÌÉÚÁÃÉÉ ÏÂÏÌÏÞËÉ GNOME Shell, ÐÁÎÅÌÉ, ÁÐÐÌÅÔÏ× É ÓÉÓÔÅÍÙ ×Ù×ÏÄÁ Õ×ÅÄÏÍÌÅÎÉÊ. Budgie ÎÅ Ñ×ÌÑÅÔÓÑ ÆÏÒËÏÍ GNOME É ÒÁÂÏÔÁÅÔ ÐÏ×ÅÒÈ ÛÔÁÔÎÙÈ ÎÉÚËÏÕÒÏ×ÎÅ×ÙÈ ËÏÍÐÏÎÅÎÔÏ× É ÂÉÂÌÉÏÔÅË GNOME. ëÏÄ ÐÒÏÅËÔÁ ÎÁÐÉÓÁÎ ÎÁ ÑÚÙËÁÈ óÉ É Vala É ÒÁÓÐÒÏÓÔÒÁÎÑÅÔÓÑ ÐÏÄ ÌÉÃÅÎÚÉÅÊ GPLv2. http://www.opennet.ru/opennews/art.shtml?num=45702 ÷ÙÐÕÓË ÄÉÓÔÒÉÂÕÔÉ×Á Linux Mint 18.1 http://www.opennet.ru/opennews/art.shtml?num=45701 Fri, 16 Dec 2016 17:11:05 +0600 ðÒÅÄÓÔÁ×ÌÅÎ ÒÅÌÉÚ ÄÉÓÔÒÉÂÕÔÉ×Á Linux Mint 18.1, ÐÅÒ×ÏÇÏ ÏÂÎÏ×ÌÅÎÉÑ ×ÅÔËÉ Linux Mint 18.x, ÆÏÒÍÉÒÕÅÍÏÊ ÎÁ ÐÁËÅÔÎÏÊ ÂÁÚÅ Ubuntu 16.04 LTS É ÐÏÄÄÅÒÖÉ×ÁÅÍÏÊ ÄÏ 2021 ÇÏÄÁ. äÉÓÔÒÉÂÕÔÉ× ÐÏÌÎÏÓÔØÀ ÓÏ×ÍÅÓÔÉÍ Ó Ubuntu, ÎÏ ÓÕÝÅÓÔ×ÅÎÎÏ ÏÔÌÉÞÁÅÔÓÑ ÐÏÄÈÏÄÏÍ Ë ÏÒÇÁÎÉÚÁÃÉÉ ÉÎÔÅÒÆÅÊÓÁ ÐÏÌØÚÏ×ÁÔÅÌÑ É ÐÏÄÂÏÒÏÍ ÉÓÐÏÌØÚÕÅÍÙÈ ÐÏ ÕÍÏÌÞÁÎÉÀ ÐÒÉÌÏÖÅÎÉÊ. òÁÚÒÁÂÏÔÞÉËÉ Linux Mint ÐÒÅÄÏÓÔÁ×ÌÑÀÔ ÄÅÓËÔÏÐ-ÏËÒÕÖÅÎÉÅ, ÓÏÏÔ×ÅÔÓÔ×ÕÀÝÅÅ ËÌÁÓÓÉÞÅÓËÉÍ ËÁÎÏÎÁÍ ÏÒÇÁÎÉÚÁÃÉÉ ÒÁÂÏÞÅÇÏ ÓÔÏÌÁ, ËÏÔÏÒÏÅ Ñ×ÌÑÅÔÓÑ ÂÏÌÅÅ ÐÒÉ×ÙÞÎÙÍ ÄÌÑ ÐÏÌØÚÏ×ÁÔÅÌÅÊ, ÎÅ ÐÒÉÎÉÍÁÀÝÉÈ ÎÏ×ÙÅ ÍÅÔÏÄÙ ÐÏÓÔÒÏÅÎÉÑ ÉÎÔÅÒÆÅÊÓÁ Unity É GNOME 3. äÌÑ ÚÁÇÒÕÚËÉ ÄÏÓÔÕÐÎÙ DVD-ÓÂÏÒËÉ ÎÁ ÂÁÚÅ ÏÂÏÌÏÞÅË MATE 1.16 (1.7 çÂ) É Cinnamon 3.2 (1.7 çÂ). http://www.opennet.ru/opennews/art.shtml?num=45701 õÑÚ×ÉÍÏÓÔØ, ÐÏÚ×ÏÌÑÀÝÁÑ ÚÁÐÕÓÔÉÔØ ËÏÄ ÐÒÉ ËÏÐÉÒÏ×ÁÎÉÉ ÆÁÊÌÁ ÎÁ ÒÁÂÏÞÉÊ ÓÔÏÌ http://www.opennet.ru/opennews/art.shtml?num=45700 Fri, 16 Dec 2016 09:45:21 +0600 ëÒÉÓ ü×ÁÎÓ (Chris Evans), ÉÚ×ÅÓÔÎÙÊ ÜËÓÐÅÒÔ ÐÏ ËÏÍÐØÀÔÅÒÎÏÊ ÂÅÚÏÐÁÓÎÏÓÔÉ É Á×ÔÏÒ ÚÁÝÉÝÅÎÎÏÇÏ FTP-ÓÅÒ×ÅÒÁ vsftpd, ÐÒÏÄÏÌÖÉÌ ÄÅÍÏÎÓÔÒÁÃÉÀ ÎÅÚÁÝÉÝ£ÎÎÏÓÔÉ ÍÕÌØÔÉÍÅÄÉÊÎÏÇÏ ÆÒÅÊÍ×ÏÒËÁ Gstreamer É ÐÒÅÄÓÔÁ×ÉÌ ÎÏ×ÕÀ 0-day ÕÑÚ×ÉÍÏÓÔØ, ÐÏÚ×ÏÌÑÀÝÕÀ ÏÒÇÁÎÉÚÏ×ÁÔØ ×ÙÐÏÌÎÅÎÉÅ ËÏÄÁ ÐÒÉ ÐÏÐÙÔËÅ ÏÂÒÁÂÏÔËÉ ÓÐÅÃÉÁÌØÎÙÍ ÏÂÒÁÚÏÍ ÏÆÏÒÍÌÅÎÎÏÇÏ ÍÕÌØÔÉÍÅÄÉÊÎÏÇÏ ËÏÎÔÅÎÔÁ. ÷ ÍÏÍÅÎÔ ÐÕÂÌÉËÁÃÉÉ ÜËÓÐÌÏÉÔÙ ÐÏÚ×ÏÌÑÀÔ ÏÒÇÁÎÉÚÏ×ÁÔØ ÓÔÁÂÉÌØÎÏ ÐÏ×ÔÏÒÑÅÍÕÀ ÁÔÁËÕ ÐÒÏÔÉ× Ubuntu 16.04 LTS É Fedora 25 Ó ÓÁÍÙÍÉ Ó×ÅÖÉÍÉ ÏÂÎÏ×ÌÅÎÉÑÍÉ. http://www.opennet.ru/opennews/art.shtml?num=45700 ÷ÙÐÕÓË KDE Applications 16.12 http://www.opennet.ru/opennews/art.shtml?num=45699 Thu, 15 Dec 2016 23:33:27 +0600 óÏÓÔÏÑÌÓÑ ÒÅÌÉÚ ÎÁÂÏÒÁ KDE Applications 16.12, ×ËÌÀÞÁÀÝÅÇÏ ÐÏÄÂÏÒËÕ ÐÏÌØÚÏ×ÁÔÅÌØÓËÉÈ ÐÒÉÌÏÖÅÎÉÊ, ÁÄÁÐÔÉÒÏ×ÁÎÎÙÈ ÄÌÑ ÒÁÂÏÔÙ Ó KDE Frameworks 5. îÁÂÏÒ KDE Applications ÐÒÉÛ£Ì ÎÁ ÓÍÅÎÕ ÐÒÉÌÏÖÅÎÉÑÍ ÉÚ ÓÏÓÔÁ×Á KDE SC, ËÏÔÏÒÙÅ ÔÅÐÅÒØ ÒÁÚ×É×ÁÀÔÓÑ × ÒÁÍËÁÈ ÏÔÄÅÌØÎÏÇÏ ÃÉËÌÁ ÒÁÚÒÁÂÏÔËÉ, ÎÅ ÐÒÉ×ÑÚÁÎÎÏÇÏ Ë ×ÅÔËÁÍ KDE, É ×ÙÐÕÓËÁÀÔÓÑ ÐÏ ÎÏ×ÏÊ ÓÈÅÍÅ ÎÕÍÅÒÁÃÉÉ ×ÅÒÓÉÊ. éÎÆÏÒÍÁÃÉÀ Ï ÎÁÌÉÞÉÉ Live-ÓÂÏÒÏË Ó ÎÏ×ÙÍ ×ÙÐÕÓËÏÍ ÍÏÖÎÏ ÐÏÌÕÞÉÔØ ÄÁÎÎÏÊ ÓÔÒÁÎÉÃÅ. http://www.opennet.ru/opennews/art.shtml?num=45699 LibreOffice ÔÅÓÔÉÒÕÅÔ ÁÌØÔÅÒÎÁÔÉ×ÎÕÀ ÐÁÎÅÌØ × ÓÔÉÌÅ Ribbon http://www.opennet.ru/opennews/art.shtml?num=45698 Thu, 15 Dec 2016 21:02:59 +0600 ÷ ÂÅÔÁ-×ÅÒÓÉÉ LibreOffice 5.3 ÄÏÓÔÕÐÎÁ ÄÌÑ ÏÚÎÁËÏÍÌÅÎÉÑ ÎÏ×ÁÑ ÐÁÎÅÌØ ÉÎÓÔÒÕÍÅÎÔÏ× NotebookBar, ÏÆÏÒÍÌÅÎÎÁÑ × ÓÔÉÌÅ Ribbon. ðÁÎÅÌØ ÐÏÚÉÃÉÏÎÉÒÕÅÔÓÑ ËÁË ÁÌØÔÅÒÎÁÔÉ×Á ÄÌÑ ÐÏÌØÚÏ×ÁÔÅÌÅÊ, ÐÒÉ×ÙËÛÉÈ Ë ÐÁÎÅÌÉ Microsoft Office. http://www.opennet.ru/opennews/art.shtml?num=45698 Red Hat Enterprise Linux 7 ÐÏÌÕÞÉÌ ÓÅÒÔÉÆÉËÁÔ ÂÅÚÏÐÁÓÎÏÓÔÉ FIPS 140-2 http://www.opennet.ru/opennews/art.shtml?num=45696 Thu, 15 Dec 2016 19:14:58 +0600 ëÏÍÐÁÎÉÑ Red Hat ÓÏÏÂÝÉÌÁ Ï ÓÅÒÔÉÆÉËÁÃÉÉ ËÒÉÐÔÏÇÒÁÆÉÞÅÓËÉÈ ÍÏÄÕÌÅÊ ÄÉÓÔÒÉÂÕÔÉ×Á Red Hat Enterprise Linux 7 (RHEL) ÎÁ ÐÒÅÄÍÅÔ ÓÏÏÔ×ÅÔÓÔ×ÉÑ ÓÔÁÎÄÁÒÔÕ ÂÅÚÏÐÁÓÎÏÓÔÉ FIPS 140-2. òÁÎÅÅ ÓÅÒÔÉÆÉËÁÃÉÑ FIPS 140-2 ÂÙÌÁ ÐÒÏÊÄÅÎÁ × 2013 ÇÏÄÕ ÄÌÑ ×ÅÔËÉ RHEL 6.x, ÎÏ ËÏÍÐÏÎÅÎÔÙ ÉÚ ×ÅÔËÉ RHEL 7 ÏÓÔÁ×ÁÌÁÓØ ÎÅÓÅÒÔÉÆÉÃÉÒÏ×ÁÎÙ. ðÏÌÕÞÅÎÉÅ ÓÅÒÔÉÆÉËÁÔÁ FIPS 140-2 Ñ×ÌÑÅÔÓÑ ÏÂÑÚÁÔÅÌØÎÙÍ ÔÒÅÂÏ×ÁÎÉÅÍ Ë ÐÒÏÄÕËÔÁÍ ÐÒÉ ÉÈ ÉÓÐÏÌØÚÏ×ÁÎÉÉ × ÏÂÅÓÐÅÞÅÎÉÉ ÚÁÝÉÔÙ ËÏÎÆÉÄÅÎÃÉÁÌØÎÙÈ ÄÁÎÎÙÈ ÇÏÓÕÄÁÒÓÔ×ÅÎÎÙÈ ÕÞÒÅÖÄÅÎÉÊ óûá É ëÁÎÁÄÙ. óÅÒÔÉÆÉËÁÔ ÐÏÄÔ×ÅÒÖÄÁÅÔ ÓÏÂÌÀÄÅÎÉÅ ÔÒÅÂÏ×ÁÎÉÊ Ë ËÒÉÐÔÏÇÒÁÆÉÞÅÓËÉÍ ÍÏÄÕÌÑÍ É ×ÙÄÁ£ÔÓÑ áÍÅÒÉËÁÎÓËÉÍ ÉÎÓÔÉÔÕÔÏÍ ÓÔÁÎÄÁÒÔÏ× É ÔÅÈÎÏÌÏÇÉÊ (NIST) ÓÏ×ÍÅÓÔÎÏ Ó ëÁÎÁÄÓËÉÍ ãÅÎÔÒÏÍ ÂÅÚÏÐÁÓÎÏÓÔÉ ËÏÍÍÕÎÉËÁÃÉÊ ÐÏÓÌÅ ÄÏÓËÏÎÁÌØÎÏÇÏ ÔÅÓÔÉÒÏ×ÁÎÉÑ ËÏÍÐÏÎÅÎÔÏ× ÄÉÓÔÒÉÂÕÔÉ×Á ÎÅÚÁ×ÉÓÉÍÏÊ ÔÅÓÔÏ×ÏÊ ÌÁÂÏÒÁÔÏÒÉÅÊ. http://www.opennet.ru/opennews/art.shtml?num=45696 ÷ÙÐÕÓË web-ÂÒÁÕÚÅÒÁ Vivaldi 1.6 http://www.opennet.ru/opennews/art.shtml?num=45695 Thu, 15 Dec 2016 17:45:47 +0600 ðÒÅÄÓÔÁ×ÌÅÎ ×ÙÐÕÓË ÐÒÏÐÒÉÅÔÁÒÎÏÇÏ web-ÂÒÁÕÚÅÒÁ Vivaldi 1.6, ÒÁÚÒÁÂÁÔÙ×ÁÅÍÏÇÏ ÎÁ ÂÁÚÅ Ä×ÉÖËÁ Chromium É ÐÒÏÄÏÌÖÁÀÝÅÇÏ ÒÁÚ×ÉÔÉÅ ÉÄÅÊ ËÌÁÓÓÉÞÅÓËÏÇÏ ÂÒÁÕÚÅÒÁ Opera, ÐÒÅÄÏÓÔÁ×ÌÑÑ ÛÉÒÏËÉÊ ÓÐÅËÔÒ ×ÏÚÍÏÖÎÏÓÔÅÊ, ×ËÌÀÞÁÑ ÕÄÏÂÎÕÀ ÓÉÓÔÅÍÕ ÇÒÕÐÐÉÒÏ×ËÉ ×ËÌÁÄÏË, ÂÏËÏ×ÕÀ ÐÁÎÅÌØ, ËÏÎÆÉÇÕÒÁÔÏÒ Ó ÂÏÌØÛÉÍ ÞÉÓÌÏÍ ÎÁÓÔÒÏÅË, ÒÅÖÉÍ ÂÌÏËÉÒÏ×ËÉ ÉÚÏÂÒÁÖÅÎÉÊ É ÎÅÖÅÌÁÔÅÌØÎÏÇÏ ËÏÎÔÅÎÔÁ, ÓÉÓÔÅÍÕ ×ÅÄÅÎÉÑ ÚÁÍÅÔÏË, ÒÅÖÉÍ ÇÏÒÉÚÏÎÔÁÌØÎÏÇÏ ÏÔÏÂÒÁÖÅÎÉÑ ×ËÌÁÄÏË. éÎÔÅÒÆÅÊÓ ÂÒÁÕÚÅÒÁ ÎÁÐÉÓÁÎ ÎÁ ÑÚÙËÅ JavaScript Ó ÉÓÐÏÌØÚÏ×ÁÎÉÅÍ ÂÉÂÌÉÏÔÅËÉ React, ÐÌÁÔÆÏÒÍÙ Node.js, Browserify É ÒÁÚÌÉÞÎÙÈ ÇÏÔÏ×ÙÈ NPM-ÍÏÄÕÌÅÊ. óÂÏÒËÉ Vivaldi ÐÏÄÇÏÔÏ×ÌÅÎÙ ÄÌÑ Linux, Windows É macOS. ðÒÏÅËÔ ÒÁÓÐÒÏÓÔÒÁÎÑÅÔ ÐÏÄ ÏÔËÒÙÔÏÊ ÌÉÃÅÎÚÉÅÊ ÉÓÈÏÄÎÙÅ ÔÅËÓÔÙ ÉÚÍÅÎÅÎÉÊ Ë Chromium (ÐÕÂÌÉËÕÀÔÓÑ ÄÌÑ ÐÒÏÛÌÙÈ ×ÙÐÕÓËÏ×). òÅÁÌÉÚÁÃÉÑ ÉÎÔÅÒÆÅÊÓÁ Vivaldi ÎÁÐÉÓÁÎÁ ÎÁ JavaScript, ÄÏÓÔÕÐÎÁ × ÉÓÈÏÄÎÙÈ ÔÅËÓÔÁÈ, ÎÏ ÐÏÄ ÐÒÏÐÒÉÅÔÁÒÎÏÊ ÌÉÃÅÎÚÉÅÊ. http://www.opennet.ru/opennews/art.shtml?num=45695 ÷ÙÐÕÓË ÄÉÓÔÒÉÂÕÔÉ×Á GeckoLinux Static http://www.opennet.ru/opennews/art.shtml?num=45692 Thu, 15 Dec 2016 11:49:26 +0600 ðÒÅÄÓÔÁ×ÌÅÎÁ ÎÏ×ÁÑ ×ÅÒÓÉÑ ÄÉÓÔÒÉÂÕÔÉ×Á GeckoLinux Static, × ÒÁÍËÁÈ ËÏÔÏÒÏÇÏ ÐÏÄÇÏÔÏ×ÌÅÎÁ ÓÐÅÃÉÁÌÉÚÉÒÏ×ÁÎÎÁÑ ÒÅÄÁËÃÉÑ openSUSE Leap 42.2. ëÒÏÍÅ GeckoLinux Static ÔÁËÖÅ ÄÏÓÔÕÐÎÙ Rolling ÒÅÄÁËÃÉÉ, ÏÓÎÏ×ÁÎÎÙÅ ÎÁ ÐÁËÅÔÁÈ ÉÚ openSUSE Tumbleweed. òÁÚÍÅÒ iso-ÏÂÒÁÚÁ ÏËÏÌÏ 1 çÂ. http://www.opennet.ru/opennews/art.shtml?num=45692 Yahoo ÓÏÏÂÝÉÌ Ï ÕÔÅÞËÅ ÂÏÌÅÅ ÍÉÌÌÉÁÒÄÁ ÕÞ£ÔÎÙÈ ÚÁÐÉÓÅÊ http://www.opennet.ru/opennews/art.shtml?num=45691 Thu, 15 Dec 2016 10:52:24 +0600 ëÏÍÐÁÎÉÑ Yahoo ÒÁÓËÒÙÌÁ Ó×ÅÄÅÎÉÑ Ï ÐÒÏÉÚÏÛÅÄÛÅÊ × Á×ÇÕÓÔÅ 2013 ÇÏÄÁ ÕÔÅÞËÅ ÂÏÌÅÅ ÍÉÌÌÉÁÒÄÁ ÕÞ£ÔÎÙÈ ÚÁÐÉÓÅÊ, × ÒÅÚÕÌØÔÁÔÅ ËÏÔÏÒÏÊ × ÒÕËÉ ÚÌÏÕÍÙÛÌÅÎÎÉËÏ× ÐÏÐÁÌÉ ÌÉÞÎÙÅ Ó×ÅÄÅÎÉÑ Ï ÐÏÌØÚÏ×ÁÔÅÌÑÈ, × ÔÏÍ ÞÉÓÌÅ ÈÜÛÉ ÐÁÒÏÌÅÊ, ÒÅÁÌØÎÙÅ ÉÍÅÎÁ ÐÏÌØÚÏ×ÁÔÅÌÅÊ É ÔÅÌÅÆÏÎÎÙÅ ÎÏÍÅÒÁ. æÁËÔ ×ÚÌÏÍÁ ÂÙÌ ×ÙÑ×ÌÅÎ ÐÏÓÌÅ ÐÏÐÁÄÁÎÉÑ × ÒÕËÉ ÐÒÁ×ÏÏÈÒÁÎÉÔÅÌØÎÙÈ ÏÒÇÁÎÏ× ËÏÐÉÉ ÂÁÚÙ ÐÏÌØÚÏ×ÁÔÅÌÅÊ, ËÏÔÏÒÁÑ ÐÏÓÌÅ ÐÒÏ×ÅÒËÉ ÏËÁÚÁÌÁÓØ Ó×ÑÚÁÎÁ Ó Yahoo. õÔ×ÅÒÖÄÁÅÔÓÑ, ÞÔÏ ÓËÏÒÅÅ ×ÓÅÇÏ ÎÙÎÅÛÎÑÑ ÕÔÅÞËÁ ÎÅ ÉÍÅÅÔ Ó×ÑÚÉ Ó ÏÂÎÁÒÏÄÏ×ÁÎÎÙÍÉ × ÓÅÎÔÑÂÒÑ Ó×ÅÄÅÎÉÑÍÉ Ï ÄÒÕÇÏÊ ÕÔÅÞËÅ ÂÁÚÙ Yahoo × 500 ÍÌÎ ÐÏÌØÚÏ×ÁÔÅÌÅÊ, ËÏÔÏÒÁÑ ÐÒÏÉÚÏÛÌÁ × 2014 ÇÏÄÕ × ÒÅÚÕÌØÔÁÔÅ ÄÒÕÇÏÇÏ ÉÎÃÉÄÅÎÔÁ. http://www.opennet.ru/opennews/art.shtml?num=45691 òÅÌÉÚ ÓÉÓÔÅÍÙ ÏÂÎÁÒÕÖÅÎÉÑ ÁÔÁË Snort 2.9.9.0 http://www.opennet.ru/opennews/art.shtml?num=45693 Thu, 15 Dec 2016 10:50:28 +0600 ëÏÍÐÁÎÉÑ Cisco ÏÐÕÂÌÉËÏ×ÁÌÁ ÎÏ×ÙÊ ÚÎÁÞÉÔÅÌØÎÙÊ ÒÅÌÉÚ Snort 2.9.9.0, Ó×ÏÂÏÄÎÏÊ ÓÉÓÔÅÍÙ ÏÂÎÁÒÕÖÅÎÉÑ É ÐÒÅÄÏÔ×ÒÁÝÅÎÉÑ ÁÔÁË, ËÏÍÂÉÎÉÒÕÀÝÅÊ × ÓÅÂÅ ÍÅÔÏÄÙ ÓÏÐÏÓÔÁ×ÌÅÎÉÑ ÐÏ ÓÉÇÎÁÔÕÒÁÍ, ÓÒÅÄÓÔ×Á ÄÌÑ ÉÎÓÐÅËÃÉÉ ÐÒÏÔÏËÏÌÏ× É ÍÅÈÁÎÉÚÍÙ ÄÌÑ ×ÙÑ×ÌÅÎÉÑ ÁÎÏÍÁÌÉÊ. http://www.opennet.ru/opennews/art.shtml?num=45693 ðÒÏÅËÔ LLVM ÐÅÒÅÈÏÄÉÔ ÎÁ ÎÏ×ÕÀ ÓÈÅÍÕ ÎÕÍÅÒÁÃÉÉ ×ÙÐÕÓËÏ× http://www.opennet.ru/opennews/art.shtml?num=45690 Thu, 15 Dec 2016 10:16:57 +0600 òÁÚÒÁÂÏÔÞÉËÉ ÐÒÏÅËÔÁ LLVM ÏÂßÑ×ÉÌÉ Ï ÉÚÍÅÎÅÎÉÉ ÐÏÄÈÏÄÁ Ë ÐÒÉÓ×ÏÅÎÉÀ ÎÏÍÅÒÏ× ×ÅÒÓÉÊ. åÓÌÉ ÒÁÎØÛÅ ÐÒÉÍÅÎÑÌÁÓØ ÄÅÓÑÔÉÞÎÁÑ ÎÕÍÅÒÁÃÉÑ Ó Õ×ÅÌÉÞÅÎÉÅÍ ÐÅÒ×ÏÊ ÃÉÆÒÙ ÄÌÑ ÚÎÁÞÉÔÅÌØÎÙÈ ×ÙÐÕÓËÏ×, ×ÔÏÒÏÊ ÄÌÑ ÆÕÎËÃÉÏÎÁÌØÎÙÈ ÏÂÎÏ×ÌÅÎÉÊ (3.8.0, 3.9.0) É ÔÒÅÔØÅÊ ÄÌÑ ËÏÒÒÅËÔÉÒÕÀÝÉÈ ×ÙÐÕÓËÏ× (3.8.1, 3.8.2), ÔÏ ÔÅÐÅÒØ ÒÅÛÅÎÏ ÓÔÅÒÅÔØ ÇÒÁÎØ ÍÅÖÄÕ ÚÎÁÞÉÔÅÌØÎÙÍÉ É ÆÕÎËÃÉÏÎÁÌØÎÙÍÉ ×ÙÐÕÓËÁÍÉ, ÍÅÎÑÑ ÔÏÌØËÏ ÐÅÒ×ÕÀ ÃÉÆÒÕ (5.0.0, 6.0.0, 7.0.0). îÏ×ÁÑ ÓÈÅÍÁ ÂÕÄÅÔ ÐÒÉÍÅÎÅÎÁ ÎÁÞÉÎÁÑ Ó ÍÁÒÔÏ×ÓËÏÇÏ ×ÙÐÕÓËÁ 4.0.0, ÐÏÓÌÅ ÞÅÇÏ × ÓÅÎÔÑÂÒÅ ÏÖÉÄÁÅÔÓÑ ÒÅÌÉÚ LLVM 5.0.0. http://www.opennet.ru/opennews/art.shtml?num=45690 ÷ÙÐÕÓË strace 4.15 Ó ÆÕÎËÃÉÅÊ ÐÏÄÍÅÎÙ ÓÉÓÔÅÍÎÙÈ ×ÙÚÏ×Ï× http://www.opennet.ru/opennews/art.shtml?num=45689 Thu, 15 Dec 2016 08:50:57 +0600 äÏÓÔÕÐÎÁ ÎÏ×ÁÑ ×ÅÒÓÉÑ ÕÔÉÌÉÔÙ strace 4.15, ÐÒÅÄÎÁÚÎÁÞÅÎÎÏÊ ÄÌÑ ÄÉÁÇÎÏÓÔÉËÉ É ÏÔÌÁÄËÉ ÐÒÏÇÒÁÍÍ × ïó ÎÁ ÂÁÚÅ ÑÄÒÁ Linux. õÔÉÌÉÔÁ ÐÏÚ×ÏÌÑÅÔ ÏÔÓÌÅÖÉ×ÁÔØ É (ÎÁÞÉÎÁÑ Ó ÄÁÎÎÏÊ ×ÅÒÓÉÉ) ×ÍÅÛÉ×ÁÔØÓÑ × ÐÒÏÃÅÓÓ ×ÚÁÉÍÏÄÅÊÓÔ×ÉÑ ÐÒÏÇÒÁÍÍÙ É ÑÄÒÁ, ×ËÌÀÞÁÑ ÐÒÏÉÓÈÏÄÑÝÉÅ ÓÉÓÔÅÍÎÙÅ ×ÙÚÏ×Ù, ×ÏÚÎÉËÁÀÝÉÅ ÓÉÇÎÁÌÙ É ÉÚÍÅÎÅÎÉÑ ÓÏÓÔÏÑÎÉÑ ÐÒÏÃÅÓÓÁ. äÌÑ Ó×ÏÅÊ ÒÁÂÏÔÙ strace ÉÓÐÏÌØÚÕÅÔ ÍÅÈÁÎÉÚÍ ptrace. ëÏÄ ÐÒÏÅËÔÁ ÒÁÓÐÒÏÓÔÒÁÎÑÅÔÓÑ ÐÏÄ ÌÉÃÅÎÚÉÅÊ BSD. îÁÞÉÎÁÑ Ó ×ÅÒÓÉÉ 4.13, ÆÏÒÍÉÒÏ×ÁÎÉÅ ×ÙÐÕÓËÏ× strace ÓÉÎÈÒÏÎÉÚÉÒÏ×ÁÎÏ Ó ×ÙÈÏÄÏÍ ÏÞÅÒÅÄÎÙÈ ×ÅÒÓÉÊ Linux. http://www.opennet.ru/opennews/art.shtml?num=45689 òÅÌÉÚ CentOS 7.1611 ÄÌÑ ÁÒÈÉÔÅËÔÕÒÙ ARM http://www.opennet.ru/opennews/art.shtml?num=45687 Wed, 14 Dec 2016 23:46:16 +0600 óÌÅÄÏÍ ÚÁ ÓÂÏÒËÏÊ ÄÌÑ ÁÒÈÉÔÅËÔÕÒÙ x86_64 ÐÒÏÅËÔ CentOS ÏÂßÑ×ÉÌ Ï ÄÏÓÔÕÐÎÏÓÔÉ ÒÅÄÁËÃÉÉ ÄÉÓÔÒÉÂÕÔÉ×Á CentOS Linux 7.1611 ÄÌÑ 32-ÒÁÚÒÑÄÎÏÊ ÁÒÈÉÔÅËÔÕÒÙ Armhfp. óÂÏÒËÁ ÍÏÖÅÔ ÉÓÐÏÌØÚÏ×ÁÔØÓÑ ÎÁ ÐÌÁÔÁÈ Raspberry Pi 3, Raspberry Pi 2, óubieTruck, Cubieboard É Bananapi. ÷ ÄÁÌØÎÅÊÛÅÍ ÏÖÉÄÁÀÔÓÑ ×ÙÐÕÓËÉ ÄÌÑ ÁÒÈÉÔÅËÔÕÒ i386, ARM64, PowerPC64 É PowerPC8 LE. http://www.opennet.ru/opennews/art.shtml?num=45687 ÷ÙÐÕÓË Qt 5.7.1 É ÉÎÔÅÇÒÉÒÏ×ÁÎÎÏÊ ÓÒÅÄÙ ÒÁÚÒÁÂÏÔËÉ Qt Creator 4.2 http://www.opennet.ru/opennews/art.shtml?num=45686 Wed, 14 Dec 2016 23:00:41 +0600 äÏÓÔÕÐÅÎ ×ÙÐÕÓË ÉÎÔÅÇÒÉÒÏ×ÁÎÎÏÊ ÓÒÅÄÙ ÒÁÚÒÁÂÏÔËÉ Qt Creator 4.2.0, ÐÒÅÄÎÁÚÎÁÞÅÎÎÏÊ ÄÌÑ ÓÏÚÄÁÎÉÑ ËÒÏÓÓÐÌÁÔÆÏÒÍÅÎÎÙÈ ÐÒÉÌÏÖÅÎÉÊ Ó ÉÓÐÏÌØÚÏ×ÁÎÉÅÍ ÂÉÂÌÉÏÔÅËÉ Qt. ðÏÄÄÅÒÖÉ×ÁÅÔÓÑ ÒÁÚÒÁÂÏÔËÁ ËÁË ËÌÁÓÓÉÞÅÓËÉÈ ÐÒÏÇÒÁÍÍ ÎÁ ÑÚÙËÅ C++, ÔÁË É ÉÓÐÏÌØÚÏ×ÁÎÉÅ ÑÚÙËÁ QML, × ËÏÔÏÒÏÍ ÄÌÑ ÏÐÒÅÄÅÌÅÎÉÑ ÓÃÅÎÁÒÉÅ× ÉÓÐÏÌØÚÕÅÔÓÑ JavaScript, Á ÓÔÒÕËÔÕÒÁ É ÐÁÒÁÍÅÔÒÙ ÜÌÅÍÅÎÔÏ× ÉÎÔÅÒÆÅÊÓÁ ÚÁÄÁÀÔÓÑ CSS-ÐÏÄÏÂÎÙÍÉ ÂÌÏËÁÍÉ. http://www.opennet.ru/opennews/art.shtml?num=45686 òÅÌÉÚ ÒÁÓÔÒÏ×ÏÇÏ ÇÒÁÆÉÞÅÓËÏÇÏ ÒÅÄÁËÔÏÒÁ Krita 3.1 http://www.opennet.ru/opennews/art.shtml?num=45683 Wed, 14 Dec 2016 16:28:59 +0600 õ×ÉÄÅÌ Ó×ÅÔ ÒÅÌÉÚ ÒÁÓÔÒÏ×ÏÇÏ ÇÒÁÆÉÞÅÓËÏÇÏ ÒÅÄÁËÔÏÒÁ Krita 3.1, ÒÁÚ×É×ÁÅÍÏÇÏ ÄÌÑ ÈÕÄÏÖÎÉËÏ× É ÉÌÌÀÓÔÒÁÔÏÒÏ×. òÅÄÁËÔÏÒ ÐÏÄÄÅÒÖÉ×ÁÅÔ ÍÎÏÇÏÓÌÏÊÎÕÀ ÏÂÒÁÂÏÔËÕ ÉÚÏÂÒÁÖÅÎÉÊ, ÐÒÅÄÏÓÔÁ×ÌÑÅÔ ÓÒÅÄÓÔ×Á ÄÌÑ ÒÁÂÏÔÙ Ó ÒÁÚÌÉÞÎÙÍÉ Ã×ÅÔÏ×ÙÍÉ ÍÏÄÅÌÑÍÉ É ÏÂÌÁÄÁÅÔ ÂÏÌØÛÉÍ ÎÁÂÏÒÏÍ ÓÒÅÄÓÔ× ÄÌÑ ÃÉÆÒÏ×ÏÊ ÖÉ×ÏÐÉÓÉ, ÓÏÚÄÁÎÉÑ ÓËÅÔÞÅÊ É ÆÏÒÍÉÒÏ×ÁÎÉÑ ÔÅËÓÔÕÒ. äÌÑ ÕÓÔÁÎÏ×ËÉ ÐÏÄÇÏÔÏ×ÌÅÎÙ ÓÁÍÏÄÏÓÔÁÔÏÞÎÙÅ ÏÂÒÁÚÙ × ÆÏÒÍÁÔÁÈ AppImage É Snap ÄÌÑ Linux (ÄÌÑ Ubuntu ÄÏÐÏÌÎÉÔÅÌØÎÏ ÐÏÄÇÏÔÏ×ÌÅÎ PPA), Á ÔÁËÖÅ ÂÉÎÁÒÎÙÅ ÓÂÏÒËÉ ÄÌÑ macOS É Windows. http://www.opennet.ru/opennews/art.shtml?num=45683 ÷ÙÐÕÓË ÏÂÌÁÞÎÏÇÏ ÈÒÁÎÉÌÉÝÁ Nextcloud 11, ÆÏÒËÁ ownCloud http://www.opennet.ru/opennews/art.shtml?num=45682 Wed, 14 Dec 2016 11:44:59 +0600 ðÏÓÌÅ ÞÅÔÙÒ£È ÍÅÓÑÃÅ× ÒÁÚÒÁÂÏÔËÉ ÐÒÅÄÓÔÁ×ÌÅÎ ×ÙÐÕÓË ÏÂÌÁÞÎÏÊ ÐÌÁÔÆÏÒÍÙ Nextcloud 11, ÒÁÚ×É×ÁÀÝÅÊÓÑ ËÁË ÆÏÒË ÐÒÏÅËÔÁ ownCloud, ÓÏÚÄÁÎÎÙÊ ÏÓÎÏ×ÎÙÍÉ ÒÁÚÒÁÂÏÔÞÉËÁÍÉ ÄÁÎÎÏÊ ÓÉÓÔÅÍÙ. Nextcloud É ownCloud ÐÏÚ×ÏÌÑÀÔ ÎÁ Ó×ÏÉÈ ÓÅÒ×ÅÒÎÙÈ ÓÉÓÔÅÍÁÈ ÒÁÚ×ÅÒÎÕÔØ ÐÏÌÎÏÃÅÎÎÏÅ ÏÂÌÁÞÎÏÅ ÈÒÁÎÉÌÉÝÅ Ó ÐÏÄÄÅÒÖËÏÊ ÓÉÎÈÒÏÎÉÚÁÃÉÉ É ÏÂÍÅÎÁ ÄÁÎÎÙÍÉ. ëÌÀÞÅ×ÙÍ ÏÔÌÉÞÉÅÍ Nextcloud ÏÔ ownCloud Ñ×ÌÑÅÔÓÑ ÎÁÍÅÒÅÎÉÅ ÐÒÅÄÏÓÔÁ×ÉÔØ × ÅÄÉÎÏÍ ÏÔËÒÙÔÏÍ ÐÒÏÄÕËÔÅ ×ÓÅ ÒÁÓÛÉÒÅÎÎÙÅ ×ÏÚÍÏÖÎÏÓÔÉ, ÒÁÎÅÅ ÐÏÓÔÁ×ÌÑÅÍÙÅ ÔÏÌØËÏ × ËÏÍÍÅÒÞÅÓËÏÊ ×ÅÒÓÉÉ ownCloud. éÓÈÏÄÎÙÅ ÔÅËÓÔÙ Nextcloud, ËÁË É ownCloud, ÒÁÓÐÒÏÓÔÒÁÎÑÀÔÓÑ ÐÏÄ ÌÉÃÅÎÚÉÅÊ AGPL. http://www.opennet.ru/opennews/art.shtml?num=45682 îÏ×ÁÑ ×ÅÒÓÉÑ Tor Browser 6.0.8 http://www.opennet.ru/opennews/art.shtml?num=45681 Wed, 14 Dec 2016 10:04:23 +0600 òÁÚÒÁÂÏÔÞÉËÉ ÁÎÏÎÉÍÎÏÊ ÓÅÔÉ Tor ÐÒÅÄÓÔÁ×ÉÌÉ ÎÏ×ÙÊ ×ÙÐÕÓË ÒÁÚ×É×ÁÅÍÏÇÏ ÐÒÏÅËÔÏÍ web-ÂÒÁÕÚÅÒÁ Tor Browser 6.0.8, ÏÒÉÅÎÔÉÒÏ×ÁÎÎÏÇÏ ÎÁ ÏÂÅÓÐÅÞÅÎÉÅ ÁÎÏÎÉÍÎÏÓÔÉ, ÂÅÚÏÐÁÓÎÏÓÔÉ É ÐÒÉ×ÁÔÎÏÓÔÉ. ÷ ÎÏ×ÏÊ ×ÅÒÓÉÉ ÕÓÔÒÁÎÅÎÙ 18 ÕÑÚ×ÉÍÏÓÔÅÊ, ÉÓÐÒÁ×ÌÅÎÎÙÅ × ËÏÄÏ×ÏÊ ÂÁÚÅ Firefox 45.6.0esr. 11 ÕÑÚ×ÉÍÏÓÔÅÊ ÐÏÍÅÞÅÎÙ ËÁË ËÒÉÔÉÞÅÓËÉÅ, Ô.Å. ÍÏÇÕÔ ÐÒÉ×ÅÓÔÉ Ë ×ÙÐÏÌÎÅÎÉÀ ËÏÄÁ ÚÌÏÕÍÙÛÌÅÎÎÉËÁ ÐÒÉ ÏÔËÒÙÔÉÉ ÓÐÅÃÉÁÌØÎÏ ÏÆÏÒÍÌÅÎÎÙÈ ÓÔÒÁÎÉÃ. http://www.opennet.ru/opennews/art.shtml?num=45681 ÷ÙÐÕÓË nginx 1.11.7 http://www.opennet.ru/opennews/art.shtml?num=45680 Wed, 14 Dec 2016 08:18:23 +0600 äÏÓÔÕÐÅÎ ÎÏ×ÙÊ ×ÙÐÕÓË ÏÓÎÏ×ÎÏÊ ×ÅÔËÉ ×ÙÓÏËÏÐÒÏÉÚ×ÏÄÉÔÅÌØÎÏÇÏ HTTP-ÓÅÒ×ÅÒÁ nginx 1.11.7, × ËÏÔÏÒÏÍ ÒÅÁÌÉÚÏ×ÁÎÙ ÓÌÅÄÕÀÝÉÅ ÉÚÍÅÎÅÎÉÑ. http://www.opennet.ru/opennews/art.shtml?num=45680 Google ÐÒÅÄÓÔÁ×ÉÌ Android Things, ïó ÄÌÑ ÉÎÔÅÒÎÅÔÁ ×ÅÝÅÊ http://www.opennet.ru/opennews/art.shtml?num=45679 Tue, 13 Dec 2016 23:45:55 +0600 ëÏÍÐÁÎÉÑ Google ÎÁÞÁÌÁ ÔÅÓÔÉÒÏ×ÁÎÉÅ Android Things, ÎÏ×ÏÊ ÒÅÄÁËÃÉÉ ÐÌÁÔÆÏÒÍÙ Android, ÐÒÅÄÎÁÚÎÁÞÅÎÎÏÊ ÄÌÑ ÉÓÐÏÌØÚÏ×ÁÎÉÑ × ÐÏÔÒÅÂÉÔÅÌØÓËÉÈ ÉÎÔÅÒÎÅÔ-ÕÓÔÒÏÊÓÔ×ÁÈ, ÏÔÎÏÓÑÝÉÈÓÑ Ë ËÁÔÅÇÏÒÉÉ ÉÎÔÅÒÎÅÔ ×ÅÝÅÊ (IoT). Android Things ÄÁ£Ô ×ÏÚÍÏÖÎÏÓÔØ ÂÙÓÔÒÏ ÓÏÚÄÁ×ÁÔØ ÕÍÎÙÅ ÕÓÔÒÏÊÓÔ×Á, ÉÓÐÏÌØÚÕÑ API ÐÌÁÔÆÏÒÍÙ Android É ÓÅÒ×ÉÓÙ Google. http://www.opennet.ru/opennews/art.shtml?num=45679 òÅÌÉÚ Proxmox VE 4.4, ÄÉÓÔÒÉÂÕÔÉ×Á ÄÌÑ ÏÒÇÁÎÉÚÁÃÉÉ ÒÁÂÏÔÙ ×ÉÒÔÕÁÌØÎÙÈ ÓÅÒ×ÅÒÏ× http://www.opennet.ru/opennews/art.shtml?num=45676 Tue, 13 Dec 2016 21:25:18 +0600 äÏÓÔÕÐÅÎ ÒÅÌÉÚ Proxmox Virtual Environment 4.4, ÓÐÅÃÉÁÌÉÚÉÒÏ×ÁÎÎÏÇÏ Linux-ÄÉÓÔÒÉÂÕÔÉ×Á ÎÁ ÂÁÚÅ Debian GNU/Linux, ÎÁÃÅÌÅÎÎÏÇÏ ÎÁ ÒÁÚ×ÅÒÔÙ×ÁÎÉÅ É ÏÂÓÌÕÖÉ×ÁÎÉÅ ×ÉÒÔÕÁÌØÎÙÈ ÓÅÒ×ÅÒÏ× Ó ÉÓÐÏÌØÚÏ×ÁÎÉÅÍ LXC É KVM, É ÓÐÏÓÏÂÎÏÇÏ ×ÙÓÔÕÐÉÔØ × ÒÏÌÉ ÚÁÍÅÎÙ ÔÁËÉÈ ÐÒÏÄÕËÔÏ×, ËÁË VMware vSphere, Microsoft Hyper-V É Citrix XenServer. òÁÚÍÅÒ ÕÓÔÁÎÏ×ÏÞÎÏÇÏ iso-ÏÂÒÁÚÁ 521 íÂ. http://www.opennet.ru/opennews/art.shtml?num=45676 òÅÌÉÚ CrossOver 16 ÄÌÑ Linux É macOS http://www.opennet.ru/opennews/art.shtml?num=45675 Tue, 13 Dec 2016 20:09:38 +0600 ëÏÍÐÁÎÉÑ CodeWeavers ×ÙÐÕÓÔÉÌÁ ÒÅÌÉÚ ÐÁËÅÔÁ Crossover 16, ÏÓÎÏ×ÁÎÎÏÇÏ ÎÁ ËÏÄÅ Wine 2.0-rc É ÐÒÅÄÎÁÚÎÁÞÅÎÎÏÇÏ ÄÌÑ ×ÙÐÏÌÎÅÎÉÑ ÐÒÏÇÒÁÍÍ É ÉÇÒ, ÎÁÐÉÓÁÎÎÙÈ ÄÌÑ ÐÌÁÔÆÏÒÍÙ Windows. CodeWeavers ×ÈÏÄÉÔ × ÞÉÓÌÏ ËÌÀÞÅ×ÙÈ ÕÞÁÓÔÎÉËÏ× ÐÒÏÅËÔÁ Wine, ÓÐÏÎÓÉÒÕÅÔ ÅÇÏ ÒÁÚÒÁÂÏÔËÕ É ×ÏÚ×ÒÁÝÁÅÔ × ÐÒÏÅËÔ ×ÓÅ ÎÏ×ÛÅÓÔ×Á, ÒÅÁÌÉÚÏ×ÁÎÎÙÅ ÄÌÑ Ó×ÏÉÈ ËÏÍÍÅÒÞÅÓËÉÈ ÐÒÏÄÕËÔÏ×. éÓÈÏÄÎÙÅ ÔÅËÓÔÙ ÏÔËÒÙÔÙÈ ËÏÍÐÏÎÅÎÔÏ× CrossOver 16.0 ÍÏÖÎÏ ÚÁÇÒÕÚÉÔØ ÎÁ ÄÁÎÎÏÊ ÓÔÒÁÎÉÃÅ. http://www.opennet.ru/opennews/art.shtml?num=45675 ðÒÏÍÅÖÕÔÏÞÎÙÊ ×ÙÐÕÓË Firefox 50.1.0 http://www.opennet.ru/opennews/art.shtml?num=45674 Tue, 13 Dec 2016 19:44:49 +0600 äÏÓÔÕÐÅÎ ÐÒÏÍÅÖÕÔÏÞÎÙÊ ×ÙÐÕÓË Firefox 50.1.0, ×ÏÂÒÁ×ÛÉÊ × ÓÅÂÑ ÎÁËÏÐÉ×ÛÉÅÓÑ ÉÓÐÒÁ×ÌÅÎÉÑ. ïÄÎÏ×ÒÅÍÅÎÎÏ ÓÆÏÒÍÉÒÏ×ÁÎÏ ÏÂÎÏ×ÌÅÎÉÅ ÄÌÑ ESR-×ÅÔËÉ - Firefox 45.6. ÷ ÐÒÉÍÅÞÁÎÉÉ Ë ×ÙÐÕÓËÁÍ ÕÐÏÍÑÎÕÔÏ ÔÏÌØËÏ ÉÓÐÒÁ×ÌÅÎÉÅ ÕÑÚ×ÉÍÏÓÔÅÊ, ÎÏ ËÏÎËÒÅÔÎÙÅ Ó×ÅÄÅÎÉÑ Ï ÕÓÔÒÁΣÎÎÙÈ ÐÒÏÂÌÅÍÁÈ Ó ÂÅÚÏÐÁÓÎÏÓÔØÀ ÐÏËÁ ÎÅ ÏÐÕÂÌÉËÏ×ÁÎÙ. http://www.opennet.ru/opennews/art.shtml?num=45674 ÷ ÁÎÔÉ×ÉÒÕÓÅ McAfee ÄÌÑ Linux ×ÙÑ×ÌÅÎÁ ÕÄÁÌ£ÎÎÁÑ root-ÕÑÚ×ÉÍÏÓÔØ http://www.opennet.ru/opennews/art.shtml?num=45672 Tue, 13 Dec 2016 16:17:29 +0600 ÷ ÐÒÏÐÒÉÅÔÁÒÎÏÍ ÁÎÔÉ×ÉÒÕÓÎÏÍ ÐÁËÅÔÅ McAfee VirusScan Enterprise ÄÌÑ ÐÌÁÔÆÏÒÍÙ Linux ×ÙÑ×ÌÅÎÏ 10 ÕÑÚ×ÉÍÏÓÔÅÊ, ÓÏÞÅÔÁÎÉÅ ËÏÔÏÒÙÈ ÄÁ£Ô ×ÏÚÍÏÖÎÏÓÔØ ÏÒÇÁÎÉÚÏ×ÁÔØ ÕÄÁÌ£ÎÎÕÀ ÁÔÁËÕ, ËÏÔÏÒÁÑ ÐÏÚ×ÏÌÑÅÔ ÎÅÁÕÔÅÎÔÉÆÉÃÉÒÏ×ÁÎÎÏÍÕ ÓÔÏÒÏÎÎÅÍÕ ÚÌÏÕÍÙÛÌÅÎÎÉËÕ ×ÙÐÏÌÎÉÔØ Ó×ÏÊ ËÏÄ ÎÁ ÓÅÒ×ÅÒÅ Ó ÐÒÁ×ÁÍÉ ÐÏÌØÚÏ×ÁÔÅÌÑ root. ðÒÏÉÚ×ÏÄÉÔÅÌÀ ÂÙÌÏ ÓÏÏÂÝÅÎÏ Ï ÐÒÏÂÌÅÍÅ ÅÝ£ × ÉÀÎÅ, ÎÏ ÏÂÎÏ×ÌÅÎÉÅ Ó ÕÓÔÒÁÎÅÎÉÅÍ ÕÑÚ×ÉÍÏÓÔÉ ÂÙÌÏ ×ÙÐÕÝÅÎÏ ÔÏÌØËÏ ×ÞÅÒÁ. ôÁË ËÁË ÉÓÐÒÁ×ÌÅÎÉÅ ÕÖÅ ÄÏÓÔÕÐÎÏ, ÏÂÎÁÒÕÖÉ×ÛÉÅ ÕÑÚ×ÉÍÏÓÔÉ ÉÓÓÌÅÄÏ×ÁÔÅÌÉ ÂÅÚÏÐÁÓÎÏÓÔÉ ÏÐÕÂÌÉËÏ×ÁÌÉ ÒÁÂÏÞÉÊ ÐÒÏÔÏÔÉÐ ÜËÓÐÌÏÉÔÁ. http://www.opennet.ru/opennews/art.shtml?num=45672 ÷ÙÐÕÓË web-ÂÒÁÕÚÅÒÁ Opera 42 http://www.opennet.ru/opennews/art.shtml?num=45671 Tue, 13 Dec 2016 12:57:09 +0600 ðÏÄÇÏÔÏ×ÌÅÎ ×ÙÐÕÓË ÐÒÏÐÒÉÅÔÁÒÎÏÇÏ ÂÒÁÕÚÅÒÁ Opera 42, ÏÓÎÏ×ÁÎÎÏÇÏ ÎÁ ËÏÄÏ×ÏÊ ÂÁÚÅ Chromium. óÂÏÒËÉ ÓÆÏÒÍÉÒÏ×ÁÎÙ ÄÌÑ ÐÌÁÔÆÏÒÍ Linux, macOS É Windows. ÷ÙÐÕÓË ÐÒÉÕÒÏÞÅÎ Ë 20-ÌÅÔÉÀ Ó ÍÏÍÅÎÔÁ ÏÓÎÏ×ÁÎÉÑ ÂÒÁÕÚÅÒÁ. http://www.opennet.ru/opennews/art.shtml?num=45671 quick-xml-0.17.2/tests/documents/sample_1.xml010064400017500001750000000022011332061054700173160ustar0000000000000000 Some <java> class Another "java" class Weird 'XML' config JavaScript & program Cascading style sheet: © - ҉ quick-xml-0.17.2/tests/documents/sample_1_full.txt010064400017500001750000000033431332061054700203670ustar0000000000000000StartDocument(1.0, utf-8) StartElement(project [name="project-name"]) Characters( ) StartElement(libraries) Characters( ) EmptyElement(library [groupId="org.example", artifactId="", version="0.1"]) Characters( ) EmptyElement(library [groupId="com.example", artifactId=""cool-lib&", version="999"]) Characters( ) EndElement(libraries) Characters( ) StartElement(module [name="module-1"]) Characters( ) StartElement(files) Characters( ) StartElement(file [name="somefile.java", type="java"]) Characters( Some class ) EndElement(file) Characters( ) StartElement(file [name="another_file.java", type="java"]) Characters( Another "java" class ) EndElement(file) Characters( ) StartElement(file [name="config.xml", type="xml"]) Characters( Weird 'XML' config ) EndElement(file) Characters( ) EndElement(files) Characters( ) StartElement(libraries) Characters( ) EmptyElement(library [groupId="junit", artifactId="junit", version="1.9.5"]) Characters( ) EndElement(libraries) Characters( ) EndElement(module) Characters( ) StartElement(module [name="module-2"]) Characters( ) StartElement(files) Characters( ) StartElement(file [name="program.js", type="javascript"]) Characters( JavaScript & program ) EndElement(file) Characters( ) StartElement(file [name="style.css", type="css"]) Characters( Cascading style sheet: © - Ò‰ ) EndElement(file) Characters( ) EndElement(files) Characters( ) EndElement(module) Characters( ) EndElement(project) EndDocument quick-xml-0.17.2/tests/documents/sample_1_short.txt010064400017500001750000000022071332061054700205620ustar0000000000000000StartDocument(1.0, utf-8) StartElement(project [name="project-name"]) StartElement(libraries) EmptyElement(library [groupId="org.example", artifactId="", version="0.1"]) EmptyElement(library [groupId="com.example", artifactId=""cool-lib&", version="999"]) EndElement(libraries) StartElement(module [name="module-1"]) StartElement(files) StartElement(file [name="somefile.java", type="java"]) Characters(Some class) EndElement(file) StartElement(file [name="another_file.java", type="java"]) Characters(Another "java" class) EndElement(file) StartElement(file [name="config.xml", type="xml"]) Characters(Weird 'XML' config) EndElement(file) EndElement(files) StartElement(libraries) EmptyElement(library [groupId="junit", artifactId="junit", version="1.9.5"]) EndElement(libraries) EndElement(module) StartElement(module [name="module-2"]) StartElement(files) StartElement(file [name="program.js", type="javascript"]) Characters(JavaScript & program) EndElement(file) StartElement(file [name="style.css", type="css"]) Characters(Cascading style sheet: © - Ò‰) EndElement(file) EndElement(files) EndElement(module) EndElement(project) EndDocument quick-xml-0.17.2/tests/documents/sample_2.xml010064400017500001750000000007151332055316700173330ustar0000000000000000 Name Another name 0.3 0.2 0.1 0.01 header 1 value Some bigger value quick-xml-0.17.2/tests/documents/sample_2_full.txt010064400017500001750000000024021332061054700203630ustar0000000000000000StartDocument(1.0, utf-8) StartElement({urn:example:namespace}p:data) Characters( ) StartElement({urn:example:namespace}p:datum [id="34"]) Characters( ) StartElement({urn:example:namespace}p:name) Characters(Name) EndElement({urn:example:namespace}p:name) Characters( ) StartElement({urn:example:double}d:name) Characters(Another name) EndElement({urn:example:double}d:name) Characters( ) StartElement({urn:example:double}d:arg) Characters(0.3) EndElement({urn:example:double}d:arg) Characters( ) StartElement({urn:example:double}d:arg) Characters(0.2) EndElement({urn:example:double}d:arg) Characters( ) StartElement({urn:example:namespace}p:arg) Characters(0.1) EndElement({urn:example:namespace}p:arg) Characters( ) StartElement({urn:example:namespace}p:arg) Characters(0.01) EndElement({urn:example:namespace}p:arg) Characters( ) StartElement({urn:example:header}h:header [name="Header-1"]) Characters(header 1 value) EndElement({urn:example:header}h:header) Characters( ) StartElement({urn:example:header}h:header [name="Header-2"]) Characters( Some bigger value ) EndElement({urn:example:header}h:header) Characters( ) EndElement({urn:example:namespace}p:datum) Characters( ) EndElement({urn:example:namespace}p:data) EndDocument quick-xml-0.17.2/tests/documents/sample_2_short.txt010064400017500001750000000020701332061054700205610ustar0000000000000000StartDocument(1.0, utf-8) StartElement({urn:example:namespace}p:data) StartElement({urn:example:namespace}p:datum [id="34"]) StartElement({urn:example:namespace}p:name) Characters(Name) EndElement({urn:example:namespace}p:name) StartElement({urn:example:double}d:name) Characters(Another name) EndElement({urn:example:double}d:name) StartElement({urn:example:double}d:arg) Characters(0.3) EndElement({urn:example:double}d:arg) StartElement({urn:example:double}d:arg) Characters(0.2) EndElement({urn:example:double}d:arg) StartElement({urn:example:namespace}p:arg) Characters(0.1) EndElement({urn:example:namespace}p:arg) StartElement({urn:example:namespace}p:arg) Characters(0.01) EndElement({urn:example:namespace}p:arg) StartElement({urn:example:header}h:header [name="Header-1"]) Characters(header 1 value) EndElement({urn:example:header}h:header) StartElement({urn:example:header}h:header [name="Header-2"]) Characters(Some bigger value) EndElement({urn:example:header}h:header) EndElement({urn:example:namespace}p:datum) EndElement({urn:example:namespace}p:data) EndDocument quick-xml-0.17.2/tests/documents/sample_3.xml010064400017500001750000000004271332055316700173340ustar0000000000000000 test kkss" = ddd' > ddddd!e3--> test kkss" = ddd' > ddddd!e3--> This is a simple test document to demonstrate the DocumentLoader example! This is a simple test document to demonstrate the DocumentLoader example! This is a simple test document to demonstrate the DocumentLoader example! This is a simple test document to demonstrate the DocumentLoader example! This is a simple test document to demonstrate the DocumentLoader example! quick-xml-0.17.2/tests/serde-migrated.rs010064400017500001750000000626641357406636400163740ustar0000000000000000#![cfg(feature = "serialize")] extern crate quick_xml; extern crate serde; use std::fmt::Debug; use quick_xml::de::from_str; use serde::{de, ser}; use serde::{Deserialize, Serialize}; #[derive(PartialEq, Debug, Serialize, Deserialize)] enum Animal { Dog, Frog(String), Ant(Simple), Cat { age: usize, name: String }, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct Simple { a: (), b: usize, c: String, d: Option, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct Inner { a: (), b: (usize, String, i8), c: Vec, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct Outer { inner: Option, } fn test_parse_ok<'a, T: std::fmt::Debug>(errors: &[(&'a str, T)]) where T: PartialEq + Debug + ser::Serialize + for<'de> de::Deserialize<'de>, { for (i, &(s, ref value)) in errors.iter().enumerate() { match from_str::(s) { Ok(v) => assert_eq!( v, *value, "{} error, expected: {:?}, found: {:?}", i, value, v ), Err(e) => panic!("{} error, expected {:?}, found error {}", i, value, e), } // // Make sure we can deserialize into an `Element`. // let xml_value: Element = from_str(s).unwrap(); // // Make sure we can deserialize from an `Element`. // let v: T = from_value(xml_value.clone()).unwrap(); // assert_eq!(v, *value); } } fn test_parse_err<'a, T>(errors: &[&'a str]) where T: PartialEq + Debug + ser::Serialize + for<'de> de::Deserialize<'de>, { for &s in errors { assert!(from_str::(s).is_err()); } } fn test_parse_invalid<'a, T>(errors: &[&'a str]) where T: PartialEq + Debug + ser::Serialize + for<'de> de::Deserialize<'de>, { for &s in errors { assert!(from_str::(s).is_err()); } } #[test] fn test_namespaces() { #[derive(PartialEq, Serialize, Deserialize, Debug)] struct Envelope { subject: String, } let s = r#" Reference rates "#; test_parse_ok(&[( s, Envelope { subject: "Reference rates".to_string(), }, )]); } #[test] #[ignore] // FIXME fn test_doctype() { #[derive(PartialEq, Serialize, Deserialize, Debug)] struct Envelope { subject: String, } test_parse_ok(&[ ( r#" Reference rates "#, Envelope { subject: "Reference rates".to_string(), }, ), ( r#" Reference rates "#, Envelope { subject: "Reference rates".to_string(), }, ), ( r#" ] > Reference rates "#, Envelope { subject: "Reference rates".to_string(), }, ), ]); } // pass in quick-xml because there is no proper doctype support // // // #[test] // fn test_doctype_fail() { // #[derive(PartialEq, Serialize, Deserialize, Debug)] // struct Envelope { // subject: String, // } // // test_parse_err::(&[ // r#" // // // > // // Reference rates // "#, // r#" // // // Reference rates // // ]> // "#, // ]) // } #[test] #[ignore] // FIXME fn test_forwarded_namespace() { #[derive(PartialEq, Serialize, Deserialize, Debug)] struct Graphml { #[serde(rename = "xsi:schemaLocation")] schema_location: String, } let s = r#" "#; test_parse_ok(&[( s, Graphml { schema_location: "http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd" .to_string(), }, )]); } #[test] fn test_parse_string() { test_parse_ok(&[ ( "This is a String", "This is a String".to_string(), ), ("", "".to_string()), (" ", "".to_string()), ("<boom/>", "".to_string()), ("", "♫".to_string()), ("", "♫".to_string()), //( // "♫]]>♫", // "♫♫".to_string(), //), ]); } #[test] #[ignore] // FIXME fn test_parse_string_not_trim() { test_parse_ok(&[(" ", " ".to_string())]); } #[test] #[ignore] // FIXME fn test_parse_enum() { use self::Animal::*; test_parse_ok(&[ ("", Dog), ( "Quak", Frog("Quak".to_string()), ), ( "bla15Foo", Ant(Simple { a: (), b: 15, c: "bla".to_string(), d: Some("Foo".to_string()), }), ), ( "bla15", Ant(Simple { a: (), b: 15, c: "bla".to_string(), d: None, }), ), ( "42Shere Khan", Cat { age: 42, name: "Shere Khan".to_string(), }, ), ]); #[derive(PartialEq, Debug, Serialize, Deserialize)] struct Helper { x: Animal, } test_parse_ok(&[ ("", Helper { x: Dog }), ( "Quak", Helper { x: Frog("Quak".to_string()), }, ), ( " 42 Shere Khan ", Helper { x: Cat { age: 42, name: "Shere Khan".to_string(), }, }, ), ]); } #[test] fn test_parse_i64() { test_parse_ok(&[ ("0", 0), ("-2", -2), ("-1234", -1234), (" -1234 ", -1234), ]); } #[test] fn test_parse_u64() { test_parse_ok(&[ ("0", 0), ("1234", 1234), (" 1234 ", 1234), ]); } #[test] fn test_parse_bool() { test_parse_ok(&[ ("true", true), ("false", false), (" true ", true), (" false ", false), ("1", true), ("0", false), ]); test_parse_invalid::(&["verum"]); } #[test] fn test_parse_unit() { test_parse_ok(&[("", ())]); } #[test] fn test_parse_f64() { test_parse_ok(&[ ("3.0", 3.0f64), ("3.1", 3.1), ("-1.2", -1.2), ("0.4", 0.4), ("0.4e5", 0.4e5), ("0.4e15", 0.4e15), ("0.4e-01", 0.4e-01), // precision troubles (" 0.4e-01 ", 0.4e-01), ]); } #[test] fn test_parse_struct() { test_parse_ok(&[ ( " abc 2 ", Simple { a: (), b: 2, c: "abc".to_string(), d: None, }, ), ( " abc 2 ", Simple { a: (), b: 2, c: "abc".to_string(), d: None, }, ), ( " abc 2 ", Simple { a: (), b: 2, c: "abc".to_string(), d: Some("Foo".to_string()), }, ), ]); } #[test] fn test_option() { test_parse_ok(&[ ("", Some("".to_string())), ("", Some("".to_string())), (" ", Some("".to_string())), ("42", Some("42".to_string())), ]); } #[test] #[ignore] // FIXME fn test_option_not_trim() { test_parse_ok(&[(" ", Some(" ".to_string()))]); } #[test] fn test_amoskvin() { #[derive(Debug, Deserialize, PartialEq, Serialize)] struct Root { foo: Vec, } #[derive(Debug, Deserialize, PartialEq, Serialize)] struct Foo { a: String, b: Option, } test_parse_ok(&[( " Hello World Hi ", Root { foo: vec![ Foo { a: "Hello".to_string(), b: Some("World".to_string()), }, Foo { a: "Hi".to_string(), b: None, }, ], }, )]); } #[test] #[ignore] // FIXME fn test_nicolai86() { #[derive(Serialize, Deserialize, PartialEq, Debug)] struct TheSender { name: String, } #[derive(Serialize, Deserialize, PartialEq, Debug)] struct CurrencyCube { currency: String, rate: String, } #[derive(Serialize, Deserialize, PartialEq, Debug)] #[allow(non_snake_case)] struct InnerCube { Cube: Vec, } #[derive(Serialize, Deserialize, PartialEq, Debug)] #[allow(non_snake_case)] struct OuterCube { Cube: Vec, } #[derive(Serialize, Deserialize, PartialEq, Debug)] #[allow(non_snake_case)] struct Envelope { subject: String, Sender: TheSender, Cube: OuterCube, } test_parse_ok(&[ ( r#" Reference rates European Central Bank "#, Envelope { subject: "Reference rates".to_string(), Sender: TheSender { name: "European Central Bank".to_string(), }, Cube: OuterCube { Cube: vec![], } }, ), ( r#" Reference rates European Central Bank "#, Envelope { subject: "Reference rates".to_string(), Sender: TheSender { name: "European Central Bank".to_string(), }, Cube: OuterCube { Cube: vec![InnerCube { Cube: vec![ CurrencyCube { currency: "GBP".to_string(), rate: "0.81725".to_string(), }, CurrencyCube { currency: "Latinum".to_string(), rate: "999999".to_string(), }, ], }], } }, ), ]); } #[test] fn test_hugo_duncan2() { let s = r#" 8d521e9a-509e-4ef6-bbb7-9f1ac0d49cd1 vpc-ba0d18d8 available "#; #[derive(PartialEq, Debug, Serialize, Deserialize)] #[allow(non_snake_case)] struct VpcSet { vpcId: String, state: String, } #[derive(PartialEq, Debug, Serialize)] struct ItemVec(Vec); impl<'de, T: de::Deserialize<'de>> de::Deserialize<'de> for ItemVec { fn deserialize(deserializer: D) -> Result, D::Error> where D: de::Deserializer<'de>, { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct Helper { item: Vec, } let h: Helper<_> = de::Deserialize::deserialize(deserializer)?; Ok(ItemVec(h.item)) } } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[allow(non_snake_case)] struct DescribeVpcsResponse { requestId: String, vpcSet: ItemVec, } test_parse_ok(&[( s, DescribeVpcsResponse { requestId: "8d521e9a-509e-4ef6-bbb7-9f1ac0d49cd1".to_string(), vpcSet: ItemVec(vec![VpcSet { vpcId: "vpc-ba0d18d8".to_string(), state: "available".to_string(), }]), }, )]); } #[test] fn test_hugo_duncan() { let s = " 9474f558-10a5-42e8-84d1-f9ee181fe943 "; #[derive(PartialEq, Debug, Serialize, Deserialize)] #[allow(non_snake_case)] struct DescribeInstancesResponse { requestId: String, reservationSet: (), } test_parse_ok(&[( s, DescribeInstancesResponse { requestId: "9474f558-10a5-42e8-84d1-f9ee181fe943".to_string(), reservationSet: (), }, )]); } #[test] fn test_parse_xml_value() { #[derive(Eq, Debug, PartialEq, Deserialize, Serialize)] struct Test { #[serde(rename = "$value")] myval: String, } test_parse_ok(&[( "abc", Test { myval: "abc".to_string(), }, )]); } #[test] #[ignore] // FIXME fn test_parse_complexstruct() { test_parse_ok(&[ ( " 2 boom 88 ", Outer { inner: Some(Inner { a: (), b: (2, "boom".to_string(), 88), c: vec![], }), }, ), ( " abc xyz 2 boom 88 ", Outer { inner: Some(Inner { a: (), b: (2, "boom".to_string(), 88), c: vec!["abc".to_string(), "xyz".to_string()], }), }, ), ("", Outer { inner: None }), ]); } #[test] fn test_parse_attributes() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { a1: String, #[serde(rename = "$value")] a2: i32, } test_parse_ok(&[( r#"42"#, A { a1: "What is the answer to the ultimate question?".to_string(), a2: 42, }, )]); #[derive(PartialEq, Debug, Serialize, Deserialize)] struct B { b1: String, b2: i32, } test_parse_ok(&[( r#""#, B { b1: "What is the answer to the ultimate question?".to_string(), b2: 42, }, )]); #[derive(PartialEq, Debug, Serialize, Deserialize)] struct C { c1: B, } test_parse_ok(&[ ( r#""#, C { c1: B { b1: "What is the answer to the ultimate question?".to_string(), b2: 42, }, }, ), ( r#" "#, C { c1: B { b1: "What is the answer to the ultimate question?".to_string(), b2: 42, }, }, ), ( r#" "#, C { c1: B { b1: "What is the answer to the ultimate question?".to_string(), b2: 42, }, }, ), ]); #[derive(PartialEq, Debug, Serialize, Deserialize)] struct D { d1: Option, } test_parse_ok(&[( r#"42"#, D { d1: Some(A { a1: "What is the answer to the ultimate question?".to_string(), a2: 42, }), }, )]); } #[test] #[ignore] // FIXME fn test_parse_hierarchies() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { a1: String, a2: (String, String), } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct B { b1: A, b2: (A, A), } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct C { c1: B, c2: Vec, } test_parse_ok(&[ ( " No Maybe Yes Red Green Blue London Berlin Paris ", C { c1: B { b1: A { a1: "No".to_string(), a2: ("Maybe".to_string(), "Yes".to_string()), }, b2: ( A { a1: "Red".to_string(), a2: ("Green".to_string(), "Blue".to_string()), }, A { a1: "London".to_string(), a2: ("Berlin".to_string(), "Paris".to_string()), }, ), }, c2: vec![], }, ), ( " Green Blue Red Berlin Paris London Maybe Yes No ", C { c1: B { b1: A { a1: "No".to_string(), a2: ("Maybe".to_string(), "Yes".to_string()), }, b2: ( A { a1: "Red".to_string(), a2: ("Green".to_string(), "Blue".to_string()), }, A { a1: "London".to_string(), a2: ("Berlin".to_string(), "Paris".to_string()), }, ), }, c2: vec![], }, ), ]); } #[test] fn unknown_field() { #[derive(Deserialize, Debug, PartialEq, Eq, Serialize)] struct A { other: Vec, } #[derive(Deserialize, Debug, PartialEq, Eq, Serialize)] struct Other { d: i32, } test_parse_ok(&[( " 5 6 ", A { other: vec![Other { d: 6 }], }, )]); } // #[test] // fn eoz() { // use std::io::Read; // let mut file = std::fs::File::open("Report_test.2.xml").unwrap(); // let mut s = String::new(); // file.read_to_string(&mut s).unwrap(); // let _xml_value: Element = from_str(&s).unwrap(); // } #[test] fn test_parse_unfinished() { test_parse_err::(&[" abc 2 "]); } #[test] fn test_things_qc_found() { test_parse_err::(&["<\u{0}:/"]); } #[test] fn futile() { #[derive(Eq, PartialEq, Debug, Serialize, Deserialize)] struct Object { id: u8, name: String, x: u8, y: u8, width: u8, height: u8, ellipse: Option<()>, } test_parse_ok(&[ ( r###" "###, Object { id: 11, name: "testEllipse".to_owned(), x: 102, y: 38, width: 21, height: 14, ellipse: Some(()), }, ), ( r###" "###, Object { id: 11, name: "testEllipse".to_owned(), x: 102, y: 38, width: 21, height: 14, ellipse: None, }, ), ]); } #[test] fn futile2() { #[derive(Eq, PartialEq, Debug, Serialize, Deserialize)] struct Null; #[derive(Eq, PartialEq, Debug, Serialize, Deserialize)] struct Object { field: Option, }; #[derive(Eq, PartialEq, Debug, Serialize, Deserialize)] struct Stuff { stuff_field: Option, }; test_parse_ok(&[ ( r###" "###, Object { field: Some(Null) }, ), ( r###" "###, Object { field: None }, ), ]); test_parse_ok(&[ ( r###" "###, Stuff { stuff_field: Some(Object { field: None }), }, ), ( r###" "###, Stuff { stuff_field: Some(Object { field: Some(Null) }), }, ), ( r###" "###, Stuff { stuff_field: None }, ), ( r###" "###, Stuff { stuff_field: None }, ), ]); } quick-xml-0.17.2/tests/serde_roundtrip.rs010064400017500001750000000054741357610734000166740ustar0000000000000000#![cfg(feature = "serialize")] extern crate quick_xml; extern crate serde; use quick_xml::{de::from_str, se::to_string}; use serde::{Deserialize, Serialize}; #[derive(Debug, Serialize, Deserialize, PartialEq)] struct Item { name: String, source: String, } #[derive(Debug, Serialize, Deserialize, PartialEq)] enum Node { Boolean(bool), Identifier { value: String, index: u32 }, EOF, } #[derive(Debug, Serialize, Deserialize, PartialEq)] struct Nodes { #[serde(rename = "$value")] items: Vec, } #[test] fn basic_struct() { let src = r#"BananaStore"#; let should_be = Item { name: "Banana".to_string(), source: "Store".to_string(), }; let item: Item = from_str(src).unwrap(); assert_eq!(item, should_be); let reserialized_item = to_string(&item).unwrap(); assert_eq!(src, reserialized_item); } #[test] #[ignore] fn round_trip_list_of_enums() { // Construct some inputs let nodes = Nodes { items: vec![ Node::Boolean(true), Node::Identifier { value: "foo".to_string(), index: 5, }, Node::EOF, ], }; let should_be = r#" true foo 5 "#; let serialized_nodes = to_string(&nodes).unwrap(); assert_eq!(serialized_nodes, should_be); // Then turn it back into a `Nodes` struct and make sure it's the same // as the original let deserialized_nodes: Nodes = from_str(serialized_nodes.as_str()).unwrap(); assert_eq!(deserialized_nodes, nodes); } #[test] fn no_contiguous_fields() { #[derive(Serialize, Deserialize, PartialEq, Debug)] struct Xml { #[serde(rename = "$value")] fields: Vec, } #[derive(Serialize, Deserialize, PartialEq, Debug)] enum Field { #[serde(rename = "field1")] Field1 { name: String }, #[serde(rename = "field2")] Field2 { name: String }, } let source = r#" "#; let xml: Xml = ::quick_xml::de::from_str(source).unwrap(); assert_eq!( xml, Xml { fields: vec![ Field::Field1 { name: "a".to_string() }, Field::Field2 { name: "b".to_string() }, Field::Field1 { name: "a".to_string() }, ], } ); // TODO: impl Serialize for struct variants // let serialized = to_string(&xml).unwrap(); // assert_eq!(serialized, source); } quick-xml-0.17.2/tests/test.rs010064400017500001750000001216731357061306100144370ustar0000000000000000extern crate quick_xml; #[cfg(feature = "serialize")] extern crate serde; use quick_xml::events::attributes::Attribute; use quick_xml::events::Event::*; use quick_xml::Reader; use std::borrow::Cow; use std::io::Cursor; #[cfg(feature = "serialize")] use serde::{Deserialize, Serialize}; #[test] fn test_sample() { let src: &[u8] = include_bytes!("sample_rss.xml"); let mut buf = Vec::new(); let mut r = Reader::from_reader(src); let mut count = 0; loop { match r.read_event(&mut buf).unwrap() { Start(_) => count += 1, Decl(e) => println!("{:?}", e.version()), Eof => break, _ => (), } buf.clear(); } println!("{}", count); } #[test] fn test_attributes_empty() { let src = b""; let mut r = Reader::from_reader(src as &[u8]); r.trim_text(true).expand_empty_elements(false); let mut buf = Vec::new(); match r.read_event(&mut buf) { Ok(Empty(e)) => { let mut atts = e.attributes(); match atts.next() { Some(Ok(Attribute { key: b"att1", value: Cow::Borrowed(b"a"), })) => (), e => panic!("Expecting att1='a' attribute, found {:?}", e), } match atts.next() { Some(Ok(Attribute { key: b"att2", value: Cow::Borrowed(b"b"), })) => (), e => panic!("Expecting att2='b' attribute, found {:?}", e), } match atts.next() { None => (), e => panic!("Expecting None, found {:?}", e), } } e => panic!("Expecting Empty event, got {:?}", e), } } #[test] fn test_attribute_equal() { let src = b""; let mut r = Reader::from_reader(src as &[u8]); r.trim_text(true).expand_empty_elements(false); let mut buf = Vec::new(); match r.read_event(&mut buf) { Ok(Empty(e)) => { let mut atts = e.attributes(); match atts.next() { Some(Ok(Attribute { key: b"att1", value: Cow::Borrowed(b"a=b"), })) => (), e => panic!("Expecting att1=\"a=b\" attribute, found {:?}", e), } match atts.next() { None => (), e => panic!("Expecting None, found {:?}", e), } } e => panic!("Expecting Empty event, got {:?}", e), } } #[test] fn test_comment_starting_with_gt() { let src = b"-->"; let mut r = Reader::from_reader(src as &[u8]); r.trim_text(true).expand_empty_elements(false); let mut buf = Vec::new(); loop { match r.read_event(&mut buf) { Ok(Comment(ref e)) if &**e == b">" => break, Ok(Eof) => panic!("Expecting Comment"), _ => (), } } } /// Single empty element with qualified attributes. /// Empty element expansion: disabled /// The code path for namespace handling is slightly different for `Empty` vs. `Start+End`. #[test] fn test_attributes_empty_ns() { let src = b""; let mut r = Reader::from_reader(src as &[u8]); r.trim_text(true).expand_empty_elements(false); let mut buf = Vec::new(); let mut ns_buf = Vec::new(); let e = match r.read_namespaced_event(&mut buf, &mut ns_buf) { Ok((None, Empty(e))) => e, e => panic!("Expecting Empty event, got {:?}", e), }; let mut atts = e .attributes() .map(|ar| ar.expect("Expecting attribute parsing to succeed.")) // we don't care about xmlns attributes for this test .filter(|kv| !kv.key.starts_with(b"xmlns")) .map(|Attribute { key: name, value }| { let (opt_ns, local_name) = r.attribute_namespace(name, &ns_buf); (opt_ns, local_name, value) }); match atts.next() { Some((None, b"att1", Cow::Borrowed(b"a"))) => (), e => panic!("Expecting att1='a' attribute, found {:?}", e), } match atts.next() { Some((Some(ns), b"att2", Cow::Borrowed(b"b"))) => { assert_eq!(&ns[..], b"urn:example:r"); } e => panic!( "Expecting {{urn:example:r}}att2='b' attribute, found {:?}", e ), } match atts.next() { None => (), e => panic!("Expecting None, found {:?}", e), } } /// Single empty element with qualified attributes. /// Empty element expansion: enabled /// The code path for namespace handling is slightly different for `Empty` vs. `Start+End`. #[test] fn test_attributes_empty_ns_expanded() { let src = b""; let mut r = Reader::from_reader(src as &[u8]); r.trim_text(true).expand_empty_elements(true); let mut buf = Vec::new(); let mut ns_buf = Vec::new(); { let e = match r.read_namespaced_event(&mut buf, &mut ns_buf) { Ok((None, Start(e))) => e, e => panic!("Expecting Empty event, got {:?}", e), }; let mut atts = e .attributes() .map(|ar| ar.expect("Expecting attribute parsing to succeed.")) // we don't care about xmlns attributes for this test .filter(|kv| !kv.key.starts_with(b"xmlns")) .map(|Attribute { key: name, value }| { let (opt_ns, local_name) = r.attribute_namespace(name, &ns_buf); (opt_ns, local_name, value) }); match atts.next() { Some((None, b"att1", Cow::Borrowed(b"a"))) => (), e => panic!("Expecting att1='a' attribute, found {:?}", e), } match atts.next() { Some((Some(ns), b"att2", Cow::Borrowed(b"b"))) => { assert_eq!(&ns[..], b"urn:example:r"); } e => panic!( "Expecting {{urn:example:r}}att2='b' attribute, found {:?}", e ), } match atts.next() { None => (), e => panic!("Expecting None, found {:?}", e), } } match r.read_namespaced_event(&mut buf, &mut ns_buf) { Ok((None, End(e))) => assert_eq!(b"a", e.name()), e => panic!("Expecting End event, got {:?}", e), } } #[test] fn test_default_ns_shadowing_empty() { let src = b""; let mut r = Reader::from_reader(src as &[u8]); r.trim_text(true).expand_empty_elements(false); let mut buf = Vec::new(); let mut ns_buf = Vec::new(); // { match r.read_namespaced_event(&mut buf, &mut ns_buf) { Ok((Some(ns), Start(e))) => { assert_eq!(&ns[..], b"urn:example:o"); assert_eq!(e.name(), b"e"); } e => panic!("Expected Start event (), got {:?}", e), } } // { let e = match r.read_namespaced_event(&mut buf, &mut ns_buf) { Ok((Some(ns), Empty(e))) => { assert_eq!(::std::str::from_utf8(ns).unwrap(), "urn:example:i"); assert_eq!(e.name(), b"e"); e } e => panic!("Expecting Empty event, got {:?}", e), }; let mut atts = e .attributes() .map(|ar| ar.expect("Expecting attribute parsing to succeed.")) // we don't care about xmlns attributes for this test .filter(|kv| !kv.key.starts_with(b"xmlns")) .map(|Attribute { key: name, value }| { let (opt_ns, local_name) = r.attribute_namespace(name, &ns_buf); (opt_ns, local_name, value) }); // the attribute should _not_ have a namespace name. The default namespace does not // apply to attributes. match atts.next() { Some((None, b"att1", Cow::Borrowed(b"a"))) => (), e => panic!("Expecting att1='a' attribute, found {:?}", e), } match atts.next() { None => (), e => panic!("Expecting None, found {:?}", e), } } // match r.read_namespaced_event(&mut buf, &mut ns_buf) { Ok((Some(ns), End(e))) => { assert_eq!(&ns[..], b"urn:example:o"); assert_eq!(e.name(), b"e"); } e => panic!("Expected End event (), got {:?}", e), } } #[test] fn test_default_ns_shadowing_expanded() { let src = b""; let mut r = Reader::from_reader(src as &[u8]); r.trim_text(true).expand_empty_elements(true); let mut buf = Vec::new(); let mut ns_buf = Vec::new(); // { match r.read_namespaced_event(&mut buf, &mut ns_buf) { Ok((Some(ns), Start(e))) => { assert_eq!(&ns[..], b"urn:example:o"); assert_eq!(e.name(), b"e"); } e => panic!("Expected Start event (), got {:?}", e), } } buf.clear(); // { let e = match r.read_namespaced_event(&mut buf, &mut ns_buf) { Ok((Some(ns), Start(e))) => { assert_eq!(&ns[..], b"urn:example:i"); assert_eq!(e.name(), b"e"); e } e => panic!("Expecting Start event (), got {:?}", e), }; let mut atts = e .attributes() .map(|ar| ar.expect("Expecting attribute parsing to succeed.")) // we don't care about xmlns attributes for this test .filter(|kv| !kv.key.starts_with(b"xmlns")) .map(|Attribute { key: name, value }| { let (opt_ns, local_name) = r.attribute_namespace(name, &ns_buf); (opt_ns, local_name, value) }); // the attribute should _not_ have a namespace name. The default namespace does not // apply to attributes. match atts.next() { Some((None, b"att1", Cow::Borrowed(b"a"))) => (), e => panic!("Expecting att1='a' attribute, found {:?}", e), } match atts.next() { None => (), e => panic!("Expecting None, found {:?}", e), } } // virtual match r.read_namespaced_event(&mut buf, &mut ns_buf) { Ok((Some(ns), End(e))) => { assert_eq!(&ns[..], b"urn:example:i"); assert_eq!(e.name(), b"e"); } e => panic!("Expected End event (), got {:?}", e), } // match r.read_namespaced_event(&mut buf, &mut ns_buf) { Ok((Some(ns), End(e))) => { assert_eq!(&ns[..], b"urn:example:o"); assert_eq!(e.name(), b"e"); } e => panic!("Expected End event (), got {:?}", e), } } #[test] #[cfg(feature = "encoding_rs")] fn test_koi8_r_encoding() { let src: &[u8] = include_bytes!("documents/opennews_all.rss"); let mut r = Reader::from_reader(src as &[u8]); r.trim_text(true).expand_empty_elements(false); let mut buf = Vec::new(); loop { match r.read_event(&mut buf) { Ok(Text(e)) => { e.unescape_and_decode(&r).unwrap(); } Ok(Eof) => break, _ => (), } } } #[test] fn fuzz_53() { let data: &[u8] = b"\xe9\x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x00\x00\n(\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\ \x00<>\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00<<\x00\x00\x00"; let cursor = Cursor::new(data); let mut reader = Reader::from_reader(cursor); let mut buf = vec![]; loop { match reader.read_event(&mut buf) { Ok(quick_xml::events::Event::Eof) | Err(..) => break, _ => buf.clear(), } } } #[test] fn test_issue94() { let data = br#" "#; let mut reader = Reader::from_reader(&data[..]); reader.trim_text(true); let mut buf = vec![]; loop { match reader.read_event(&mut buf) { Ok(quick_xml::events::Event::Eof) | Err(..) => break, _ => buf.clear(), } buf.clear(); } } #[test] fn fuzz_101() { let data: &[u8] = b"\x00\x00<\x00\x00\x0a>�?#\x0a413518\ #\x0a\x0a\x0a;<:<)(<:\x0a\x0a\x0a\x0a;<:\x0a\x0a\ <:\x0a\x0a\x0a\x0a\x0a<\x00*\x00\x00\x00\x00"; let cursor = Cursor::new(data); let mut reader = Reader::from_reader(cursor); let mut buf = vec![]; loop { match reader.read_event(&mut buf) { Ok(Start(ref e)) | Ok(Empty(ref e)) => { if e.unescaped().is_err() { break; } for a in e.attributes() { if a.ok().map_or(true, |a| a.unescaped_value().is_err()) { break; } } } Ok(Text(ref e)) => { if e.unescaped().is_err() { break; } } Ok(Eof) | Err(..) => break, _ => (), } buf.clear(); } } #[test] fn test_default_namespace() { let mut r = Reader::from_str(""); r.trim_text(true); // let mut buf = Vec::new(); let mut ns_buf = Vec::new(); if let Ok((None, Start(_))) = r.read_namespaced_event(&mut buf, &mut ns_buf) { } else { panic!("expecting outer start element with no namespace"); } // { let event = match r.read_namespaced_event(&mut buf, &mut ns_buf) { Ok((Some(b"www1"), Start(event))) => event, Ok((Some(_), Start(_))) => panic!("expecting namespace to resolve to 'www1'"), _ => panic!("expecting namespace resolution"), }; //We check if the resolve_namespace method also work properly match r.event_namespace(event.name(), &mut ns_buf) { (Some(b"www1"), _) => (), (Some(_), _) => panic!("expecting namespace to resolve to 'www1'"), ns => panic!( "expecting namespace resolution by the resolve_nemespace method {:?}", ns ), } } // match r.read_namespaced_event(&mut buf, &mut ns_buf) { Ok((Some(b"www1"), End(_))) => (), Ok((Some(_), End(_))) => panic!("expecting namespace to resolve to 'www1'"), _ => panic!("expecting namespace resolution"), } // very important: a should not be in any namespace. The default namespace only applies to // the sub-document it is defined on. if let Ok((None, End(_))) = r.read_namespaced_event(&mut buf, &mut ns_buf) { } else { panic!("expecting outer end element with no namespace"); } } #[cfg(feature = "serialize")] #[test] fn line_score() { #[derive(Debug, PartialEq, Deserialize)] struct LineScoreData { game_pk: u32, game_type: char, venue: String, venue_w_chan_loc: String, venue_id: u32, time: String, time_zone: String, ampm: String, home_team_id: u32, home_team_city: String, home_team_name: String, home_league_id: u32, away_team_id: u32, away_team_city: String, away_team_name: String, away_league_id: u32, #[serde(rename = "linescore", skip_serializing)] innings: Vec, } #[derive(Debug, PartialEq, Deserialize)] struct LineScore { #[serde(rename = "away_inning_runs")] away_runs: u32, #[serde(rename = "home_inning_runs")] //needs to be an Option, since home team doesn't always bat. home_runs: Option, // Keeping the inning as a string, since we'll need it to construct URLs later inning: String, } let res: LineScoreData = quick_xml::de::from_str(include_str!("linescore.xml")).unwrap(); let expected = LineScoreData { game_pk: 239575, game_type: 'R', venue: "Generic".to_owned(), venue_w_chan_loc: "USNY0996".to_owned(), venue_id: 401, time: "Gm 2".to_owned(), time_zone: "ET".to_owned(), ampm: "AM".to_owned(), home_team_id: 611, home_team_city: "DSL Dodgers".to_owned(), home_team_name: "DSL Dodgers".to_owned(), home_league_id: 130, away_team_id: 604, away_team_city: "DSL Blue Jays1".to_owned(), away_team_name: "DSL Blue Jays1".to_owned(), away_league_id: 130, innings: vec![ LineScore { away_runs: 1, home_runs: Some(0), inning: "1".to_owned(), }, LineScore { away_runs: 0, home_runs: Some(0), inning: "2".to_owned(), }, LineScore { away_runs: 1, home_runs: Some(1), inning: "3".to_owned(), }, LineScore { away_runs: 2, home_runs: Some(0), inning: "4".to_owned(), }, LineScore { away_runs: 0, home_runs: Some(0), inning: "5".to_owned(), }, LineScore { away_runs: 0, home_runs: Some(0), inning: "6".to_owned(), }, LineScore { away_runs: 0, home_runs: Some(0), inning: "7".to_owned(), }, ], }; assert_eq!(res, expected); } #[cfg(feature = "serialize")] #[test] fn players() { #[derive(PartialEq, Deserialize, Serialize, Debug)] struct Game { #[serde(rename = "team")] teams: Vec, //umpires: Umpires } #[derive(PartialEq, Deserialize, Serialize, Debug)] struct Team { #[serde(rename = "type")] home_away: HomeAway, id: String, name: String, #[serde(rename = "player")] players: Vec, #[serde(rename = "coach")] coaches: Vec, } #[derive(PartialEq, Deserialize, Serialize, Debug)] enum HomeAway { #[serde(rename = "home")] Home, #[serde(rename = "away")] Away, } #[derive(PartialEq, Deserialize, Serialize, Debug, Clone)] struct Player { id: u32, #[serde(rename = "first")] name_first: String, #[serde(rename = "last")] name_last: String, game_position: Option, bat_order: Option, position: String, } #[derive(PartialEq, Deserialize, Serialize, Debug)] struct Coach { position: String, #[serde(rename = "first")] name_first: String, #[serde(rename = "last")] name_last: String, id: u32, } let res: Game = quick_xml::de::from_str(include_str!("players.xml")).unwrap(); let expected = Game { teams: vec![ Team { home_away: HomeAway::Away, id: "CIN".to_owned(), name: "Cincinnati Reds".to_owned(), players: vec![ Player { id: 115135, name_first: "Ken".to_owned(), name_last: "Griffey".to_owned(), game_position: Some("RF".to_owned()), bat_order: Some(3), position: "RF".to_owned(), }, Player { id: 115608, name_first: "Scott".to_owned(), name_last: "Hatteberg".to_owned(), game_position: None, bat_order: None, position: "1B".to_owned(), }, Player { id: 118967, name_first: "Kent".to_owned(), name_last: "Mercker".to_owned(), game_position: None, bat_order: None, position: "P".to_owned(), }, Player { id: 136460, name_first: "Alex".to_owned(), name_last: "Gonzalez".to_owned(), game_position: None, bat_order: None, position: "SS".to_owned(), }, Player { id: 150020, name_first: "Jerry".to_owned(), name_last: "Hairston".to_owned(), game_position: None, bat_order: None, position: "SS".to_owned(), }, Player { id: 150188, name_first: "Francisco".to_owned(), name_last: "Cordero".to_owned(), game_position: None, bat_order: None, position: "P".to_owned(), }, Player { id: 150221, name_first: "Mike".to_owned(), name_last: "Lincoln".to_owned(), game_position: None, bat_order: None, position: "P".to_owned(), }, Player { id: 150319, name_first: "Josh".to_owned(), name_last: "Fogg".to_owned(), game_position: None, bat_order: None, position: "P".to_owned(), }, Player { id: 150472, name_first: "Ryan".to_owned(), name_last: "Freel".to_owned(), game_position: Some("LF".to_owned()), bat_order: Some(2), position: "CF".to_owned(), }, Player { id: 276520, name_first: "Bronson".to_owned(), name_last: "Arroyo".to_owned(), game_position: None, bat_order: None, position: "P".to_owned(), }, Player { id: 279571, name_first: "Matt".to_owned(), name_last: "Belisle".to_owned(), game_position: Some("P".to_owned()), bat_order: Some(9), position: "P".to_owned(), }, Player { id: 279913, name_first: "Corey".to_owned(), name_last: "Patterson".to_owned(), game_position: Some("CF".to_owned()), bat_order: Some(1), position: "CF".to_owned(), }, Player { id: 346793, name_first: "Jeremy".to_owned(), name_last: "Affeldt".to_owned(), game_position: None, bat_order: None, position: "P".to_owned(), }, Player { id: 408252, name_first: "Brandon".to_owned(), name_last: "Phillips".to_owned(), game_position: Some("2B".to_owned()), bat_order: Some(4), position: "2B".to_owned(), }, Player { id: 421685, name_first: "Aaron".to_owned(), name_last: "Harang".to_owned(), game_position: None, bat_order: None, position: "P".to_owned(), }, Player { id: 424325, name_first: "David".to_owned(), name_last: "Ross".to_owned(), game_position: Some("C".to_owned()), bat_order: Some(8), position: "C".to_owned(), }, Player { id: 429665, name_first: "Edwin".to_owned(), name_last: "Encarnacion".to_owned(), game_position: Some("3B".to_owned()), bat_order: Some(6), position: "3B".to_owned(), }, Player { id: 433898, name_first: "Jeff".to_owned(), name_last: "Keppinger".to_owned(), game_position: Some("SS".to_owned()), bat_order: Some(7), position: "SS".to_owned(), }, Player { id: 435538, name_first: "Bill".to_owned(), name_last: "Bray".to_owned(), game_position: None, bat_order: None, position: "P".to_owned(), }, Player { id: 440361, name_first: "Norris".to_owned(), name_last: "Hopper".to_owned(), game_position: None, bat_order: None, position: "O".to_owned(), }, Player { id: 450172, name_first: "Edinson".to_owned(), name_last: "Volquez".to_owned(), game_position: None, bat_order: None, position: "P".to_owned(), }, Player { id: 454537, name_first: "Jared".to_owned(), name_last: "Burton".to_owned(), game_position: None, bat_order: None, position: "P".to_owned(), }, Player { id: 455751, name_first: "Bobby".to_owned(), name_last: "Livingston".to_owned(), game_position: None, bat_order: None, position: "P".to_owned(), }, Player { id: 456501, name_first: "Johnny".to_owned(), name_last: "Cueto".to_owned(), game_position: None, bat_order: None, position: "P".to_owned(), }, Player { id: 458015, name_first: "Joey".to_owned(), name_last: "Votto".to_owned(), game_position: Some("1B".to_owned()), bat_order: Some(5), position: "1B".to_owned(), }, ], coaches: vec![ Coach { position: "manager".to_owned(), name_first: "Dusty".to_owned(), name_last: "Baker".to_owned(), id: 110481, }, Coach { position: "batting_coach".to_owned(), name_first: "Brook".to_owned(), name_last: "Jacoby".to_owned(), id: 116461, }, Coach { position: "pitching_coach".to_owned(), name_first: "Dick".to_owned(), name_last: "Pole".to_owned(), id: 120649, }, Coach { position: "first_base_coach".to_owned(), name_first: "Billy".to_owned(), name_last: "Hatcher".to_owned(), id: 115602, }, Coach { position: "third_base_coach".to_owned(), name_first: "Mark".to_owned(), name_last: "Berry".to_owned(), id: 427028, }, Coach { position: "bench_coach".to_owned(), name_first: "Chris".to_owned(), name_last: "Speier".to_owned(), id: 122573, }, Coach { position: "bullpen_coach".to_owned(), name_first: "Juan".to_owned(), name_last: "Lopez".to_owned(), id: 427306, }, Coach { position: "bullpen_catcher".to_owned(), name_first: "Mike".to_owned(), name_last: "Stefanski".to_owned(), id: 150464, }, ], }, Team { home_away: HomeAway::Home, id: "NYM".to_owned(), name: "New York Mets".to_owned(), players: vec![ Player { id: 110189, name_first: "Moises".to_owned(), name_last: "Alou".to_owned(), game_position: Some("LF".to_owned()), bat_order: Some(6), position: "LF".to_owned(), }, Player { id: 112116, name_first: "Luis".to_owned(), name_last: "Castillo".to_owned(), game_position: Some("2B".to_owned()), bat_order: Some(2), position: "2B".to_owned(), }, Player { id: 113232, name_first: "Carlos".to_owned(), name_last: "Delgado".to_owned(), game_position: Some("1B".to_owned()), bat_order: Some(7), position: "1B".to_owned(), }, Player { id: 113702, name_first: "Damion".to_owned(), name_last: "Easley".to_owned(), game_position: None, bat_order: None, position: "2B".to_owned(), }, Player { id: 118377, name_first: "Pedro".to_owned(), name_last: "Martinez".to_owned(), game_position: None, bat_order: None, position: "P".to_owned(), }, Player { id: 123790, name_first: "Billy".to_owned(), name_last: "Wagner".to_owned(), game_position: None, bat_order: None, position: "P".to_owned(), }, Player { id: 133340, name_first: "Orlando".to_owned(), name_last: "Hernandez".to_owned(), game_position: None, bat_order: None, position: "P".to_owned(), }, Player { id: 135783, name_first: "Ramon".to_owned(), name_last: "Castro".to_owned(), game_position: None, bat_order: None, position: "C".to_owned(), }, Player { id: 136724, name_first: "Marlon".to_owned(), name_last: "Anderson".to_owned(), game_position: None, bat_order: None, position: "LF".to_owned(), }, Player { id: 136860, name_first: "Carlos".to_owned(), name_last: "Beltran".to_owned(), game_position: Some("CF".to_owned()), bat_order: Some(4), position: "CF".to_owned(), }, Player { id: 150411, name_first: "Brian".to_owned(), name_last: "Schneider".to_owned(), game_position: Some("C".to_owned()), bat_order: Some(8), position: "C".to_owned(), }, Player { id: 276371, name_first: "Johan".to_owned(), name_last: "Santana".to_owned(), game_position: Some("P".to_owned()), bat_order: Some(9), position: "P".to_owned(), }, Player { id: 277184, name_first: "Matt".to_owned(), name_last: "Wise".to_owned(), game_position: None, bat_order: None, position: "P".to_owned(), }, Player { id: 346795, name_first: "Endy".to_owned(), name_last: "Chavez".to_owned(), game_position: None, bat_order: None, position: "RF".to_owned(), }, Player { id: 407901, name_first: "Jorge".to_owned(), name_last: "Sosa".to_owned(), game_position: None, bat_order: None, position: "P".to_owned(), }, Player { id: 408230, name_first: "Pedro".to_owned(), name_last: "Feliciano".to_owned(), game_position: None, bat_order: None, position: "P".to_owned(), }, Player { id: 408310, name_first: "Aaron".to_owned(), name_last: "Heilman".to_owned(), game_position: None, bat_order: None, position: "P".to_owned(), }, Player { id: 408314, name_first: "Jose".to_owned(), name_last: "Reyes".to_owned(), game_position: Some("SS".to_owned()), bat_order: Some(1), position: "SS".to_owned(), }, Player { id: 425508, name_first: "Ryan".to_owned(), name_last: "Church".to_owned(), game_position: Some("RF".to_owned()), bat_order: Some(5), position: "RF".to_owned(), }, Player { id: 429720, name_first: "John".to_owned(), name_last: "Maine".to_owned(), game_position: None, bat_order: None, position: "P".to_owned(), }, Player { id: 431151, name_first: "David".to_owned(), name_last: "Wright".to_owned(), game_position: Some("3B".to_owned()), bat_order: Some(3), position: "3B".to_owned(), }, Player { id: 434586, name_first: "Ambiorix".to_owned(), name_last: "Burgos".to_owned(), game_position: None, bat_order: None, position: "P".to_owned(), }, Player { id: 434636, name_first: "Angel".to_owned(), name_last: "Pagan".to_owned(), game_position: None, bat_order: None, position: "LF".to_owned(), }, Player { id: 450306, name_first: "Jason".to_owned(), name_last: "Vargas".to_owned(), game_position: None, bat_order: None, position: "P".to_owned(), }, Player { id: 460059, name_first: "Mike".to_owned(), name_last: "Pelfrey".to_owned(), game_position: None, bat_order: None, position: "P".to_owned(), }, ], coaches: vec![ Coach { position: "manager".to_owned(), name_first: "Willie".to_owned(), name_last: "Randolph".to_owned(), id: 120927, }, Coach { position: "batting_coach".to_owned(), name_first: "Howard".to_owned(), name_last: "Johnson".to_owned(), id: 116593, }, Coach { position: "pitching_coach".to_owned(), name_first: "Rick".to_owned(), name_last: "Peterson".to_owned(), id: 427395, }, Coach { position: "first_base_coach".to_owned(), name_first: "Tom".to_owned(), name_last: "Nieto".to_owned(), id: 119796, }, Coach { position: "third_base_coach".to_owned(), name_first: "Sandy".to_owned(), name_last: "Alomar".to_owned(), id: 110185, }, Coach { position: "bench_coach".to_owned(), name_first: "Jerry".to_owned(), name_last: "Manuel".to_owned(), id: 118262, }, Coach { position: "bullpen_coach".to_owned(), name_first: "Guy".to_owned(), name_last: "Conti".to_owned(), id: 434699, }, Coach { position: "bullpen_catcher".to_owned(), name_first: "Dave".to_owned(), name_last: "Racaniello".to_owned(), id: 534948, }, Coach { position: "coach".to_owned(), name_first: "Sandy".to_owned(), name_last: "Alomar".to_owned(), id: 110184, }, Coach { position: "coach".to_owned(), name_first: "Juan".to_owned(), name_last: "Lopez".to_owned(), id: 495390, }, ], }, ], }; assert_eq!(res, expected); } quick-xml-0.17.2/tests/unit_tests.rs010064400017500001750000000577041357061306100156640ustar0000000000000000extern crate quick_xml; use std::io::Cursor; use std::str::from_utf8; use quick_xml::events::Event::*; use quick_xml::events::{BytesDecl, BytesEnd, BytesStart, BytesText, Event}; use quick_xml::{Reader, Result, Writer}; macro_rules! next_eq_name { ($r:expr, $t:tt, $bytes:expr) => { let mut buf = Vec::new(); match $r.read_event(&mut buf).unwrap() { $t(ref e) if e.name() == $bytes => (), e => panic!( "expecting {}({:?}), found {:?}", stringify!($t), from_utf8($bytes), e ), } buf.clear(); }; } macro_rules! next_eq_content { ($r:expr, $t:tt, $bytes:expr) => { let mut buf = Vec::new(); match $r.read_event(&mut buf).unwrap() { $t(ref e) if &**e == $bytes => (), e => panic!( "expecting {}({:?}), found {:?}", stringify!($t), from_utf8($bytes), e ), } buf.clear(); }; } macro_rules! next_eq { ($r:expr, Start, $bytes:expr) => (next_eq_name!($r, Start, $bytes);); ($r:expr, End, $bytes:expr) => (next_eq_name!($r, End, $bytes);); ($r:expr, Empty, $bytes:expr) => (next_eq_name!($r, Empty, $bytes);); ($r:expr, Comment, $bytes:expr) => (next_eq_content!($r, Comment, $bytes);); ($r:expr, Text, $bytes:expr) => (next_eq_content!($r, Text, $bytes);); ($r:expr, CData, $bytes:expr) => (next_eq_content!($r, CData, $bytes);); ($r:expr, $t0:tt, $b0:expr, $($t:tt, $bytes:expr),*) => { next_eq!($r, $t0, $b0); next_eq!($r, $($t, $bytes),*); }; } #[test] fn test_start() { let mut r = Reader::from_str(""); r.trim_text(true); next_eq!(r, Start, b"a"); } #[test] fn test_start_end() { let mut r = Reader::from_str(""); r.trim_text(true); next_eq!(r, Start, b"a", End, b"a"); } #[test] fn test_start_end_with_ws() { let mut r = Reader::from_str(""); r.trim_text(true); next_eq!(r, Start, b"a", End, b"a"); } #[test] fn test_start_end_attr() { let mut r = Reader::from_str(""); r.trim_text(true); next_eq!(r, Start, b"a", End, b"a"); } #[test] fn test_empty() { let mut r = Reader::from_str(""); r.trim_text(true).expand_empty_elements(false); next_eq!(r, Empty, b"a"); } #[test] fn test_empty_can_be_expanded() { let mut r = Reader::from_str(""); r.trim_text(true).expand_empty_elements(true); next_eq!(r, Start, b"a", End, b"a"); } #[test] fn test_empty_attr() { let mut r = Reader::from_str(""); r.trim_text(true).expand_empty_elements(false); next_eq!(r, Empty, b"a"); } #[test] fn test_start_end_comment() { let mut r = Reader::from_str(" "); r.trim_text(true).expand_empty_elements(false); next_eq!(r, Start, b"b", Empty, b"a", Empty, b"a", Comment, b"t", End, b"b"); } #[test] fn test_start_txt_end() { let mut r = Reader::from_str("test"); r.trim_text(true); next_eq!(r, Start, b"a", Text, b"test", End, b"a"); } #[test] fn test_comment() { let mut r = Reader::from_str(""); r.trim_text(true); next_eq!(r, Comment, b"test"); } #[test] fn test_xml_decl() { let mut r = Reader::from_str(""); r.trim_text(true); let mut buf = Vec::new(); match r.read_event(&mut buf).unwrap() { Decl(ref e) => { match e.version() { Ok(v) => assert_eq!( &*v, b"1.0", "expecting version '1.0', got '{:?}", from_utf8(&*v) ), Err(e) => assert!(false, "{:?}", e), } match e.encoding() { Some(Ok(v)) => assert_eq!( &*v, b"utf-8", "expecting encoding 'utf-8', got '{:?}", from_utf8(&*v) ), Some(Err(e)) => panic!("{:?}", e), None => panic!("cannot find encoding"), } match e.standalone() { None => (), e => panic!("doesn't expect standalone, got {:?}", e), } } _ => panic!("unable to parse XmlDecl"), } } #[test] fn test_trim_test() { let txt = " "; let mut r = Reader::from_str(txt); r.trim_text(true); next_eq!(r, Start, b"a", Start, b"b", End, b"b", End, b"a"); let mut r = Reader::from_str(txt); r.trim_text(false); next_eq!( r, Text, b"", Start, b"a", Text, b"", Start, b"b", Text, b" ", End, b"b", Text, b"", End, b"a" ); } #[test] fn test_cdata() { let mut r = Reader::from_str(""); r.trim_text(true); next_eq!(r, CData, b"test"); } #[test] fn test_cdata_open_close() { let mut r = Reader::from_str(" test]]>"); r.trim_text(true); next_eq!(r, CData, b"test <> test"); } #[test] fn test_start_attr() { let mut r = Reader::from_str(""); r.trim_text(true); next_eq!(r, Start, b"a"); } #[test] fn test_nested() { let mut r = Reader::from_str("test"); r.trim_text(true).expand_empty_elements(false); next_eq!(r, Start, b"a", Start, b"b", Text, b"test", End, b"b", Empty, b"c", End, b"a"); } #[test] fn test_writer() { let txt = include_str!("../tests/documents/test_writer.xml").trim(); let mut reader = Reader::from_str(txt); reader.trim_text(true); let mut writer = Writer::new(Cursor::new(Vec::new())); let mut buf = Vec::new(); loop { match reader.read_event(&mut buf) { Ok(Eof) => break, Ok(e) => assert!(writer.write_event(e).is_ok()), Err(e) => panic!(e), } } let result = writer.into_inner().into_inner(); assert_eq!(result, txt.as_bytes()); } #[test] fn test_writer_borrow() { let txt = include_str!("../tests/documents/test_writer.xml").trim(); let mut reader = Reader::from_str(txt); reader.trim_text(true); let mut writer = Writer::new(Cursor::new(Vec::new())); let mut buf = Vec::new(); loop { match reader.read_event(&mut buf) { Ok(Eof) => break, Ok(e) => assert!(writer.write_event(&e).is_ok()), // either `e` or `&e` Err(e) => panic!(e), } } let result = writer.into_inner().into_inner(); assert_eq!(result, txt.as_bytes()); } #[test] fn test_writer_indent() { let txt = include_str!("../tests/documents/test_writer_indent.xml"); let mut reader = Reader::from_str(txt); reader.trim_text(true); let mut writer = Writer::new_with_indent(Cursor::new(Vec::new()), b' ', 4); let mut buf = Vec::new(); loop { match reader.read_event(&mut buf) { Ok(Eof) => break, Ok(e) => assert!(writer.write_event(e).is_ok()), Err(e) => panic!(e), } } let result = writer.into_inner().into_inner(); // println!("{:?}", String::from_utf8_lossy(&result)); assert_eq!(result, txt.as_bytes()); } #[test] fn test_write_empty_element_attrs() { let str_from = r#""#; let expected = r#""#; let mut reader = Reader::from_str(str_from); reader.expand_empty_elements(false); let mut writer = Writer::new(Cursor::new(Vec::new())); let mut buf = Vec::new(); loop { match reader.read_event(&mut buf) { Ok(Eof) => break, Ok(e) => assert!(writer.write_event(e).is_ok()), Err(e) => panic!(e), } } let result = writer.into_inner().into_inner(); assert_eq!(String::from_utf8(result).unwrap(), expected); } #[test] fn test_write_attrs() { let str_from = r#""#; let expected = r#""#; let mut reader = Reader::from_str(str_from); reader.trim_text(true); let mut writer = Writer::new(Cursor::new(Vec::new())); let mut buf = Vec::new(); loop { let event = match reader.read_event(&mut buf) { Ok(Eof) => break, Ok(Start(elem)) => { let mut attrs = elem.attributes().collect::>>().unwrap(); attrs.extend_from_slice(&[("a", "b").into(), ("c", "d").into()]); let mut elem = BytesStart::owned(b"copy".to_vec(), 4); elem.extend_attributes(attrs); elem.push_attribute(("x", "y\"z")); Start(elem) } Ok(End(_)) => End(BytesEnd::borrowed(b"copy")), Ok(e) => e, Err(e) => panic!(e), }; assert!(writer.write_event(event).is_ok()); } let result = writer.into_inner().into_inner(); assert_eq!(result, expected.as_bytes()); } #[test] fn test_new_xml_decl_full() { let mut writer = Writer::new(Vec::new()); writer .write_event(Decl(BytesDecl::new(b"1.2", Some(b"utf-X"), Some(b"yo")))) .expect("writing xml decl should succeed"); let result = writer.into_inner(); assert_eq!( String::from_utf8(result).expect("utf-8 output"), "".to_owned(), "writer output (LHS)" ); } #[test] fn test_new_xml_decl_standalone() { let mut writer = Writer::new(Vec::new()); writer .write_event(Decl(BytesDecl::new(b"1.2", None, Some(b"yo")))) .expect("writing xml decl should succeed"); let result = writer.into_inner(); assert_eq!( String::from_utf8(result).expect("utf-8 output"), "".to_owned(), "writer output (LHS)" ); } #[test] fn test_new_xml_decl_encoding() { let mut writer = Writer::new(Vec::new()); writer .write_event(Decl(BytesDecl::new(b"1.2", Some(b"utf-X"), None))) .expect("writing xml decl should succeed"); let result = writer.into_inner(); assert_eq!( String::from_utf8(result).expect("utf-8 output"), "".to_owned(), "writer output (LHS)" ); } #[test] fn test_new_xml_decl_version() { let mut writer = Writer::new(Vec::new()); writer .write_event(Decl(BytesDecl::new(b"1.2", None, None))) .expect("writing xml decl should succeed"); let result = writer.into_inner(); assert_eq!( String::from_utf8(result).expect("utf-8 output"), "".to_owned(), "writer output (LHS)" ); } /// This test ensures that empty XML declaration attribute values are not a problem. #[test] fn test_new_xml_decl_empty() { let mut writer = Writer::new(Vec::new()); // An empty version should arguably be an error, but we don't expect anyone to actually supply // an empty version. writer .write_event(Decl(BytesDecl::new(b"", Some(b""), Some(b"")))) .expect("writing xml decl should succeed"); let result = writer.into_inner(); assert_eq!( String::from_utf8(result).expect("utf-8 output"), "".to_owned(), "writer output (LHS)" ); } #[test] fn test_buf_position() { let mut r = Reader::from_str(""); r.trim_text(true).check_end_names(true); let mut buf = Vec::new(); match r.read_event(&mut buf) { Err(_) if r.buffer_position() == 2 => assert!(true), // error at char 2: no opening tag Err(e) => panic!( "expecting buf_pos = 2, found {}, err: {:?}", r.buffer_position(), e ), e => panic!("expecting error, found {:?}", e), } r = Reader::from_str(" tag found assert!(true); } Err(e) => panic!( "expecting buf_pos = 5, found {}, err: {:?}", r.buffer_position(), e ), e => assert!(false, "expecting error, found {:?}", e), } } #[test] fn test_namespace() { let mut r = Reader::from_str("in namespace!"); r.trim_text(true); let mut buf = Vec::new(); let mut ns_buf = Vec::new(); if let Ok((None, Start(_))) = r.read_namespaced_event(&mut buf, &mut ns_buf) { } else { assert!(false, "expecting start element with no namespace"); } if let Ok((Some(a), Start(_))) = r.read_namespaced_event(&mut buf, &mut ns_buf) { if &*a == b"www1" { assert!(true); } else { assert!(false, "expecting namespace to resolve to 'www1'"); } } else { assert!(false, "expecting namespace resolution"); } } #[test] fn test_default_namespace() { let mut r = Reader::from_str(""); r.trim_text(true); // let mut buf = Vec::new(); let mut ns_buf = Vec::new(); if let Ok((None, Start(_))) = r.read_namespaced_event(&mut buf, &mut ns_buf) { } else { assert!(false, "expecting outer start element with no namespace"); } // if let Ok((Some(a), Start(_))) = r.read_namespaced_event(&mut buf, &mut ns_buf) { if &*a == b"www1" { assert!(true); } else { assert!(false, "expecting namespace to resolve to 'www1'"); } } else { assert!(false, "expecting namespace resolution"); } // if let Ok((Some(a), End(_))) = r.read_namespaced_event(&mut buf, &mut ns_buf) { if &*a == b"www1" { assert!(true); } else { assert!(false, "expecting namespace to resolve to 'www1'"); } } else { assert!(false, "expecting namespace resolution"); } // very important: a should not be in any namespace. The default namespace only applies to // the sub-document it is defined on. if let Ok((None, End(_))) = r.read_namespaced_event(&mut buf, &mut ns_buf) { } else { assert!(false, "expecting outer end element with no namespace"); } } #[test] fn test_default_namespace_reset() { let mut r = Reader::from_str(""); r.trim_text(true); let mut buf = Vec::new(); let mut ns_buf = Vec::new(); if let Ok((Some(a), Start(_))) = r.read_namespaced_event(&mut buf, &mut ns_buf) { assert_eq!( &a[..], b"www1", "expecting outer start element with to resolve to 'www1'" ); } else { panic!("expecting outer start element with to resolve to 'www1'"); } match r.read_namespaced_event(&mut buf, &mut ns_buf) { Ok((None, Start(_))) => (), e => panic!("expecting inner start element, got {:?}", e), } if let Ok((None, End(_))) = r.read_namespaced_event(&mut buf, &mut ns_buf) { } else { assert!(false, "expecting inner end element"); } if let Ok((Some(a), End(_))) = r.read_namespaced_event(&mut buf, &mut ns_buf) { assert_eq!( &a[..], b"www1", "expecting outer end element with to resolve to 'www1'" ); } else { panic!("expecting outer end element with to resolve to 'www1'"); } } #[test] fn test_escaped_content() { let mut r = Reader::from_str("<test>"); r.trim_text(true); next_eq!(r, Start, b"a"); let mut buf = Vec::new(); match r.read_event(&mut buf) { Ok(Text(e)) => { if &*e != b"<test>" { panic!( "content unexpected: expecting '<test>', got '{:?}'", from_utf8(&*e) ); } match e.unescaped() { Ok(ref c) => { if &**c != b"" { panic!( "unescaped content unexpected: expecting '<test<', got '{:?}'", from_utf8(c) ) } } Err(e) => panic!( "cannot escape content at position {}: {:?}", r.buffer_position(), e ), } } Ok(e) => panic!("Expecting text event, got {:?}", e), Err(e) => panic!( "Cannot get next event at position {}: {:?}", r.buffer_position(), e ), } next_eq!(r, End, b"a"); } #[test] fn test_read_write_roundtrip_results_in_identity() { let input = r#"
data
"#; let mut reader = Reader::from_str(input); reader.trim_text(false).expand_empty_elements(false); let mut writer = Writer::new(Cursor::new(Vec::new())); let mut buf = Vec::new(); loop { match reader.read_event(&mut buf) { Ok(Eof) => break, Ok(e) => assert!(writer.write_event(e).is_ok()), Err(e) => panic!(e), } } let result = writer.into_inner().into_inner(); assert_eq!(result, input.as_bytes()); } #[test] fn test_read_write_roundtrip() { let input = r#"
data <escaped>
"#; let mut reader = Reader::from_str(input); reader.trim_text(false).expand_empty_elements(false); let mut writer = Writer::new(Cursor::new(Vec::new())); let mut buf = Vec::new(); loop { match reader.read_event(&mut buf) { Ok(Eof) => break, Ok(e) => assert!(writer.write_event(e).is_ok()), Err(e) => panic!(e), } } let result = writer.into_inner().into_inner(); assert_eq!(String::from_utf8(result).unwrap(), input.to_string()); } #[test] fn test_read_write_roundtrip_escape() { let input = r#"
data <escaped>
"#; let mut reader = Reader::from_str(input); reader.trim_text(false).expand_empty_elements(false); let mut writer = Writer::new(Cursor::new(Vec::new())); let mut buf = Vec::new(); loop { match reader.read_event(&mut buf) { Ok(Eof) => break, Ok(Text(e)) => { let t = e.escaped(); assert!(writer .write_event(Event::Text(BytesText::from_escaped(t.to_vec()))) .is_ok()); } Ok(e) => assert!(writer.write_event(e).is_ok()), Err(e) => panic!(e), } } let result = writer.into_inner().into_inner(); assert_eq!(String::from_utf8(result).unwrap(), input.to_string()); } #[test] fn test_read_write_roundtrip_escape_text() { let input = r#"
data <escaped>
"#; let mut reader = Reader::from_str(input); reader.trim_text(false).expand_empty_elements(false); let mut writer = Writer::new(Cursor::new(Vec::new())); let mut buf = Vec::new(); loop { match reader.read_event(&mut buf) { Ok(Eof) => break, Ok(Text(e)) => { let t = e.unescape_and_decode(&reader).unwrap(); assert!(writer .write_event(Event::Text(BytesText::from_plain_str(&t))) .is_ok()); } Ok(e) => assert!(writer.write_event(e).is_ok()), Err(e) => panic!(e), } } let result = writer.into_inner().into_inner(); assert_eq!(String::from_utf8(result).unwrap(), input.to_string()); } #[test] fn test_closing_bracket_in_single_quote_attr() { let mut r = Reader::from_str(""); r.trim_text(true); let mut buf = Vec::new(); match r.read_event(&mut buf) { Ok(Start(e)) => { let mut attrs = e.attributes(); match attrs.next() { Some(Ok(attr)) => assert_eq!(attr, ("attr".as_bytes(), ">".as_bytes()).into()), x => panic!("expected attribute 'attr', got {:?}", x), } match attrs.next() { Some(Ok(attr)) => assert_eq!(attr, ("check".as_bytes(), "2".as_bytes()).into()), x => panic!("expected attribute 'check', got {:?}", x), } assert!(attrs.next().is_none(), "expected only two attributes"); } x => panic!("expected , got {:?}", x), } next_eq!(r, End, b"a"); } #[test] fn test_closing_bracket_in_double_quote_attr() { let mut r = Reader::from_str("\" check=\"2\">"); r.trim_text(true); let mut buf = Vec::new(); match r.read_event(&mut buf) { Ok(Start(e)) => { let mut attrs = e.attributes(); match attrs.next() { Some(Ok(attr)) => assert_eq!(attr, ("attr".as_bytes(), ">".as_bytes()).into()), x => panic!("expected attribute 'attr', got {:?}", x), } match attrs.next() { Some(Ok(attr)) => assert_eq!(attr, ("check".as_bytes(), "2".as_bytes()).into()), x => panic!("expected attribute 'check', got {:?}", x), } assert!(attrs.next().is_none(), "expected only two attributes"); } x => panic!("expected , got {:?}", x), } next_eq!(r, End, b"a"); } #[test] fn test_closing_bracket_in_double_quote_mixed() { let mut r = Reader::from_str("'\" check=\"'2'\">"); r.trim_text(true); let mut buf = Vec::new(); match r.read_event(&mut buf) { Ok(Start(e)) => { let mut attrs = e.attributes(); match attrs.next() { Some(Ok(attr)) => assert_eq!(attr, ("attr".as_bytes(), "'>'".as_bytes()).into()), x => panic!("expected attribute 'attr', got {:?}", x), } match attrs.next() { Some(Ok(attr)) => assert_eq!(attr, ("check".as_bytes(), "'2'".as_bytes()).into()), x => panic!("expected attribute 'check', got {:?}", x), } assert!(attrs.next().is_none(), "expected only two attributes"); } x => panic!("expected , got {:?}", x), } next_eq!(r, End, b"a"); } #[test] fn test_closing_bracket_in_single_quote_mixed() { let mut r = Reader::from_str(""); r.trim_text(true); let mut buf = Vec::new(); match r.read_event(&mut buf) { Ok(Start(e)) => { let mut attrs = e.attributes(); match attrs.next() { Some(Ok(attr)) => assert_eq!(attr, ("attr".as_bytes(), "\">\"".as_bytes()).into()), x => panic!("expected attribute 'attr', got {:?}", x), } match attrs.next() { Some(Ok(attr)) => assert_eq!(attr, ("check".as_bytes(), "\"2\"".as_bytes()).into()), x => panic!("expected attribute 'check', got {:?}", x), } assert!(attrs.next().is_none(), "expected only two attributes"); } x => panic!("expected , got {:?}", x), } next_eq!(r, End, b"a"); } quick-xml-0.17.2/tests/xmlrs_reader_tests.rs010064400017500001750000000261651357035401500173720ustar0000000000000000extern crate quick_xml; use quick_xml::events::{BytesStart, Event}; use quick_xml::{Reader, Result}; use std::str::from_utf8; #[test] fn sample_1_short() { test( include_bytes!("documents/sample_1.xml"), include_bytes!("documents/sample_1_short.txt"), true, ); } #[test] fn sample_1_full() { test( include_bytes!("documents/sample_1.xml"), include_bytes!("documents/sample_1_full.txt"), false, ); } #[test] fn sample_2_short() { test( include_bytes!("documents/sample_2.xml"), include_bytes!("documents/sample_2_short.txt"), true, ); } #[test] fn sample_2_full() { test( include_bytes!("documents/sample_2.xml"), include_bytes!("documents/sample_2_full.txt"), false, ); } // #[test] // fn sample_3_short() { // test( // include_bytes!("documents/sample_3.xml"), // include_bytes!("documents/sample_3_short.txt"), // true // ); // } // #[test] // fn sample_3_full() { // test( // include_bytes!("documents/sample_3.xml"), // include_bytes!("documents/sample_3_full.txt"), // false // ); // } // #[test] // fn sample_4_short() { // test( // include_bytes!("documents/sample_4.xml"), // include_bytes!("documents/sample_4_short.txt"), // true // ); // } // #[test] // fn sample_4_full() { // test( // include_bytes!("documents/sample_4.xml"), // include_bytes!("documents/sample_4_full.txt"), // false // ); // // } #[test] fn sample_ns_short() { test( include_bytes!("documents/sample_ns.xml"), include_bytes!("documents/sample_ns_short.txt"), true, ); } #[test] fn eof_1() { test( br#""#, br#" |Error: Unexpected token '--' "#, true, ); test( br#""#, br#" |Error: Unexpected token '--' "#, true, ); } #[test] fn tabs_1() { test( b"\t\t", br#" StartElement(a) EmptyElement(b) EndElement(a) EndDocument "#, true, ); } #[test] fn issue_83_duplicate_attributes() { // Error when parsing attributes won't stop main event reader // as it is a lazy operation => add ending events test( br#""#, b" |StartElement(hello) |1:30 EmptyElement(some-tag, attr-error: error while parsing \ attribute at position 16: Duplicate attribute at position 9 and 16) |EndElement(hello) |EndDocument ", true, ); } #[test] fn issue_93_large_characters_in_entity_references() { test( r#"&𤶼;"#.as_bytes(), r#" |StartElement(hello) |1:10 Error while escaping character at range 1..5: Unrecognized escape symbol: Ok("𤶼") |EndElement(hello) |EndDocument "# .as_bytes(), true, ) } #[test] fn issue_98_cdata_ending_with_right_bracket() { test( br#""#, br#" |StartElement(hello) |Characters() |CData(Foo [Bar]) |Characters() |EndElement(hello) |EndDocument "#, false, ) } #[test] fn issue_105_unexpected_double_dash() { test( br#"-- "#, br#" |StartElement(hello) |Characters(-- ) |EndElement(hello) |EndDocument "#, false, ); test( br#"--"#, br#" |StartElement(hello) |Characters(--) |EndElement(hello) |EndDocument "#, false, ); test( br#"-->"#, br#" |StartElement(hello) |Characters(-->) |EndElement(hello) |EndDocument "#, false, ); test( br#""#, br#" |StartElement(hello) |Characters() |CData(--) |Characters() |EndElement(hello) |EndDocument "#, false, ); } #[test] fn issue_attributes_have_no_default_namespace() { // At the moment, the 'test' method doesn't render namespaces for attribute names. // This test only checks whether the default namespace got applied to the EmptyElement. test( br#""#, br#" |EmptyElement({urn:foo}hello [x="y"]) |EndDocument "#, true, ); } #[test] fn issue_default_namespace_on_outermost_element() { // Regression test test( br#""#, br#" |EmptyElement({urn:foo}hello) |EndDocument "#, true, ); } #[test] fn default_namespace_applies_to_end_elem() { test( br#" "#, br#" |StartElement({urn:foo}hello [x="y"]) |EmptyElement({urn:foo}inner) |EndElement({urn:foo}hello) |EndDocument "#, true, ); } fn test(input: &[u8], output: &[u8], is_short: bool) { let mut reader = Reader::from_reader(input); reader .trim_text(is_short) .check_comments(true) .expand_empty_elements(false); let mut spec_lines = SpecIter(output).enumerate(); let mut buf = Vec::new(); let mut ns_buffer = Vec::new(); if !is_short { // discard first whitespace reader.read_event(&mut buf).unwrap(); } loop { buf.clear(); let event = reader.read_namespaced_event(&mut buf, &mut ns_buffer); let line = xmlrs_display(&event); if let Some((n, spec)) = spec_lines.next() { if spec.trim() == "EndDocument" { break; } if line.trim() != spec.trim() { panic!( "\n-------------------\n\ Unexpected event at line {}:\n\ Expected: {}\nFound: {}\n\ -------------------\n", n + 1, spec, line ); } } else { if line == "EndDocument" { break; } panic!("Unexpected event: {}", line); } if !is_short && line.starts_with("StartDocument") { // advance next Characters(empty space) ... if let Ok(Event::Text(ref e)) = reader.read_event(&mut Vec::new()) { if e.iter().any(|b| match *b { b' ' | b'\r' | b'\n' | b'\t' => false, _ => true, }) { panic!("Reader expects empty Text event after a StartDocument"); } } else { panic!("Reader expects empty Text event after a StartDocument"); } } } } fn namespace_name(n: &Option<&[u8]>, name: &[u8]) -> String { match *n { Some(n) => format!("{{{}}}{}", from_utf8(n).unwrap(), from_utf8(name).unwrap()), None => from_utf8(name).unwrap().to_owned(), } } fn make_attrs(e: &BytesStart) -> ::std::result::Result { let mut atts = Vec::new(); for a in e.attributes() { match a { Ok(a) => { if a.key.len() < 5 || !a.key.starts_with(b"xmlns") { atts.push(format!( "{}=\"{}\"", from_utf8(a.key).unwrap(), from_utf8(&*a.unescaped_value().unwrap()).unwrap() )); } } Err(e) => return Err(e.to_string()), } } Ok(atts.join(", ")) } fn xmlrs_display(opt_event: &Result<(Option<&[u8]>, Event)>) -> String { match opt_event { Ok((ref n, Event::Start(ref e))) => { let name = namespace_name(n, e.name()); match make_attrs(e) { Ok(ref attrs) if attrs.is_empty() => format!("StartElement({})", &name), Ok(ref attrs) => format!("StartElement({} [{}])", &name, &attrs), Err(e) => format!("StartElement({}, attr-error: {})", &name, &e), } } Ok((ref n, Event::Empty(ref e))) => { let name = namespace_name(n, e.name()); match make_attrs(e) { Ok(ref attrs) if attrs.is_empty() => format!("EmptyElement({})", &name), Ok(ref attrs) => format!("EmptyElement({} [{}])", &name, &attrs), Err(e) => format!("EmptyElement({}, attr-error: {})", &name, &e), } } Ok((ref n, Event::End(ref e))) => format!("EndElement({})", namespace_name(n, e.name())), Ok((_, Event::Comment(ref e))) => format!("Comment({})", from_utf8(e).unwrap()), Ok((_, Event::CData(ref e))) => format!("CData({})", from_utf8(e).unwrap()), Ok((_, Event::Text(ref e))) => match e.unescaped() { Ok(c) => format!("Characters({})", from_utf8(&*c).unwrap()), Err(ref e) => format!("{}", e), }, Ok((_, Event::Decl(ref e))) => { let version_cow = e.version().unwrap(); let version = from_utf8(version_cow.as_ref()).unwrap(); let encoding_cow = e.encoding().unwrap().unwrap(); let encoding = from_utf8(encoding_cow.as_ref()).unwrap(); format!("StartDocument({}, {})", version, encoding) } Ok((_, Event::Eof)) => format!("EndDocument"), Ok((_, Event::PI(ref e))) => format!("ProcessingInstruction(PI={})", from_utf8(e).unwrap()), Err(ref e) => format!("Error: {}", e), Ok((_, Event::DocType(ref e))) => format!("DocType({})", from_utf8(e).unwrap()), } } struct SpecIter<'a>(&'a [u8]); impl<'a> Iterator for SpecIter<'a> { type Item = &'a str; fn next(&mut self) -> Option<&'a str> { let start = self .0 .iter() .position(|b| match *b { b' ' | b'\r' | b'\n' | b'\t' | b'|' | b':' => false, b'0'..=b'9' => false, _ => true, }) .unwrap_or(0); if let Some(p) = self.0.windows(2).position(|w| w == b")\n") { let (prev, next) = self.0.split_at(p + 1); self.0 = next; Some(from_utf8(&prev[start..]).expect("Error decoding to uft8")) } else { if self.0.is_empty() { None } else { let p = self.0; self.0 = &[]; Some(from_utf8(&p[start..]).unwrap()) } } } } quick-xml-0.17.2/.cargo_vcs_info.json0000644000000001120000000000000130660ustar00{ "git": { "sha1": "8019580c5f509fd0060354c5b14649ad46a1c592" } } quick-xml-0.17.2/Cargo.lock0000644000000071200000000000000110470ustar00# This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] name = "cfg-if" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "encoding_rs" version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "memchr" version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "proc-macro2" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "quick-xml" version = "0.17.2" dependencies = [ "encoding_rs 0.8.20 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "quote" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde" version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "serde_derive 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_derive" version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "syn 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "syn" version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "unicode-xid" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] "checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4" "checksum encoding_rs 0.8.20 (registry+https://github.com/rust-lang/crates.io-index)" = "87240518927716f79692c2ed85bfe6e98196d18c6401ec75355760233a7e12e9" "checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" "checksum proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9c9e470a8dc4aeae2dee2f335e8f533e2d4b347e1434e5671afc49b054592f27" "checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" "checksum serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)" = "1217f97ab8e8904b57dd22eb61cde455fa7446a9c1cf43966066da047c1f3702" "checksum serde_derive 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)" = "a8c6faef9a2e64b0064f48570289b4bf8823b7581f1d6157c1b52152306651d0" "checksum syn 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)" = "bb2945f8e42087fab9a15d3441f8483b7d2076a4d9573a348ac0d9f146ddd39e" "checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"