diff --git a/.gitignore b/.gitignore index 18b12d1c..0ca6673f 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ build Cargo.lock Icon? +public/test_site/test.txt diff --git a/Cargo.toml b/Cargo.toml index b25ed80f..a90f2398 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ name = "serde_yml" readme = "README.md" repository = "https://github.com/sebastienrousseau/serde_yml" rust-version = "1.60" -version = "0.0.9" +version = "0.0.10" include = [ "/CONTRIBUTING.md", "/LICENSE-APACHE", @@ -40,7 +40,7 @@ include = [ [dependencies] indexmap = "2.2.6" itoa = "1.0.11" -libyml = "0.0.1" +libyml = "0.0.3" log = { version = "0.4.21", features = ["std"] } memchr = "2.7.2" ryu = "1.0.18" diff --git a/README.md b/README.md index 28917b7e..7992858c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -Serde YML logo @@ -41,17 +41,17 @@ and for inspiring this project. ```toml [dependencies] serde = "1.0" -serde_yml = "0.0.9" +serde_yml = "0.0.10" ``` Release notes are available under [GitHub releases][04]. -## Using Serde YAML +## Using Serde YML [API documentation is available in rustdoc form][docs.rs] but the general idea is: -[docs.rs]: https://docs.rs/serde_yaml +[docs.rs]: https://docs.rs/serde_yml ```rust use serde::{Serialize, Deserialize}; diff --git a/TEMPLATE.md b/TEMPLATE.md index 3810d322..51e8225c 100644 --- a/TEMPLATE.md +++ b/TEMPLATE.md @@ -42,21 +42,21 @@ Unless you explicitly state otherwise, any contribution intentionally submitted #### Forked Serde YAML -- **Hard reset**: Hard reset to the latest @dtolnay [latest release](https://github.com/dtolnay/serde-yaml/commit/2009506d33767dfc88e979d6bc0d53d09f941c94) to keep traceability and retain commits history to the original [Serde YAML](https://github.com/dtolnay/serde-yaml) codebase and credits to the maintainers. -- **Renaming**: This project, has been renamed to `Serde YML` to avoid confusion with the original Serde YAML crate which is now archived and no longer maintained. While `Serde YML` started as a fork of serde-yaml, it has now evolved into a separate library with its own goals and direction in mind and does not intend to replace the original serde-yaml crate. +- **Hard reset**: Hard reset to the latest @dtolnay [latest release](https://github.com/dtolnay/serde-yaml/commit/2009506d33767dfc88e979d6bc0d53d09f941c94) to keep traceability and retain commits history to the original [Serde YAML](https://github.com/dtolnay/serde-yaml) codebase and credits to the maintainers. +- **Renaming**: This project, has been renamed to `Serde YML` to avoid confusion with the original Serde YAML crate which is now archived and no longer maintained. While `Serde YML` started as a fork of serde-yaml, it has now evolved into a separate library with its own goals and direction in mind and does not intend to replace the original serde-yaml crate. #### CI Improvements -- **ci(serde-yaml)**: Added a missing release workflow and made minor tweaks to the README for better clarity and documentation. This update ensures smoother and more reliable release processes. - - Commit: `ci(serde-yaml): :green_heart: add missing release workflow and minor tweaks in README` +- **ci(serde-yaml)**: Added a missing release workflow and made minor tweaks to the README for better clarity and documentation. This update ensures smoother and more reliable release processes. + - Commit: `ci(serde-yaml): :green_heart: add missing release workflow and minor tweaks in README` #### Testing Enhancements -- **test(serde-yaml)**: Enhanced test coverage by adding new unit tests for `mapping.rs`. These tests ensure the robustness and reliability of the `Mapping` struct and its associated methods. +- **test(serde-yaml)**: Enhanced test coverage by adding new unit tests for `mapping.rs`. These tests ensure the robustness and reliability of the `Mapping` struct and its associated methods. - - Commit: `test(serde-yaml): :white_check_mark: add new tests for `mapping.rs`` + - Commit: `test(serde-yaml): :white_check_mark: add new tests for`mapping.rs`` -- **test(serde-yaml)**: Expanded the test suite by adding comprehensive unit tests for the `ser.rs` module. The new tests cover various serialization scenarios, including scalar values, sequences, maps, nested structures, optional fields, and custom serializers. - - Commit: `test(serde-yaml): :white_check_mark: add unit tests for the `ser.rs` module` +- **test(serde-yaml)**: Expanded the test suite by adding comprehensive unit tests for the `ser.rs` module. The new tests cover various serialization scenarios, including scalar values, sequences, maps, nested structures, optional fields, and custom serializers. + - Commit: `test(serde-yaml): :white_check_mark: add unit tests for the`ser.rs`module` -**Full Changelog**: https://github.com/sebastienrousseau/serde_yml/commits/v0.0.9 +**Full Changelog**: diff --git a/examples/example.rs b/examples/example.rs index 095a928b..d2afb97a 100644 --- a/examples/example.rs +++ b/examples/example.rs @@ -11,9 +11,15 @@ /// Contains the example modules for the `loader` module. mod loader; +/// Contains the example modules for the `modules` module. +mod modules; + /// Contains the example modules for the `serializer` module. mod serializer; +/// Contains the example modules for the `value` module. +mod value; + /// Examples for the `with` module. mod with; @@ -29,9 +35,15 @@ fn main() { // Run the example module `loader`. loader::main(); + // Run the example module `modules`. + modules::main(); + // Run the example module `serializer`. serializer::main(); + // Run the example module `value`. + value::main(); + // Run the example module `with`. with::main(); diff --git a/examples/libyml/emitter_examples.rs b/examples/libyml/emitter_examples.rs new file mode 100644 index 00000000..f683bc24 --- /dev/null +++ b/examples/libyml/emitter_examples.rs @@ -0,0 +1,345 @@ +//! Examples for the `Emitter` struct and its methods in the `emitter` module. +//! +//! This file demonstrates the creation, usage, and various functionalities of the `Emitter` for emitting YAML events, including scalars, sequences, mappings, and document/stream events. + +use serde_yml::libyml::emitter::{ + Emitter, Event, Mapping, Scalar, ScalarStyle, Sequence, +}; +use std::io::Cursor; + +pub(crate) fn main() { + // Print a message to indicate the file being executed + println!("\n❯ Executing examples/libyml/emitter_examples.rs"); + + // Example: Emitting a stream start and end event + let mut buffer = Cursor::new(Vec::new()); + { + let mut emitter = Emitter::new(Box::new(&mut buffer)); + emitter.emit(Event::StreamStart).unwrap(); + emitter.emit(Event::StreamEnd).unwrap(); + } + let output = + String::from_utf8_lossy(&buffer.into_inner()).to_string(); + println!("\n✅ Emitted stream start and end: {}", output); + + // Example: Emitting a document start and end event with a scalar + let mut buffer = Cursor::new(Vec::new()); + { + let mut emitter = Emitter::new(Box::new(&mut buffer)); + emitter.emit(Event::StreamStart).unwrap(); + emitter.emit(Event::DocumentStart).unwrap(); + emitter + .emit(Event::Scalar(Scalar { + tag: None, + value: "hello", + style: ScalarStyle::Plain, + })) + .unwrap(); + emitter.emit(Event::DocumentEnd).unwrap(); + emitter.emit(Event::StreamEnd).unwrap(); + } + let output = + String::from_utf8_lossy(&buffer.into_inner()).to_string(); + println!("\n✅ Emitted document with scalar: {}", output); + + // Example: Emitting a sequence + let mut buffer = Cursor::new(Vec::new()); + { + let mut emitter = Emitter::new(Box::new(&mut buffer)); + emitter.emit(Event::StreamStart).unwrap(); + emitter.emit(Event::DocumentStart).unwrap(); + emitter + .emit(Event::SequenceStart(Sequence { tag: None })) + .unwrap(); + emitter + .emit(Event::Scalar(Scalar { + tag: None, + value: "item1", + style: ScalarStyle::Plain, + })) + .unwrap(); + emitter + .emit(Event::Scalar(Scalar { + tag: None, + value: "item2", + style: ScalarStyle::Plain, + })) + .unwrap(); + emitter.emit(Event::SequenceEnd).unwrap(); + emitter.emit(Event::DocumentEnd).unwrap(); + emitter.emit(Event::StreamEnd).unwrap(); + } + let output = + String::from_utf8_lossy(&buffer.into_inner()).to_string(); + println!("\n✅ Emitted sequence: {}", output); + + // Example: Emitting a mapping + let mut buffer = Cursor::new(Vec::new()); + { + let mut emitter = Emitter::new(Box::new(&mut buffer)); + emitter.emit(Event::StreamStart).unwrap(); + emitter.emit(Event::DocumentStart).unwrap(); + emitter + .emit(Event::MappingStart(Mapping { tag: None })) + .unwrap(); + emitter + .emit(Event::Scalar(Scalar { + tag: None, + value: "key1", + style: ScalarStyle::Plain, + })) + .unwrap(); + emitter + .emit(Event::Scalar(Scalar { + tag: None, + value: "value1", + style: ScalarStyle::Plain, + })) + .unwrap(); + emitter + .emit(Event::Scalar(Scalar { + tag: None, + value: "key2", + style: ScalarStyle::Plain, + })) + .unwrap(); + emitter + .emit(Event::Scalar(Scalar { + tag: None, + value: "value2", + style: ScalarStyle::Plain, + })) + .unwrap(); + emitter.emit(Event::MappingEnd).unwrap(); + emitter.emit(Event::DocumentEnd).unwrap(); + emitter.emit(Event::StreamEnd).unwrap(); + } + let output = + String::from_utf8_lossy(&buffer.into_inner()).to_string(); + println!("\n✅ Emitted mapping: {}", output); + + // Example: Flushing the emitter + let mut buffer = Cursor::new(Vec::new()); + { + let mut emitter = Emitter::new(Box::new(&mut buffer)); + emitter.emit(Event::StreamStart).unwrap(); + emitter.emit(Event::DocumentStart).unwrap(); + emitter + .emit(Event::Scalar(Scalar { + tag: None, + value: "hello", + style: ScalarStyle::Plain, + })) + .unwrap(); + emitter.flush().unwrap(); + emitter.emit(Event::DocumentEnd).unwrap(); + emitter.emit(Event::StreamEnd).unwrap(); + } + let output = + String::from_utf8_lossy(&buffer.into_inner()).to_string(); + println!("\n✅ Emitted and flushed: {}", output); + + // Example: Emitting scalar with tag + let mut buffer = Cursor::new(Vec::new()); + { + let mut emitter = Emitter::new(Box::new(&mut buffer)); + emitter.emit(Event::StreamStart).unwrap(); + emitter.emit(Event::DocumentStart).unwrap(); + emitter + .emit(Event::Scalar(Scalar { + tag: Some("!mytag".to_string()), + value: "hello", + style: ScalarStyle::Plain, + })) + .unwrap(); + emitter.emit(Event::DocumentEnd).unwrap(); + emitter.emit(Event::StreamEnd).unwrap(); + } + let output = + String::from_utf8_lossy(&buffer.into_inner()).to_string(); + println!("\n✅ Emitted scalar with tag: {}", output); + + // Example: Emitting sequence with tag + let mut buffer = Cursor::new(Vec::new()); + { + let mut emitter = Emitter::new(Box::new(&mut buffer)); + emitter.emit(Event::StreamStart).unwrap(); + emitter.emit(Event::DocumentStart).unwrap(); + emitter + .emit(Event::SequenceStart(Sequence { + tag: Some("!mytag".to_string()), + })) + .unwrap(); + emitter + .emit(Event::Scalar(Scalar { + tag: None, + value: "item1", + style: ScalarStyle::Plain, + })) + .unwrap(); + emitter + .emit(Event::Scalar(Scalar { + tag: None, + value: "item2", + style: ScalarStyle::Plain, + })) + .unwrap(); + emitter.emit(Event::SequenceEnd).unwrap(); + emitter.emit(Event::DocumentEnd).unwrap(); + emitter.emit(Event::StreamEnd).unwrap(); + } + let output = + String::from_utf8_lossy(&buffer.into_inner()).to_string(); + println!("\n✅ Emitted sequence with tag: {}", output); + + // Example: Emitting mapping with tag + let mut buffer = Cursor::new(Vec::new()); + { + let mut emitter = Emitter::new(Box::new(&mut buffer)); + emitter.emit(Event::StreamStart).unwrap(); + emitter.emit(Event::DocumentStart).unwrap(); + emitter + .emit(Event::MappingStart(Mapping { + tag: Some("!mytag".to_string()), + })) + .unwrap(); + emitter + .emit(Event::Scalar(Scalar { + tag: None, + value: "key1", + style: ScalarStyle::Plain, + })) + .unwrap(); + emitter + .emit(Event::Scalar(Scalar { + tag: None, + value: "value1", + style: ScalarStyle::Plain, + })) + .unwrap(); + emitter + .emit(Event::Scalar(Scalar { + tag: None, + value: "key2", + style: ScalarStyle::Plain, + })) + .unwrap(); + emitter + .emit(Event::Scalar(Scalar { + tag: None, + value: "value2", + style: ScalarStyle::Plain, + })) + .unwrap(); + emitter.emit(Event::MappingEnd).unwrap(); + emitter.emit(Event::DocumentEnd).unwrap(); + emitter.emit(Event::StreamEnd).unwrap(); + } + let output = + String::from_utf8_lossy(&buffer.into_inner()).to_string(); + println!("\n✅ Emitted mapping with tag: {}", output); + + // Example: Emitting an empty sequence + let mut buffer = Cursor::new(Vec::new()); + { + let mut emitter = Emitter::new(Box::new(&mut buffer)); + emitter.emit(Event::StreamStart).unwrap(); + emitter.emit(Event::DocumentStart).unwrap(); + emitter + .emit(Event::SequenceStart(Sequence { tag: None })) + .unwrap(); + emitter.emit(Event::SequenceEnd).unwrap(); + emitter.emit(Event::DocumentEnd).unwrap(); + emitter.emit(Event::StreamEnd).unwrap(); + } + let output = + String::from_utf8_lossy(&buffer.into_inner()).to_string(); + println!("\n✅ Emitted empty sequence: {}", output); + + // Example: Emitting an empty mapping + let mut buffer = Cursor::new(Vec::new()); + { + let mut emitter = Emitter::new(Box::new(&mut buffer)); + emitter.emit(Event::StreamStart).unwrap(); + emitter.emit(Event::DocumentStart).unwrap(); + emitter + .emit(Event::MappingStart(Mapping { tag: None })) + .unwrap(); + emitter.emit(Event::MappingEnd).unwrap(); + emitter.emit(Event::DocumentEnd).unwrap(); + emitter.emit(Event::StreamEnd).unwrap(); + } + let output = + String::from_utf8_lossy(&buffer.into_inner()).to_string(); + println!("\n✅ Emitted empty mapping: {}", output); + + // Example: Emitting a nested sequence + let mut buffer = Cursor::new(Vec::new()); + { + let mut emitter = Emitter::new(Box::new(&mut buffer)); + emitter.emit(Event::StreamStart).unwrap(); + emitter.emit(Event::DocumentStart).unwrap(); + emitter + .emit(Event::SequenceStart(Sequence { tag: None })) + .unwrap(); + emitter + .emit(Event::SequenceStart(Sequence { tag: None })) + .unwrap(); + emitter + .emit(Event::Scalar(Scalar { + tag: None, + value: "nested", + style: ScalarStyle::Plain, + })) + .unwrap(); + emitter.emit(Event::SequenceEnd).unwrap(); + emitter.emit(Event::SequenceEnd).unwrap(); + emitter.emit(Event::DocumentEnd).unwrap(); + emitter.emit(Event::StreamEnd).unwrap(); + } + let output = + String::from_utf8_lossy(&buffer.into_inner()).to_string(); + println!("\n✅ Emitted nested sequence: {}", output); + + // Example: Emitting a nested mapping + let mut buffer = Cursor::new(Vec::new()); + { + let mut emitter = Emitter::new(Box::new(&mut buffer)); + emitter.emit(Event::StreamStart).unwrap(); + emitter.emit(Event::DocumentStart).unwrap(); + emitter + .emit(Event::MappingStart(Mapping { tag: None })) + .unwrap(); + emitter + .emit(Event::Scalar(Scalar { + tag: None, + value: "key", + style: ScalarStyle::Plain, + })) + .unwrap(); + emitter + .emit(Event::MappingStart(Mapping { tag: None })) + .unwrap(); + emitter + .emit(Event::Scalar(Scalar { + tag: None, + value: "nested_key", + style: ScalarStyle::Plain, + })) + .unwrap(); + emitter + .emit(Event::Scalar(Scalar { + tag: None, + value: "nested_value", + style: ScalarStyle::Plain, + })) + .unwrap(); + emitter.emit(Event::MappingEnd).unwrap(); + emitter.emit(Event::MappingEnd).unwrap(); + emitter.emit(Event::DocumentEnd).unwrap(); + emitter.emit(Event::StreamEnd).unwrap(); + } + let output = + String::from_utf8_lossy(&buffer.into_inner()).to_string(); + println!("\n✅ Emitted nested mapping: {}", output); +} diff --git a/examples/libyml/mod.rs b/examples/libyml/mod.rs index 3a0ae6ce..f9fa13d1 100644 --- a/examples/libyml/mod.rs +++ b/examples/libyml/mod.rs @@ -1,8 +1,32 @@ /// This module contains the `tag` example. -pub(crate) mod tag; +pub(crate) mod tag_examples; + +/// This module contains the `emitter` example. +pub(crate) mod emitter_examples; + +/// This module contains the `parser` example. +pub(crate) mod parser_examples; + +/// This module contains the `safe_cstr` example. +pub(crate) mod safe_cstr_examples; + +/// This module contains the `util` example. +pub(crate) mod util_examples; /// The main function that runs all the example modules. pub(crate) fn main() { + // Run the example module `emitter`. + emitter_examples::main(); + + // Run the example module `parser`. + parser_examples::main(); + + // Run the example module `safe_cstr`. + safe_cstr_examples::main(); + // Run the example module `tag`. - tag::main(); + tag_examples::main(); + + // Run the example module `util`. + util_examples::main(); } diff --git a/examples/libyml/parser_examples.rs b/examples/libyml/parser_examples.rs new file mode 100644 index 00000000..f7d7f06b --- /dev/null +++ b/examples/libyml/parser_examples.rs @@ -0,0 +1,285 @@ +//! Examples for the `Parser` struct and its methods in the `parser` module. +//! +//! This file demonstrates the creation, usage, and event parsing of `Parser` instances, +//! as well as handling different types of YAML events and input scenarios. + +use serde_yml::libyml::parser::{Event, Parser}; +use std::borrow::Cow; + +#[allow(clippy::single_match)] +pub(crate) fn main() { + // Print a message to indicate the file being executed. + println!("\n❯ Executing examples/libyml/parser_examples.rs"); + + // Example 1: Creating a parser and parsing a stream start event + { + let input = Cow::Borrowed(b"foo: bar\n"); + let mut parser = Parser::new(Cow::Borrowed(input.as_ref())); + match parser.parse_next_event() { + Ok((event, _)) => { + match event { + Event::StreamStart => { + println!("\n✅ Stream start event parsed successfully.") + } + _ => println!("\n❌ Unexpected event."), + } + } + Err(err) => println!("Error parsing event: {:?}", err), + } + } + + // Example 2: Parsing a stream end event + { + let input = Cow::Borrowed(b"foo: bar\n").as_ref(); + let mut parser = Parser::new(Cow::Borrowed(input)); + while let Ok((event, _)) = parser.parse_next_event() { + if matches!(event, Event::StreamEnd) { + println!("\n✅ Stream end event parsed successfully."); + break; + } + } + } + + // Example 3: Parsing a document start event + { + let input = Cow::Borrowed(b"---\nfoo: bar\n").as_ref(); + let mut parser = Parser::new(Cow::Borrowed(input)); + while let Ok((event, _)) = parser.parse_next_event() { + if matches!(event, Event::DocumentStart) { + println!( + "\n✅ Document start event parsed successfully." + ); + break; + } + } + } + + // Example 4: Parsing a document end event + { + let input = + Cow::Borrowed(b"foo: bar\n---\nbaz: qux\n").as_ref(); + let mut parser = Parser::new(Cow::Borrowed(input)); + while let Ok((event, _)) = parser.parse_next_event() { + if matches!(event, Event::DocumentEnd) { + println!( + "\n✅ Document end event parsed successfully." + ); + break; + } + } + } + + // Example 5: Parsing a scalar event + { + let input = Cow::Borrowed(b"bar\n").as_ref(); + let mut parser = Parser::new(Cow::Borrowed(input)); + while let Ok((event, _)) = parser.parse_next_event() { + if let Event::Scalar(scalar) = event { + println!( + "\n✅ Scalar event parsed successfully with value: {:?}", + scalar.value + ); + break; + } + } + } + + // Example 6: Parsing a sequence start event + { + let input = Cow::Borrowed(b"- item1\n- item2\n").as_ref(); + let mut parser = Parser::new(Cow::Borrowed(input)); + while let Ok((event, _)) = parser.parse_next_event() { + if matches!(event, Event::SequenceStart(_)) { + println!( + "\n✅ Sequence start event parsed successfully." + ); + break; + } + } + } + + // Example 7: Parsing a sequence end event + { + let input = Cow::Borrowed(b"- item1\n- item2\n").as_ref(); + let mut parser = Parser::new(Cow::Borrowed(input)); + while let Ok((event, _)) = parser.parse_next_event() { + if matches!(event, Event::SequenceEnd) { + println!( + "\n✅ Sequence end event parsed successfully." + ); + break; + } + } + } + + // Example 8: Parsing a mapping start event + { + let input = Cow::Borrowed(b"key: value\n").as_ref(); + let mut parser = Parser::new(Cow::Borrowed(input)); + while let Ok((event, _)) = parser.parse_next_event() { + if matches!(event, Event::MappingStart(_)) { + println!( + "\n✅ Mapping start event parsed successfully." + ); + break; + } + } + } + + // Example 9: Parsing a mapping end event + { + let input = Cow::Borrowed(b"key: value\n").as_ref(); + let mut parser = Parser::new(Cow::Borrowed(input)); + while let Ok((event, _)) = parser.parse_next_event() { + if matches!(event, Event::MappingEnd) { + println!("\n✅ Mapping end event parsed successfully."); + break; + } + } + } + + // Example 10: Handling unexpected input + { + let input = Cow::Borrowed(b"unexpected: [value").as_ref(); // Malformed YAML + let mut parser = Parser::new(Cow::Borrowed(input)); + match parser.parse_next_event() { + Ok(_) => println!( + "\n❌ Unexpectedly parsed malformed input without error." + ), + Err(err) => { + println!("\n❌ Error parsing malformed input: {:?}", err) + } + } + } + + // Example 11: Handling empty input + { + let input = Cow::Borrowed(b"").as_ref(); + let mut parser = Parser::new(Cow::Borrowed(input)); + match parser.parse_next_event() { + Ok((event, _)) => match event { + Event::StreamEnd => println!("Stream end event parsed successfully for empty input."), + _ => println!("Unexpected event for empty input."), + }, + Err(err) => println!("Error parsing empty input: {:?}", err), + } + } + + // Example 12: Parsing nested sequences + { + let input = + Cow::Borrowed(b"- item1\n- - nested1\n - nested2\n") + .as_ref(); + let mut parser = Parser::new(Cow::Borrowed(input)); + let mut found_nested_start = false; + + while let Ok((event, _)) = parser.parse_next_event() { + if matches!(event, Event::SequenceStart(_)) { + if found_nested_start { + println!("\n✅ Nested sequence start event parsed successfully."); + break; + } else { + found_nested_start = true; + } + } + } + + if !found_nested_start { + println!("\n❌ Nested sequence start event was not found."); + } + } + + // Example 13: Parsing mixed content (sequence and mapping) + { + let input = + Cow::Borrowed(b"- item1\nkey: value\n- item2\n").as_ref(); + let mut parser = Parser::new(Cow::Borrowed(input)); + let mut found_sequence = false; + let mut found_mapping = false; + + while let Ok((event, _)) = parser.parse_next_event() { + if matches!(event, Event::SequenceStart(_)) { + found_sequence = true; + } + if matches!(event, Event::MappingStart(_)) { + found_mapping = true; + } + if found_sequence && found_mapping { + println!("\n✅ Mixed content parsed successfully (sequence and mapping)."); + break; + } + } + + if !found_sequence { + println!("\n❌ Sequence start event was not found."); + } + if !found_mapping { + println!("\n❌ Mapping start event was not found."); + } + } + + // Example 14: Error handling with invalid input + { + let input = Cow::Borrowed(b"invalid: [yaml").as_ref(); // Invalid YAML + let mut parser = Parser::new(Cow::Borrowed(input)); + match parser.parse_next_event() { + Ok(_) => println!( + "\n❌ Unexpectedly parsed invalid input without error." + ), + Err(err) => println!( + "\n✅ Correctly handled error for invalid input: {:?}", + err + ), + } + } + + // Example 15: Parser initialization check + { + let input = Cow::Borrowed(b"foo: bar\n").as_ref(); + let parser = Parser::new(Cow::Borrowed(input)); + if parser.is_ok() { + println!("\n✅ Parser initialized successfully."); + } else { + println!("\n❌ Parser failed to initialize."); + } + } + + // Example 16: Parsing complex nested structures + { + let input = Cow::Borrowed( + b"- item1\n- item2:\n - nested1\n - nested2\n- item3\n", + ) + .as_ref(); + let mut parser = Parser::new(Cow::Borrowed(input)); + while let Ok((event, _)) = parser.parse_next_event() { + match event { + Event::SequenceStart(_) => { + println!("\n✅ Sequence start event found.") + } + Event::MappingStart(_) => { + println!("\n✅ Mapping start event found.") + } + Event::Scalar(scalar) => { + println!("\n✅ Scalar value: {:?}", scalar.value) + } + _ => {} + } + } + } + + // Example 17: Handling comments in YAML (if supported) + { + let input = + Cow::Borrowed(b"# This is a comment\nfoo: bar\n").as_ref(); + let mut parser = Parser::new(Cow::Borrowed(input)); + while let Ok((event, _)) = parser.parse_next_event() { + match event { + Event::Scalar(scalar) => { + println!("\n✅ Scalar value: {:?}", scalar.value) + } + // Event::Comment(comment) => println!("\n✅ Comment: {:?}", comment), // Uncomment if comments are supported + _ => {} + } + } + } +} diff --git a/examples/libyml/safe_cstr_examples.rs b/examples/libyml/safe_cstr_examples.rs new file mode 100644 index 00000000..8477cfa1 --- /dev/null +++ b/examples/libyml/safe_cstr_examples.rs @@ -0,0 +1,137 @@ +//! Examples for the `CStr` struct and its methods in the `safe_cstr` module. +//! +//! This file demonstrates the creation, usage, and comparison of `CStr` instances, +//! as well as the usage of its various methods. + +use serde_yml::libyml::safe_cstr::{CStr, CStrError}; +use std::ffi::CString; +use std::ptr::NonNull; + +pub(crate) fn main() { + // Print a message to indicate the file being executed. + println!("\n❯ Executing examples/libyml/safe_cstr_examples.rs"); + + // Example: Creating a new CStr instance from a byte slice with a null terminator + let bytes: &'static [u8] = b"hello\0"; + match CStr::from_bytes_with_nul(bytes) { + Ok(cstr) => { + println!("\n✅ Created a new CStr instance: {:?}", cstr) + } + Err(_) => println!("\n❌ Failed to create CStr instance"), + } + + // Example: Creating a new CStr instance from a byte slice without a null terminator + let bytes: &'static [u8] = b"hello"; + match CStr::from_bytes_with_nul(bytes) { + Ok(_) => println!("\n❌ This should not happen"), + Err(CStrError) => println!("\n✅ Correctly failed to create CStr instance without null terminator"), + } + + // Example: Creating a new CStr instance from an empty byte slice + let bytes: &'static [u8] = b""; + match CStr::from_bytes_with_nul(bytes) { + Ok(_) => println!("\n❌ This should not happen"), + Err(CStrError) => println!("\n✅ Correctly failed to create CStr instance from empty byte slice"), + } + + // Example: Creating a new CStr instance from a byte slice with only a null terminator + let bytes: &'static [u8] = b"\0"; + match CStr::from_bytes_with_nul(bytes) { + Ok(cstr) => { + println!("\n✅ Created an empty CStr instance: {:?}", cstr) + } + Err(_) => println!("\n❌ Failed to create CStr instance"), + } + + // Example: Creating a new CStr instance from a byte slice with one character and a null terminator + let bytes: &'static [u8] = b"a\0"; + match CStr::from_bytes_with_nul(bytes) { + Ok(cstr) => println!( + "\n✅ Created a CStr instance with one character: {:?}", + cstr + ), + Err(_) => println!("\n❌ Failed to create CStr instance"), + } + + // Example: Creating a new CStr instance from a non-null pointer + let c_string = CString::new("hello").unwrap(); + let ptr = NonNull::new(c_string.into_raw()).unwrap(); + let cstr = CStr::from_ptr(ptr); + println!( + "\n✅ Created a new CStr instance from a pointer: {:?}", + cstr + ); + + // Example: Calculating the length of the CStr instance + let bytes: &'static [u8] = b"hello\0"; + let cstr = CStr::from_bytes_with_nul(bytes).unwrap(); + println!("\n✅ Length of the CStr instance: {}", cstr.len()); + + // Example: Checking if the CStr instance is empty + let bytes: &'static [u8] = b"\0"; + let cstr = CStr::from_bytes_with_nul(bytes).unwrap(); + println!("\n✅ Is the CStr instance empty? {}", cstr.is_empty()); + + // Example: Retrieving the underlying byte slice of the CStr instance + let bytes: &'static [u8] = b"hello\0"; + let cstr = CStr::from_bytes_with_nul(bytes).unwrap(); + println!( + "\n✅ The underlying byte slice of the CStr instance: {:?}", + cstr.to_bytes() + ); + + // Example: Using the Display implementation for CStr + let bytes: &'static [u8] = b"hello\0"; + let cstr = CStr::from_bytes_with_nul(bytes).unwrap(); + println!( + "\n✅ Display representation of the CStr instance: {}", + cstr + ); + + // Example: Using the Debug implementation for CStr + let bytes: &'static [u8] = b"hello\0"; + let cstr = CStr::from_bytes_with_nul(bytes).unwrap(); + println!( + "\n✅ Debug representation of the CStr instance: {:?}", + cstr + ); + + // Example: Using the Display implementation for CStr with invalid UTF-8 + let bytes: &'static [u8] = b"hello\xFFworld\0"; + let cstr = CStr::from_bytes_with_nul(bytes).unwrap(); + println!("\n✅ Display representation of the CStr instance with invalid UTF-8: {}", cstr); + + // Example: Using the Debug implementation for CStr with invalid UTF-8 + let bytes: &'static [u8] = b"hello\xFFworld\0"; + let cstr = CStr::from_bytes_with_nul(bytes).unwrap(); + println!("\n✅ Debug representation of the CStr instance with invalid UTF-8: {:?}", cstr); + + // Example: Handling the custom CStrError error type + let error = CStrError; + println!("\n✅ Custom CStrError message: {}", error); + + // Example: Creating a CStr instance with a very long string + const LONG_STRING_SIZE: usize = 10_000; + let mut long_string = Vec::with_capacity(LONG_STRING_SIZE + 1); + long_string.extend(std::iter::repeat(b'a').take(LONG_STRING_SIZE)); + long_string.push(b'\0'); + let bytes = Box::leak(long_string.into_boxed_slice()); + match CStr::from_bytes_with_nul(bytes) { + Ok(cstr) => println!("\n✅ Created a CStr instance with a long string: Length = {}", cstr.len()), + Err(_) => println!("\n❌ Failed to create CStr instance with long string"), + } + + // Example: Creating a CStr instance with Unicode characters + let bytes: &'static [u8] = "hello🌍\0".as_bytes(); + match CStr::from_bytes_with_nul(bytes) { + Ok(cstr) => println!("\n✅ Created a CStr instance with Unicode characters: {:?}", cstr), + Err(_) => println!("\n❌ Failed to create CStr instance with Unicode characters"), + } + + // Example: Creating a CStr instance with multiple null terminators + let bytes: &'static [u8] = b"hello\0world\0"; + match CStr::from_bytes_with_nul(bytes) { + Ok(cstr) => println!("\n✅ Created a CStr instance with multiple null terminators: {:?}", cstr), + Err(_) => println!("\n❌ Failed to create CStr instance with multiple null terminators"), + } +} diff --git a/examples/libyml/tag.rs b/examples/libyml/tag.rs deleted file mode 100644 index bae73fde..00000000 --- a/examples/libyml/tag.rs +++ /dev/null @@ -1,76 +0,0 @@ -//! Examples for the `Tag` struct and its methods in the `tag` module. -//! -//! This file demonstrates the creation, usage, and comparison of `Tag` instances, -//! as well as the usage of its various methods. - -use serde_yml::libyml::tag::Tag; -use serde_yml::libyml::tag::TagFormatError; - -pub(crate) fn main() { - // Print a message to indicate the file being executed. - println!("\n❯ Executing examples/libyml/tag.rs"); - - // Example: Creating a new Tag instance - let tag_null = Tag::new(Tag::NULL); - println!( - "\n✅ Created a new Tag instance for NULL: {:?}", - tag_null - ); - - // Example: Creating a Tag instance for a custom tag - let custom_tag = Tag::new("tag:example.org,2024:custom"); - println!( - "\n✅ Created a new Tag instance for custom tag: {:?}", - custom_tag - ); - - // Example: Checking if a Tag starts with a prefix - match custom_tag.starts_with("tag:example.org") { - Ok(true) => { - println!("\n✅ The tag starts with the given prefix.") - } - Ok(false) => println!( - "\n✅ The tag does not start with the given prefix." - ), - Err(TagFormatError) => { - println!("\n✅ Error: The prefix is longer than the tag.") - } - } - - // Example: Comparing a Tag with a &str - let comparison_str = "tag:example.org,2024:custom"; - if custom_tag == comparison_str { - println!("\n✅ The tag is equal to the given string slice."); - } else { - println!( - "\n✅ The tag is not equal to the given string slice." - ); - } - - // Example: Using Deref to access the underlying byte slice - let tag_bytes: &[u8] = &custom_tag; - println!( - "\n✅ The underlying byte slice of the tag: {:?}", - tag_bytes - ); - - // Example: Using the Debug implementation - println!( - "\n✅ Debug representation of the custom tag: {:?}", - custom_tag - ); - - // Example: Using Tag constants - let tag_bool = Tag::new(Tag::BOOL); - println!( - "\n✅ Created a new Tag instance for BOOL: {:?}", - tag_bool - ); - let tag_int = Tag::new(Tag::INT); - println!("\n✅ Created a new Tag instance for INT: {:?}", tag_int); - let tag_float = Tag::new(Tag::FLOAT); - println!( - "\n✅ Created a new Tag instance for FLOAT: {:?}", - tag_float - ); -} diff --git a/examples/libyml/tag_examples.rs b/examples/libyml/tag_examples.rs new file mode 100644 index 00000000..cd3c93f1 --- /dev/null +++ b/examples/libyml/tag_examples.rs @@ -0,0 +1,164 @@ +//! Examples for the `Tag` struct and its methods in the `tag` module. +//! +//! This file demonstrates the creation, usage, and comparison of `Tag` instances, +//! as well as the usage of its various methods. + +use serde_yml::libyml::tag::{Tag, TagFormatError}; + +pub(crate) fn main() { + // Print a message to indicate the file being executed. + println!("\n❯ Executing examples/libyml/tag_examples.rs"); + + // Example: Creating a new Tag instance + let tag_null = Tag::new(Tag::NULL); + println!( + "\n✅ Created a new Tag instance for NULL: {:?}", + tag_null + ); + + // Example: Creating a Tag instance for a custom tag + let custom_tag = Tag::new("tag:example.org,2024:custom"); + println!( + "\n✅ Created a new Tag instance for custom tag: {:?}", + custom_tag + ); + + // Example: Checking if a Tag starts with a prefix + match custom_tag.starts_with("tag:example.org") { + Ok(true) => { + println!("\n✅ The tag starts with the given prefix.") + } + Ok(false) => println!( + "\n✅ The tag does not start with the given prefix." + ), + Err(TagFormatError) => { + println!("\n✅ Error: The prefix is longer than the tag.") + } + } + + // Example: Comparing a Tag with a &str + let comparison_str = "tag:example.org,2024:custom"; + if custom_tag == comparison_str { + println!("\n✅ The tag is equal to the given string slice."); + } else { + println!( + "\n✅ The tag is not equal to the given string slice." + ); + } + + // Example: Using Deref to access the underlying byte slice + let tag_bytes: &[u8] = &custom_tag; + println!( + "\n✅ The underlying byte slice of the tag: {:?}", + tag_bytes + ); + + // Example: Using the Debug implementation + println!( + "\n✅ Debug representation of the custom tag: {:?}", + custom_tag + ); + + // Example: Using Tag constants + let tag_bool = Tag::new(Tag::BOOL); + println!( + "\n✅ Created a new Tag instance for BOOL: {:?}", + tag_bool + ); + let tag_int = Tag::new(Tag::INT); + println!("\n✅ Created a new Tag instance for INT: {:?}", tag_int); + let tag_float = Tag::new(Tag::FLOAT); + println!( + "\n✅ Created a new Tag instance for FLOAT: {:?}", + tag_float + ); + + // Example: Handling TagFormatError when the prefix is longer than the tag + match custom_tag.starts_with("tag:example.org,2024:custom:extra") { + Ok(_) => println!("\n✅ The tag starts with the given prefix."), + Err(TagFormatError) => { + println!("\n✅ Error: The prefix is longer than the tag.") + } + } + + // Example: Validating a list of YAML tags + let tags = vec![ + Tag::new("tag:example.org,2024:custom1"), + Tag::new("tag:example.org,2024:custom2"), + Tag::new("tag:example.com,2024:other"), + ]; + + for tag in &tags { + if tag.starts_with("tag:example.org").unwrap_or(false) { + println!("\n✅ The tag {:?} starts with the prefix 'tag:example.org'", tag); + } else { + println!("\n✅ The tag {:?} does not start with the prefix 'tag:example.org'", tag); + } + } + + // Example: Comparing tags with different formats + let another_custom_tag = Tag::new("tag:example.org,2024:custom"); + if custom_tag == another_custom_tag { + println!("\n✅ The custom_tag is equal to another_custom_tag."); + } else { + println!( + "\n✅ The custom_tag is not equal to another_custom_tag." + ); + } + + // Example: Filtering tags based on a prefix + let filtered_tags: Vec<&Tag> = tags + .iter() + .filter(|tag| { + tag.starts_with("tag:example.org").unwrap_or(false) + }) + .collect(); + + println!( + "\n✅ Filtered tags that start with 'tag:example.org': {:?}", + filtered_tags + ); + + // Example: Creating a custom function to process tags + fn print_tag_info(tag: &Tag) { + println!("\n📌 Tag info: {:?}", tag); + if tag.starts_with("tag:example.org").unwrap_or(false) { + println!("✅ This tag starts with 'tag:example.org'"); + } else { + println!( + "❌ This tag does not start with 'tag:example.org'" + ); + } + } + + let custom_tag = Tag::new("tag:example.org,2024:custom"); + print_tag_info(&custom_tag); + + // Example: Error handling with invalid tags + let invalid_tag = "invalid:tag"; + match Tag::new(invalid_tag).starts_with("tag:example.org") { + Ok(_) => println!( + "\n✅ The invalid_tag starts with the given prefix." + ), + Err(TagFormatError) => { + println!("\n❌ Error: The prefix is longer than the invalid_tag.") + } + } + + // Example: Real-world scenario - parsing and validating tags from a YAML document + let yaml_tags = vec![ + "tag:example.org,2024:custom1", + "tag:example.org,2024:custom2", + "tag:example.com,2024:other", + "invalid:tag", + ]; + + for yaml_tag in yaml_tags { + let tag = Tag::new(yaml_tag); + match tag.starts_with("tag:example.org") { + Ok(true) => println!("\n✅ The tag {:?} is valid and starts with 'tag:example.org'", tag), + Ok(false) => println!("\n✅ The tag {:?} is valid but does not start with 'tag:example.org'", tag), + Err(TagFormatError) => println!("\n❌ The tag {:?} is invalid or the prefix is too long", tag), + } + } +} diff --git a/examples/libyml/util_examples.rs b/examples/libyml/util_examples.rs new file mode 100644 index 00000000..d3c86eda --- /dev/null +++ b/examples/libyml/util_examples.rs @@ -0,0 +1,69 @@ +//! Examples for the `Owned` and `InitPtr` structs and their methods in the `util` module. +//! +//! This file demonstrates the creation, usage, and safety considerations of `Owned` and `InitPtr` instances, +//! as well as the usage of their various methods. + +use serde_yml::libyml::util::{InitPtr, Owned}; +use std::mem::MaybeUninit; +use std::ops::Deref; + +pub(crate) fn main() { + // Print a message to indicate the file being executed. + println!("\n❯ Executing examples/libyml/util_examples.rs"); + + // Example: Creating a new uninitialized Owned instance + let uninit_owned: Owned, i32> = + Owned::new_uninit(); + println!( + "\n✅ Created a new uninitialized Owned instance: {:?}", + uninit_owned + ); + + // Example: Converting an uninitialized Owned instance to an initialized one + let init_owned: Owned = + unsafe { Owned::assume_init(uninit_owned) }; + println!( + "\n✅ Converted to an initialized Owned instance: {:?}", + init_owned + ); + + // Example: Dereferencing an Owned instance + let init_ptr = init_owned.deref().ptr; + println!( + "\n✅ Dereferenced the Owned instance to get the InitPtr: {:?}", + init_ptr + ); + + // Example: Creating an InitPtr instance + let mut value: i32 = 42; + let init_ptr = InitPtr { ptr: &mut value }; + println!( + "\n✅ Created an InitPtr instance: {:?} with value: {}", + init_ptr, + unsafe { *init_ptr.ptr } + ); + + // Example: Using the Drop implementation + { + let drop_owned: Owned, i32> = + Owned::new_uninit(); + println!( + "\n✅ Created a new Owned instance to be dropped: {:?}", + drop_owned + ); + } // drop_owned goes out of scope here, and memory is deallocated. + + // Example: Creating Owned instances with different types + let uninit_owned_f64: Owned, f64> = + Owned::new_uninit(); + println!( + "\n✅ Created a new uninitialized Owned instance: {:?}", + uninit_owned_f64 + ); + let init_owned_f64: Owned = + unsafe { Owned::assume_init(uninit_owned_f64) }; + println!( + "\n✅ Converted to an initialized Owned instance: {:?}", + init_owned_f64 + ); +} diff --git a/examples/modules/mod.rs b/examples/modules/mod.rs new file mode 100644 index 00000000..3326d899 --- /dev/null +++ b/examples/modules/mod.rs @@ -0,0 +1,8 @@ +/// This module contains the `path` example. +pub(crate) mod path_examples; + +/// The main function that runs all the example modules. +pub(crate) fn main() { + // Run the example module `path`. + path_examples::main(); +} diff --git a/examples/modules/path_examples.rs b/examples/modules/path_examples.rs new file mode 100644 index 00000000..dd4fa0a6 --- /dev/null +++ b/examples/modules/path_examples.rs @@ -0,0 +1,230 @@ +//! Examples for the `Path` enum and its usage in the `path` module. +//! +//! This file demonstrates the creation, usage, and formatting of `Path` instances, +//! as well as handling various path scenarios. + +use serde_json::to_string; +use serde_yml::modules::path::Path; + +pub(crate) fn main() { + // Print a message to indicate the file being executed. + println!("\n❯ Executing examples/libyml/path_examples.rs"); + + // Example: Creating a Path::Root instance + let path_root = Path::Root; + println!("\n✅ Created a Path::Root instance: {}", path_root); // Output: . + + // Example: Creating a Path::Seq instance + let path_seq = Path::Seq { + parent: &path_root, + index: 42, + }; + println!("\n✅ Created a Path::Seq instance: {}", path_seq); // Output: [42] + + // Example: Creating a Path::Map instance + let path_map = Path::Map { + parent: &path_root, + key: "key", + }; + println!("\n✅ Created a Path::Map instance: {}", path_map); // Output: key + + // Example: Creating a Path::Alias instance + let path_alias = Path::Alias { parent: &path_root }; + println!("\n✅ Created a Path::Alias instance: {}", path_alias); // Output: (empty string) + + // Example: Creating a Path::Unknown instance + let path_unknown = Path::Unknown { parent: &path_root }; + println!("\n✅ Created a Path::Unknown instance: {}", path_unknown); // Output: ? + + // Example: Nested paths + let path_nested = Path::Unknown { + parent: &Path::Alias { + parent: &Path::Map { + parent: &Path::Seq { + parent: &path_root, + index: 0, + }, + key: "key", + }, + }, + }; + println!("\n✅ Created a nested Path instance: {}", path_nested); // Output: [0].key..? + + // Example: Deeply nested paths + let path_deeply_nested = Path::Unknown { + parent: &Path::Alias { + parent: &Path::Map { + parent: &Path::Seq { + parent: &Path::Map { + parent: &Path::Seq { + parent: &path_root, + index: 1, + }, + key: "first", + }, + index: 2, + }, + key: "second", + }, + }, + }; + println!( + "\n✅ Created a deeply nested Path instance: {}", + path_deeply_nested + ); // Output: [1].first[2].second..? + + // Example: Path with an empty key in Path::Map + let path_map_empty_key = Path::Map { + parent: &path_root, + key: "", + }; + println!( + "\n✅ Created a Path::Map instance with an empty key: {}", + path_map_empty_key + ); // Output: (empty string) + + // Example: Path with maximum index in Path::Seq + let path_seq_max_index = Path::Seq { + parent: &path_root, + index: usize::MAX, + }; + println!( + "\n✅ Created a Path::Seq instance with max index: {}", + path_seq_max_index + ); // Output: [18446744073709551615] + + // Example: Complex nested paths + let path_complex_nested = Path::Unknown { + parent: &Path::Alias { + parent: &Path::Map { + parent: &Path::Seq { + parent: &Path::Map { + parent: &Path::Seq { + parent: &Path::Map { + parent: &path_root, + key: "third", + }, + index: 3, + }, + key: "second", + }, + index: 2, + }, + key: "first", + }, + }, + }; + println!( + "\n✅ Created a complex nested Path instance: {}", + path_complex_nested + ); // Output: [2].first[3].second.third..? + + // Example: Path with multiple unknowns + let path_multiple_unknowns = Path::Unknown { + parent: &Path::Unknown { + parent: &Path::Unknown { parent: &path_root }, + }, + }; + println!( + "\n✅ Created a Path instance with multiple unknowns: {}", + path_multiple_unknowns + ); // Output: .?.?.? + + // Example: Path with multiple aliases + let path_multiple_aliases = Path::Alias { + parent: &Path::Alias { + parent: &Path::Alias { parent: &path_root }, + }, + }; + println!( + "\n✅ Created a Path instance with multiple aliases: {}", + path_multiple_aliases + ); // Output: .. + + // Example: Path with multiple sequences + let path_multiple_sequences = Path::Seq { + parent: &Path::Seq { + parent: &Path::Seq { + parent: &path_root, + index: 1, + }, + index: 2, + }, + index: 3, + }; + println!( + "\n✅ Created a Path instance with multiple sequences: {}", + path_multiple_sequences + ); // Output: \[1\].\[2\].\[3\] + + // Example: Path with multiple maps + let path_multiple_maps = Path::Map { + parent: &Path::Map { + parent: &Path::Map { + parent: &path_root, + key: "first", + }, + key: "second", + }, + key: "third", + }; + println!( + "\n✅ Created a Path instance with multiple maps: {}", + path_multiple_maps + ); // Output: first.second.third + + // Example: Path with multiple aliases, sequences, and maps + let path_multiple_nested = Path::Alias { + parent: &Path::Seq { + parent: &Path::Map { + parent: &Path::Alias { parent: &path_root }, + key: "first", + }, + index: 2, + }, + }; + println!( + "\n✅ Created a Path instance with multiple nested paths: {}", + path_multiple_nested + ); // Output: .first.\[2\]. + + // Example: Path with multiple unknowns, aliases, sequences, and maps + let path_multiple_complex = Path::Unknown { + parent: &Path::Alias { + parent: &Path::Seq { + parent: &Path::Map { + parent: &Path::Unknown { parent: &path_root }, + key: "first", + }, + index: 2, + }, + }, + }; + println!( + "\n✅ Created a Path instance with multiple complex paths: {}", + path_multiple_complex + ); // Output: ?.first.\[2\]..? + + // Example: Serializing and deserializing Path instances + let path_to_serialize = Path::Seq { + parent: &path_root, + index: 42, + }; + + let serialized = to_string(&path_to_serialize).unwrap(); + println!("\n✅ Serialized Path::Seq instance: {}", serialized); + + // Example: Comparing Path instances + let another_path_seq = Path::Seq { + parent: &path_root, + index: 42, + }; + if path_seq == another_path_seq { + println!("\n✅ The path_seq is equal to another_path_seq."); + } else { + println!("\n❌ The path_seq is not equal to another_path_seq."); + } + + // Example: Debug representation of Path instances + println!("\n✅ Debug representation of path_seq: {:?}", path_seq); +} diff --git a/examples/value/de_examples.rs b/examples/value/de_examples.rs new file mode 100644 index 00000000..7c5a10b5 --- /dev/null +++ b/examples/value/de_examples.rs @@ -0,0 +1,115 @@ +//! This file demonstrates the deserialization of various data structures such as empty tuples, +//! empty tuple structs, newtype variants, sequences, maps, option types, and enums with multiple variants using `serde_yml`. + +use serde::Deserialize; +use serde_yml::Value; + +pub(crate) fn main() { + // Print a message to indicate the file being executed. + println!("\n❯ Executing examples/de_examples.rs"); + + // Example: Deserializing an empty tuple struct. + fn example_deserialize_empty_tuple_struct() { + let yaml_str = "---"; + let value: Value = serde_yml::from_str(yaml_str).unwrap(); + + #[derive(Deserialize, PartialEq, Debug)] + struct Empty; + + let result: Empty = serde_yml::from_value(value).unwrap(); + println!("\n✅ Deserialized Empty tuple struct: {:?}", result); + } + + // Example: Deserializing an empty tuple. + fn example_deserialize_empty_tuple() { + let yaml_str = "---"; + let value: Value = serde_yml::from_str(yaml_str).unwrap(); + + let result: () = serde_yml::from_value(value).unwrap(); + println!("\n✅ Deserialized Empty tuple: {:?}", result); + } + + // Example: Deserializing a newtype variant. + fn example_deserialize_newtype_variant() { + let yaml_str = "!Variant 0"; + let value: Value = serde_yml::from_str(yaml_str).unwrap(); + + #[derive(Deserialize, PartialEq, Debug)] + enum E { + Variant(i32), + } + + let result: E = serde_yml::from_value(value).unwrap(); + println!("\n✅ Deserialized newtype variant: {:?}", result); + } + + // Example: Deserializing a struct with multiple fields. + fn example_deserialize_struct_with_fields() { + let yaml_str = " +name: \"John Doe\" +age: 30 +"; + let value: Value = serde_yml::from_str(yaml_str).unwrap(); + + #[derive(Deserialize, PartialEq, Debug)] + struct Person { + name: String, + age: i32, + } + + let result: Person = serde_yml::from_value(value).unwrap(); + println!("\n✅ Deserialized struct with fields: {:?}", result); + } + + // Example: Deserializing a sequence (Vec). + fn example_deserialize_sequence() { + let yaml_str = " +- 1 +- 2 +- 3 +"; + let value: Value = serde_yml::from_str(yaml_str).unwrap(); + + let result: Vec = serde_yml::from_value(value).unwrap(); + println!("\n✅ Deserialized sequence: {:?}", result); + } + + // Example: Deserializing a map (HashMap). + fn example_deserialize_map() { + use std::collections::HashMap; + let yaml_str = " +key1: value1 +key2: value2 +"; + let value: Value = serde_yml::from_str(yaml_str).unwrap(); + + let result: HashMap = + serde_yml::from_value(value).unwrap(); + println!("\n✅ Deserialized map: {:?}", result); + } + + // Example: Deserializing an option type. + fn example_deserialize_option() { + let yaml_str_some = "some_value"; + let value_some: Value = + serde_yml::from_str(yaml_str_some).unwrap(); + let result_some: Option = + serde_yml::from_value(value_some).unwrap(); + println!("\n✅ Deserialized option (Some): {:?}", result_some); + + let yaml_str_none = "---"; + let value_none: Value = + serde_yml::from_str(yaml_str_none).unwrap(); + let result_none: Option = + serde_yml::from_value(value_none).unwrap(); + println!("\n✅ Deserialized option (None): {:?}", result_none); + } + // Execute the examples + example_deserialize_empty_tuple_struct(); + example_deserialize_empty_tuple(); + example_deserialize_newtype_variant(); + example_deserialize_struct_with_fields(); + example_deserialize_sequence(); + example_deserialize_map(); + example_deserialize_option(); +} diff --git a/examples/value/index_examples.rs b/examples/value/index_examples.rs new file mode 100644 index 00000000..b866ec39 --- /dev/null +++ b/examples/value/index_examples.rs @@ -0,0 +1,222 @@ +//! Examples for the `Index` trait and its implementations in the `index` module. +//! +//! This file demonstrates the usage of the `Index` trait with various implementations, +//! including indexing into sequences and mappings, and handling out-of-bounds and invalid indices. + +use serde_yml::value::Index; +use serde_yml::Value; + +pub(crate) fn main() { + // Print a message to indicate the file being executed. + println!("\n❯ Executing examples/libyml/index_examples.rs"); + + // Example: Indexing into a sequence using usize + let sequence = Value::Sequence(vec![ + Value::Number(1.into()), + Value::Number(2.into()), + ]); + let index = 1; + match index.index_into(&sequence) { + Some(value) => { + println!("\n✅ Indexed into sequence: {:?}", value) + } + None => println!("\n❌ Index out of bounds"), + } + + // Example: Indexing into a mapping using usize + let mut mapping = serde_yml::Mapping::new(); + mapping + .insert(Value::Number(1.into()), Value::String("one".into())); + let value = Value::Mapping(mapping); + match index.index_into(&value) { + Some(value) => { + println!("\n✅ Indexed into mapping: {:?}", value) + } + None => println!("\n❌ Key not found"), + } + + // Example: Indexing into a sequence with out-of-bounds index using usize + let index = 3; + match index.index_into(&sequence) { + Some(value) => { + println!("\n✅ Indexed into sequence: {:?}", value) + } + None => println!("\n❌ Index out of bounds"), + } + + // Example: Indexing into a mapping with a non-numeric key using usize + let mut mapping = serde_yml::Mapping::new(); + mapping.insert( + Value::String("key".into()), + Value::String("value".into()), + ); + let value = Value::Mapping(mapping); + match index.index_into(&value) { + Some(value) => { + println!("\n✅ Indexed into mapping: {:?}", value) + } + None => println!("\n❌ Key not found"), + } + + // Example: Mutably indexing into a sequence using usize + let mut sequence = Value::Sequence(vec![ + Value::Number(1.into()), + Value::Number(2.into()), + ]); + let index = 1; + if let Some(value) = index.index_into_mut(&mut sequence) { + *value = Value::Number(3.into()); + println!("\n✅ Mutably indexed into sequence: {:?}", sequence); + } + + // Example: Mutably indexing into a mapping using usize + let mut mapping = serde_yml::Mapping::new(); + mapping + .insert(Value::Number(1.into()), Value::String("one".into())); + let mut value = Value::Mapping(mapping); + if let Some(value) = index.index_into_mut(&mut value) { + *value = Value::String("two".into()); + println!("\n✅ Mutably indexed into mapping: {:?}", value); + } + + // Example: Mutably indexing into a sequence with out-of-bounds index using usize + let index = 3; + if let Some(value) = index.index_into_mut(&mut sequence) { + *value = Value::Number(4.into()); + } else { + println!("\n❌ Index out of bounds"); + } + + // Example: Using index_or_insert with a sequence using usize + let mut sequence = Value::Sequence(vec![Value::Number(1.into())]); + let index = 1; + if index >= sequence.as_sequence().unwrap().len() { + for _ in sequence.as_sequence().unwrap().len()..=index { + sequence.as_sequence_mut().unwrap().push(Value::Null); + } + } + index + .index_or_insert(&mut sequence) + .clone_from(&Value::Number(2.into())); + println!("\n✅ Used index_or_insert with sequence: {:?}", sequence); + + // Example: Using index_or_insert with a mapping using usize + let mapping = serde_yml::Mapping::new(); + let mut value = Value::Mapping(mapping); + index + .index_or_insert(&mut value) + .clone_from(&Value::String("one".into())); + let mut expected_mapping = serde_yml::Mapping::new(); + expected_mapping + .insert(Value::Number(1.into()), Value::String("one".into())); + println!("\n✅ Used index_or_insert with mapping: {:?}", value); + + // Example: Indexing into a non-indexable value + let value = Value::String("hello".into()); + match index.index_into(&value) { + Some(value) => println!("\n✅ Indexed into value: {:?}", value), + None => println!("\n❌ Cannot index into non-indexable value"), + } + + // Example: Mutably indexing into a non-indexable value + let mut value = Value::String("hello".into()); + if let Some(value) = index.index_into_mut(&mut value) { + *value = Value::String("world".into()); + println!("\n✅ Mutably indexed into value: {:?}", value); + } else { + println!("\n❌ Cannot index into non-indexable value"); + } + + // Example: Using index_or_insert with a non-indexable value + let value = Value::String("hello".into()); + let result = std::panic::catch_unwind(move || { + let mut value_owned = value.clone(); + index.index_or_insert(&mut value_owned); + }); + match result { + Ok(_) => println!("\n❌ Should have panicked"), + Err(_) => { + println!("\n✅ Correctly panicked for non-indexable value") + } + } + + // Example: Using index_or_insert with a null value + let value = Value::Null; + let result = std::panic::catch_unwind(move || { + let mut value_owned = value.clone(); + index.index_or_insert(&mut value_owned); + }); + match result { + Ok(_) => println!("\n❌ Should have panicked"), + Err(_) => println!("\n✅ Correctly panicked for null value"), + } + + // Example: Indexing into a mapping using &str + let mut mapping = serde_yml::Mapping::new(); + mapping.insert( + Value::String("key".into()), + Value::String("value".into()), + ); + let value = Value::Mapping(mapping); + let index = "key"; + match index.index_into(&value) { + Some(value) => { + println!("\n✅ Indexed into mapping with &str: {:?}", value) + } + None => println!("\n❌ Key not found"), + } + + // Example: Mutably indexing into a mapping using &str + let mut mapping = serde_yml::Mapping::new(); + mapping.insert( + Value::String("key".into()), + Value::String("value".into()), + ); + let mut value = Value::Mapping(mapping); + let index = "key"; + if let Some(value) = index.index_into_mut(&mut value) { + *value = Value::String("new_value".into()); + println!( + "\n✅ Mutably indexed into mapping with &str: {:?}", + value + ); + } + + // Example: Using index_or_insert with a mapping using &str + let mut mapping = serde_yml::Mapping::new(); + mapping.insert( + Value::String("key".into()), + Value::String("value".into()), + ); + let mut value = Value::Mapping(mapping); + let index = "new_key"; + index + .index_or_insert(&mut value) + .clone_from(&Value::String("new_value".into())); + println!( + "\n✅ Used index_or_insert with mapping and &str: {:?}", + value + ); + + // Example: Indexing into a nested mapping + let mut nested_mapping = serde_yml::Mapping::new(); + nested_mapping.insert( + Value::String("inner_key".into()), + Value::String("inner_value".into()), + ); + let mut outer_mapping = serde_yml::Mapping::new(); + outer_mapping.insert( + Value::String("outer_key".into()), + Value::Mapping(nested_mapping), + ); + let value = Value::Mapping(outer_mapping); + let index = Value::String("outer_key".into()); + if let Some(inner_value) = index + .index_into(&value) + .and_then(|v| "inner_key".index_into(v)) + { + println!("\n✅ Indexed into nested mapping: {:?}", inner_value); + } else { + println!("\n❌ Key not found in nested mapping"); + } +} diff --git a/examples/value/mod.rs b/examples/value/mod.rs new file mode 100644 index 00000000..780763b1 --- /dev/null +++ b/examples/value/mod.rs @@ -0,0 +1,14 @@ +/// This module contains the `de` examples. +pub(crate) mod de_examples; + +/// This module contains the `index` examples. +pub(crate) mod index_examples; + +/// The main function that runs all the example modules. +pub(crate) fn main() { + // Run the example module `de_examples`. + de_examples::main(); + + // Run the example module `index_examples`. + index_examples::main(); +} diff --git a/src/lib.rs b/src/lib.rs index c6aabebb..9d1ec43c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -45,7 +45,7 @@ //! //! ```toml //! [dependencies] -//! serde_yml = "0.0.9" +//! serde_yml = "0.0.10" //! ``` //! //! ## Usage diff --git a/src/libyml/emitter.rs b/src/libyml/emitter.rs index c5b95c24..6057909f 100644 --- a/src/libyml/emitter.rs +++ b/src/libyml/emitter.rs @@ -28,9 +28,9 @@ use std::{ /// Errors that can occur during YAML emission. #[derive(Debug)] -pub(crate) enum Error { +pub enum Error { /// Errors related to libyml. - Libyaml(libyml::error::Error), + Libyml(libyml::error::Error), /// I/O errors. Io(io::Error), } @@ -143,7 +143,7 @@ pub struct Mapping { impl<'a> Emitter<'a> { /// Creates a new YAML emitter. - pub(crate) fn new(write: Box) -> Emitter<'a> { + pub fn new(write: Box) -> Emitter<'a> { let owned = Owned::>::new_uninit(); let pin = unsafe { let emitter = addr_of_mut!((*owned.ptr).sys); @@ -168,10 +168,7 @@ impl<'a> Emitter<'a> { } /// Emits a YAML event. - pub(crate) fn emit( - &mut self, - event: Event<'_>, - ) -> Result<(), Error> { + pub fn emit(&mut self, event: Event<'_>) -> Result<(), Error> { let mut sys_event = MaybeUninit::::uninit(); let sys_event = sys_event.as_mut_ptr(); unsafe { @@ -285,7 +282,7 @@ impl<'a> Emitter<'a> { } }; if initialize_status.fail { - return Err(Error::Libyaml(libyml::Error::emit_error( + return Err(Error::Libyml(libyml::Error::emit_error( emitter, ))); } @@ -297,7 +294,7 @@ impl<'a> Emitter<'a> { } /// Flushes the YAML emitter. - pub(crate) fn flush(&mut self) -> Result<(), Error> { + pub fn flush(&mut self) -> Result<(), Error> { unsafe { let emitter = addr_of_mut!((*self.pin.ptr).sys); if yaml_emitter_flush(emitter).fail { @@ -309,18 +306,18 @@ impl<'a> Emitter<'a> { /// Retrieves the inner writer from the YAML emitter. #[allow(unused_mut)] - pub(crate) fn into_inner(mut self) -> Box { + pub fn into_inner(mut self) -> Box { let sink = Box::new(io::sink()); unsafe { mem::replace(&mut (*self.pin.ptr).write, sink) } } /// Retrieves the error from the YAML emitter. - fn error(&mut self) -> Error { + pub fn error(&mut self) -> Error { let emitter = unsafe { &mut *self.pin.ptr }; if let Some(write_error) = emitter.write_error.take() { Error::Io(write_error) } else { - Error::Libyaml(unsafe { + Error::Libyml(unsafe { libyml::Error::emit_error(&emitter.sys) }) } diff --git a/src/libyml/error.rs b/src/libyml/error.rs index c2621d66..f85f909a 100644 --- a/src/libyml/error.rs +++ b/src/libyml/error.rs @@ -17,30 +17,30 @@ pub struct Error { /// /// This field uses the `yaml_error_type_t` type from the `libyml` crate, /// which represents different types of errors. - kind: sys::YamlErrorTypeT, + pub kind: sys::YamlErrorTypeT, /// A null-terminated string describing the problem that caused the error. /// /// The `CStr<'static>` type represents a borrowed C-style string with a static lifetime. - problem: CStr<'static>, + pub problem: CStr<'static>, /// The offset of the problem that caused the error. - problem_offset: u64, + pub problem_offset: u64, /// The mark indicating the position of the problem that caused the error. /// /// The `Mark` type represents a position in the YAML input. - problem_mark: Mark, + pub problem_mark: Mark, /// An optional null-terminated string providing additional context for the error. /// /// The `CStr<'static>` type represents a borrowed C-style string with a static lifetime. - context: Option>, + pub context: Option>, /// The mark indicating the position of the context related to the error. /// /// The `Mark` type represents a position in the YAML input. - context_mark: Mark, + pub context_mark: Mark, } impl Error { @@ -191,7 +191,7 @@ pub struct Mark { /// /// This field is marked as `pub(super)`, which means it is accessible within the current module /// and its parent module, but not from outside the crate. - pub(super) sys: sys::YamlMarkT, + pub sys: sys::YamlMarkT, } impl Mark { diff --git a/src/libyml/parser.rs b/src/libyml/parser.rs index 940cd935..222ebf9a 100644 --- a/src/libyml/parser.rs +++ b/src/libyml/parser.rs @@ -35,7 +35,7 @@ pub struct Parser<'input> { /// /// Pinning is used to ensure that the `Parser` remains at a fixed memory /// location, which is required for safe interaction with the `libyml` library. - pin: Owned>, + pub pin: Owned>, } /// Represents a pinned parser for YAML deserialization. @@ -44,13 +44,13 @@ pub struct Parser<'input> { /// for parsing YAML documents. It is pinned to a specific lifetime `'input` /// to ensure that the borrowed input data remains valid throughout the /// lifetime of the parser. -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct ParserPinned<'input> { /// The underlying `YamlParserT` struct from the `libyml` library. - sys: sys::YamlParserT, + pub sys: sys::YamlParserT, /// The input data being parsed. - input: Cow<'input, [u8]>, + pub input: Cow<'input, [u8]>, } /// Represents a YAML event encountered during parsing. @@ -180,6 +180,30 @@ impl<'input> Parser<'input> { if sys::yaml_parser_parse(parser, event).fail { return Err(Error::parse_error(parser)); } + + let event_type = (*event).type_; + + // Handle specific cases + if event_type == sys::YamlNoEvent + || event_type == sys::YamlStreamEndEvent + { + let mark = Mark { + sys: (*event).start_mark, + }; + sys::yaml_event_delete(event); + return Ok((Event::StreamEnd, mark)); + } + + if event_type == sys::YamlScalarEvent + && (*event).data.scalar.value.is_null() + { + let mark = Mark { + sys: (*event).start_mark, + }; + sys::yaml_event_delete(event); + return Ok((Event::StreamEnd, mark)); + } + let ret = convert_event(&*event, &(*self.pin.ptr).input); let mark = Mark { sys: (*event).start_mark, @@ -188,8 +212,28 @@ impl<'input> Parser<'input> { Ok((ret, mark)) } } + /// Checks if the parser is initialized and ready to parse YAML. + /// + /// This function returns `true` if the parser is initialized and ready to parse YAML, and `false` otherwise. + pub fn is_ok(&self) -> bool { + unsafe { + let parser = addr_of_mut!((*self.pin.ptr).sys); + if sys::yaml_parser_initialize(parser).fail { + return false; + } + sys::yaml_parser_set_encoding( + parser, + sys::YamlUtf8Encoding, + ); + let input_ptr = (*self.pin.ptr).input.as_ptr(); + let input_len = (*self.pin.ptr).input.len() as u64; + sys::yaml_parser_set_input_string( + parser, input_ptr, input_len, + ); + true + } + } } - unsafe fn convert_event<'input>( sys: &sys::YamlEventT, input: &'input Cow<'input, [u8]>, diff --git a/src/libyml/util.rs b/src/libyml/util.rs index 04936550..0a31dbe1 100644 --- a/src/libyml/util.rs +++ b/src/libyml/util.rs @@ -8,7 +8,7 @@ use std::{ /// A struct representing ownership of a pointer to a value of type `T`. /// `Init` represents the initialization state of the value. #[derive(Debug)] -pub(crate) struct Owned { +pub struct Owned { ptr: NonNull, marker: PhantomData>, } @@ -19,7 +19,7 @@ impl Owned { /// # Safety /// The created instance contains uninitialized memory, and should be properly /// initialized before use. - pub(crate) fn new_uninit() -> Owned, T> { + pub fn new_uninit() -> Owned, T> { // Allocate memory for `T` but leave it uninitialized. let boxed = Box::new(MaybeUninit::::uninit()); Owned { @@ -35,7 +35,7 @@ impl Owned { /// /// # Safety /// The caller must ensure that `definitely_init` is properly initialized. - pub(crate) unsafe fn assume_init( + pub unsafe fn assume_init( definitely_init: Owned, T>, ) -> Owned { let ptr = definitely_init.ptr; @@ -49,8 +49,10 @@ impl Owned { /// A transparent wrapper around a mutable pointer of type `T`. #[repr(transparent)] -pub(crate) struct InitPtr { - pub(crate) ptr: *mut T, +#[derive(Debug)] +pub struct InitPtr { + /// The mutable pointer. + pub ptr: *mut T, } impl Deref for Owned { diff --git a/src/modules/error.rs b/src/modules/error.rs index 4e0abc86..d6aa1bbd 100644 --- a/src/modules/error.rs +++ b/src/modules/error.rs @@ -76,7 +76,7 @@ pub enum ErrorImpl { /// A generic error message with an optional position. Message(String, Option), /// An error originating from the `libyml` library. - Libyaml(libyml::Error), + Libyml(libyml::Error), /// An I/O error. IoError(io::Error), /// An error encountered while converting a byte slice to a string using UTF-8 encoding. @@ -115,7 +115,7 @@ impl Display for ErrorImpl { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { ErrorImpl::Message(msg, _) => write!(f, "Error: {}", msg), - ErrorImpl::Libyaml(_) => write!(f, "Error: An error occurred in the Libyaml library"), + ErrorImpl::Libyml(_) => write!(f, "Error: An error occurred in the Libyml library"), ErrorImpl::IoError(err) => write!(f, "I/O Error: {}", err), ErrorImpl::FromUtf8(err) => write!(f, "UTF-8 Conversion Error: {}", err), ErrorImpl::EndOfStream => write!(f, "Unexpected End of YAML Stream: The YAML stream ended unexpectedly while parsing a value"), @@ -191,14 +191,14 @@ pub fn fix_mark( impl From for Error { fn from(err: libyml::Error) -> Self { - Error(Box::new(ErrorImpl::Libyaml(err))) + Error(Box::new(ErrorImpl::Libyml(err))) } } impl From for Error { fn from(err: emitter::Error) -> Self { match err { - emitter::Error::Libyaml(err) => Self::from(err), + emitter::Error::Libyml(err) => Self::from(err), emitter::Error::Io(err) => new(ErrorImpl::IoError(err)), } } @@ -255,7 +255,7 @@ impl ErrorImpl { ErrorImpl::Message(_, Some(Pos { mark, path: _ })) | ErrorImpl::RecursionLimitExceeded(mark) | ErrorImpl::UnknownAnchor(mark) => Some(*mark), - ErrorImpl::Libyaml(err) => Some(err.mark()), + ErrorImpl::Libyml(err) => Some(err.mark()), ErrorImpl::Shared(err) => err.mark(), _ => None, } @@ -270,7 +270,7 @@ impl ErrorImpl { } f.write_str(description) } - ErrorImpl::Libyaml(_) => unreachable!(), + ErrorImpl::Libyml(_) => unreachable!(), ErrorImpl::IoError(err) => Display::fmt(err, f), ErrorImpl::FromUtf8(err) => Display::fmt(err, f), ErrorImpl::EndOfStream => f.write_str("EOF while parsing a value"), @@ -312,7 +312,7 @@ impl ErrorImpl { fn display(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - ErrorImpl::Libyaml(err) => Display::fmt(err, f), + ErrorImpl::Libyml(err) => Display::fmt(err, f), ErrorImpl::Shared(err) => err.display(f), _ => { self.message(f)?; @@ -328,7 +328,7 @@ impl ErrorImpl { fn debug(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - ErrorImpl::Libyaml(err) => Debug::fmt(err, f), + ErrorImpl::Libyml(err) => Debug::fmt(err, f), ErrorImpl::Shared(err) => err.debug(f), _ => { f.write_str("Error(")?; diff --git a/src/modules/path.rs b/src/modules/path.rs index ad285a0c..2899d56e 100644 --- a/src/modules/path.rs +++ b/src/modules/path.rs @@ -1,3 +1,4 @@ +use serde::Serialize; use std::fmt::{self, Display}; /// `Path` represents the path to the current value in the input, like `dependencies.serde.typo1`. @@ -12,7 +13,7 @@ use std::fmt::{self, Display}; /// - `Map`: Represents a map (object) path with a reference to the parent path and a key. /// - `Alias`: Represents an alias path with a reference to the parent path. /// - `Unknown`: Represents an unknown path with a reference to the parent path. -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, PartialEq, Serialize)] pub enum Path<'a> { /// Represents the root path. Root, diff --git a/src/value/de.rs b/src/value/de.rs index 2c4d2ad0..a61ffb64 100644 --- a/src/value/de.rs +++ b/src/value/de.rs @@ -14,6 +14,7 @@ use std::fmt::Result as FmtResult; use std::slice; use std::vec; +/// Deserializes a `Value` from a given deserializer. impl<'de> Deserialize<'de> for Value { fn deserialize(deserializer: D) -> Result where @@ -31,46 +32,46 @@ impl<'de> Deserialize<'de> for Value { formatter.write_str("any YAML value") } - fn visit_bool(self, b: bool) -> Result + fn visit_bool(self, value: bool) -> Result where E: de::Error, { - Ok(Value::Bool(b)) + Ok(Value::Bool(value)) } - fn visit_i64(self, i: i64) -> Result + fn visit_i64(self, value: i64) -> Result where E: de::Error, { - Ok(Value::Number(i.into())) + Ok(Value::Number(value.into())) } - fn visit_u64(self, u: u64) -> Result + fn visit_u64(self, value: u64) -> Result where E: de::Error, { - Ok(Value::Number(u.into())) + Ok(Value::Number(value.into())) } - fn visit_f64(self, f: f64) -> Result + fn visit_f64(self, value: f64) -> Result where E: de::Error, { - Ok(Value::Number(f.into())) + Ok(Value::Number(value.into())) } - fn visit_str(self, s: &str) -> Result + fn visit_str(self, value: &str) -> Result where E: de::Error, { - Ok(Value::String(s.to_owned())) + Ok(Value::String(value.to_owned())) } - fn visit_string(self, s: String) -> Result + fn visit_string(self, value: String) -> Result where E: de::Error, { - Ok(Value::String(s)) + Ok(Value::String(value)) } fn visit_unit(self) -> Result @@ -97,20 +98,20 @@ impl<'de> Deserialize<'de> for Value { Deserialize::deserialize(deserializer) } - fn visit_seq(self, data: A) -> Result + fn visit_seq(self, seq: A) -> Result where A: SeqAccess<'de>, { - let de = SeqAccessDeserializer::new(data); + let de = SeqAccessDeserializer::new(seq); let sequence = Sequence::deserialize(de)?; Ok(Value::Sequence(sequence)) } - fn visit_map(self, data: A) -> Result + fn visit_map(self, map: A) -> Result where A: MapAccess<'de>, { - let de = MapAccessDeserializer::new(data); + let de = MapAccessDeserializer::new(map); let mapping = Mapping::deserialize(de)?; Ok(Value::Mapping(mapping)) } @@ -134,6 +135,7 @@ impl<'de> Deserialize<'de> for Value { } impl Value { + /// Deserializes a number from the given `Value`. fn deserialize_number<'de, V>( &self, visitor: V, @@ -148,6 +150,7 @@ impl Value { } } +/// Visits a `Sequence` value with the given `Visitor`. fn visit_sequence<'de, V>( sequence: Sequence, visitor: V, @@ -166,6 +169,7 @@ where } } +/// Visits a borrowed `Sequence` value with the given `Visitor`. fn visit_sequence_ref<'de, V>( sequence: &'de Sequence, visitor: V, @@ -184,6 +188,7 @@ where } } +/// Visits a `Mapping` value with the given `Visitor`. fn visit_mapping<'de, V>( mapping: Mapping, visitor: V, @@ -202,6 +207,7 @@ where } } +/// Visits a borrowed `Mapping` value with the given `Visitor`. fn visit_mapping_ref<'de, V>( mapping: &'de Mapping, visitor: V, @@ -538,6 +544,7 @@ impl<'de> Deserializer<'de> for Value { } } +/// Represents an enum deserializer. struct EnumDeserializer<'a> { tag: &'a str, value: Option, @@ -561,6 +568,7 @@ impl<'de> EnumAccess<'de> for EnumDeserializer<'_> { } } +/// Represents a variant deserializer. struct VariantDeserializer { value: Option, } @@ -623,11 +631,13 @@ impl<'de> VariantAccess<'de> for VariantDeserializer { } } +/// Represents a sequence deserializer. pub(crate) struct SeqDeserializer { iter: vec::IntoIter, } impl SeqDeserializer { + /// Creates a new `SeqDeserializer` from the given vector of `Value`s. pub(crate) fn new(vec: Vec) -> Self { SeqDeserializer { iter: vec.into_iter(), @@ -705,12 +715,14 @@ impl<'de> SeqAccess<'de> for SeqDeserializer { } } +/// Represents a map deserializer. pub(crate) struct MapDeserializer { iter: ::IntoIter, value: Option, } impl MapDeserializer { + /// Creates a new `MapDeserializer` from the given `Mapping`. pub(crate) fn new(map: Mapping) -> Self { MapDeserializer { iter: map.into_iter(), @@ -984,7 +996,6 @@ impl<'de> Deserializer<'de> for &'de Value { { self.deserialize_unit(visitor) } - fn deserialize_newtype_struct( self, _name: &'static str, @@ -1104,16 +1115,14 @@ impl<'de> Deserializer<'de> for &'de Value { visitor.visit_unit() } } - +/// Represents an enum deserializer for borrowed values. struct EnumRefDeserializer<'de> { tag: &'de str, value: Option<&'de Value>, } - impl<'de> EnumAccess<'de> for EnumRefDeserializer<'de> { type Error = Error; type Variant = VariantRefDeserializer<'de>; - fn variant_seed( self, seed: V, @@ -1127,14 +1136,12 @@ impl<'de> EnumAccess<'de> for EnumRefDeserializer<'de> { Ok((variant, visitor)) } } - +/// Represents a variant deserializer for borrowed values. struct VariantRefDeserializer<'de> { value: Option<&'de Value>, } - impl<'de> VariantAccess<'de> for VariantRefDeserializer<'de> { type Error = Error; - fn unit_variant(self) -> Result<(), Error> { match self.value { Some(value) => { @@ -1194,20 +1201,18 @@ impl<'de> VariantAccess<'de> for VariantRefDeserializer<'de> { } } } - +/// Represents a sequence deserializer for borrowed values. pub(crate) struct SeqRefDeserializer<'de> { iter: slice::Iter<'de, Value>, } - impl<'de> SeqRefDeserializer<'de> { + /// Creates a new SeqRefDeserializer from the given slice of Values. pub(crate) fn new(slice: &'de [Value]) -> Self { SeqRefDeserializer { iter: slice.iter() } } } - impl<'de> Deserializer<'de> for SeqRefDeserializer<'de> { type Error = Error; - #[inline] fn deserialize_any( mut self, @@ -1249,10 +1254,8 @@ impl<'de> Deserializer<'de> for SeqRefDeserializer<'de> { map struct enum identifier } } - impl<'de> SeqAccess<'de> for SeqRefDeserializer<'de> { type Error = Error; - fn next_element_seed( &mut self, seed: T, @@ -1273,13 +1276,13 @@ impl<'de> SeqAccess<'de> for SeqRefDeserializer<'de> { } } } - +/// Represents a map deserializer for borrowed values. pub(crate) struct MapRefDeserializer<'de> { iter: Option<<&'de Mapping as IntoIterator>::IntoIter>, value: Option<&'de Value>, } - impl<'de> MapRefDeserializer<'de> { + /// Creates a new MapRefDeserializer from the given Mapping. pub(crate) fn new(map: &'de Mapping) -> Self { MapRefDeserializer { iter: Some(map.iter()), @@ -1287,10 +1290,8 @@ impl<'de> MapRefDeserializer<'de> { } } } - impl<'de> MapAccess<'de> for MapRefDeserializer<'de> { type Error = Error; - fn next_key_seed( &mut self, seed: T, @@ -1324,10 +1325,8 @@ impl<'de> MapAccess<'de> for MapRefDeserializer<'de> { } } } - impl<'de> Deserializer<'de> for MapRefDeserializer<'de> { type Error = Error; - #[inline] fn deserialize_any(self, visitor: V) -> Result where @@ -1352,8 +1351,8 @@ impl<'de> Deserializer<'de> for MapRefDeserializer<'de> { map struct enum identifier } } - impl Value { + /// Returns an error indicating that the value is of an invalid type for the given visitor. #[cold] fn invalid_type(&self, exp: &dyn Expected) -> E where @@ -1361,7 +1360,7 @@ impl Value { { de::Error::invalid_type(self.unexpected(), exp) } - + /// Returns an `Unexpected` instance for the current `Value`. #[cold] pub(crate) fn unexpected(&self) -> Unexpected<'_> { match self { diff --git a/src/value/index.rs b/src/value/index.rs index fe068880..dd5c1478 100644 --- a/src/value/index.rs +++ b/src/value/index.rs @@ -38,6 +38,7 @@ impl Index for usize { _ => None, } } + fn index_into_mut<'v>( &self, v: &'v mut Value, @@ -50,6 +51,7 @@ impl Index for usize { _ => None, } } + fn index_or_insert<'v>( &self, mut v: &'v mut Value, @@ -147,12 +149,14 @@ impl Index for Value { fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value> { index_into_mapping(self, v) } + fn index_into_mut<'v>( &self, v: &'v mut Value, ) -> Option<&'v mut Value> { index_into_mut_mapping(self, v) } + fn index_or_insert<'v>(&self, v: &'v mut Value) -> &'v mut Value { index_or_insert_mapping(self, v) } @@ -162,12 +166,14 @@ impl Index for str { fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value> { index_into_mapping(self, v) } + fn index_into_mut<'v>( &self, v: &'v mut Value, ) -> Option<&'v mut Value> { index_into_mut_mapping(self, v) } + fn index_or_insert<'v>(&self, v: &'v mut Value) -> &'v mut Value { index_or_insert_mapping(self, v) } @@ -177,12 +183,14 @@ impl Index for String { fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value> { self.as_str().index_into(v) } + fn index_into_mut<'v>( &self, v: &'v mut Value, ) -> Option<&'v mut Value> { self.as_str().index_into_mut(v) } + fn index_or_insert<'v>(&self, v: &'v mut Value) -> &'v mut Value { self.as_str().index_or_insert(v) } @@ -195,12 +203,14 @@ where fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value> { (**self).index_into(v) } + fn index_into_mut<'v>( &self, v: &'v mut Value, ) -> Option<&'v mut Value> { (**self).index_into_mut(v) } + fn index_or_insert<'v>(&self, v: &'v mut Value) -> &'v mut Value { (**self).index_or_insert(v) } @@ -242,83 +252,83 @@ impl fmt::Display for Type<'_> { // `as_sequence`, or match. The value of this impl is that it adds a way of // working with Value that is not well served by the existing approaches: // concise and careless and sometimes that is exactly what you want. + +/// Index into a `serde_yml::Value` using the syntax `value[0]` or `value["k"]`. +/// +/// Returns `Value::Null` if the type of `self` does not match the type of +/// the index, for example if the index is a string and `self` is a sequence +/// or a number. Also returns `Value::Null` if the given key does not exist +/// in the map or the given index is not within the bounds of the sequence. +/// +/// For retrieving deeply nested values, you should have a look at the +/// `Value::pointer` method. +/// +/// # Examples +/// +/// ``` +/// # use serde_yml::Value; +/// # +/// # fn main() -> serde_yml::Result<()> { +/// let data: serde_yml::Value = serde_yml::from_str(r#"{ x: { y: [z, zz] } }"#)?; +/// +/// assert_eq!(data["x"]["y"], serde_yml::from_str::(r#"["z", "zz"]"#).unwrap()); +/// assert_eq!(data["x"]["y"][0], serde_yml::from_str::(r#""z""#).unwrap()); +/// +/// assert_eq!(data["a"], serde_yml::from_str::(r#"null"#).unwrap()); // returns null for undefined values +/// assert_eq!(data["a"]["b"], serde_yml::from_str::(r#"null"#).unwrap()); // does not panic +/// # Ok(()) +/// # } +/// ``` impl ops::Index for Value where I: Index, { type Output = Value; - /// Index into a `serde_yml::Value` using the syntax `value[0]` or - /// `value["k"]`. - /// - /// Returns `Value::Null` if the type of `self` does not match the type of - /// the index, for example if the index is a string and `self` is a sequence - /// or a number. Also returns `Value::Null` if the given key does not exist - /// in the map or the given index is not within the bounds of the sequence. - /// - /// For retrieving deeply nested values, you should have a look at the - /// `Value::pointer` method. - /// - /// # Examples - /// - /// ``` - /// # use serde_yml::Value; - /// # - /// # fn main() -> serde_yml::Result<()> { - /// let data: serde_yml::Value = serde_yml::from_str(r#"{ x: { y: [z, zz] } }"#)?; - /// - /// assert_eq!(data["x"]["y"], serde_yml::from_str::(r#"["z", "zz"]"#).unwrap()); - /// assert_eq!(data["x"]["y"][0], serde_yml::from_str::(r#""z""#).unwrap()); - /// - /// assert_eq!(data["a"], serde_yml::from_str::(r#"null"#).unwrap()); // returns null for undefined values - /// assert_eq!(data["a"]["b"], serde_yml::from_str::(r#"null"#).unwrap()); // does not panic - /// # Ok(()) - /// # } - /// ``` fn index(&self, index: I) -> &Value { static NULL: Value = Value::Null; index.index_into(self).unwrap_or(&NULL) } } +/// Write into a `serde_yml::Value` using the syntax `value[0] = ...` or +/// `value["k"] = ...`. +/// +/// If the index is a number, the value must be a sequence of length bigger +/// than the index. Indexing into a value that is not a sequence or a +/// sequence that is too small will panic. +/// +/// If the index is a string, the value must be an object or null which is +/// treated like an empty object. If the key is not already present in the +/// object, it will be inserted with a value of null. Indexing into a value +/// that is neither an object nor null will panic. +/// +/// # Examples +/// +/// ``` +/// # fn main() -> serde_yml::Result<()> { +/// let mut data: serde_yml::Value = serde_yml::from_str(r#"{x: 0}"#)?; +/// +/// // replace an existing key +/// data["x"] = serde_yml::from_str(r#"1"#)?; +/// +/// // insert a new key +/// data["y"] = serde_yml::from_str(r#"[false, false, false]"#)?; +/// +/// // replace a value in a sequence +/// data["y"][0] = serde_yml::from_str(r#"true"#)?; +/// +/// // inserted a deeply nested key +/// data["a"]["b"]["c"]["d"] = serde_yml::from_str(r#"true"#)?; +/// +/// println!("{:?}", data); +/// # Ok(()) +/// # } +/// ``` impl ops::IndexMut for Value where I: Index, { - /// Write into a `serde_yml::Value` using the syntax `value[0] = ...` or - /// `value["k"] = ...`. - /// - /// If the index is a number, the value must be a sequence of length bigger - /// than the index. Indexing into a value that is not a sequence or a - /// sequence that is too small will panic. - /// - /// If the index is a string, the value must be an object or null which is - /// treated like an empty object. If the key is not already present in the - /// object, it will be inserted with a value of null. Indexing into a value - /// that is neither an object nor null will panic. - /// - /// # Examples - /// - /// ``` - /// # fn main() -> serde_yml::Result<()> { - /// let mut data: serde_yml::Value = serde_yml::from_str(r#"{x: 0}"#)?; - /// - /// // replace an existing key - /// data["x"] = serde_yml::from_str(r#"1"#)?; - /// - /// // insert a new key - /// data["y"] = serde_yml::from_str(r#"[false, false, false]"#)?; - /// - /// // replace a value in a sequence - /// data["y"][0] = serde_yml::from_str(r#"true"#)?; - /// - /// // inserted a deeply nested key - /// data["a"]["b"]["c"]["d"] = serde_yml::from_str(r#"true"#)?; - /// - /// println!("{:?}", data); - /// # Ok(()) - /// # } - /// ``` fn index_mut(&mut self, index: I) -> &mut Value { index.index_or_insert(self) } diff --git a/tests/libyml/mod.rs b/tests/libyml/mod.rs new file mode 100644 index 00000000..59402571 --- /dev/null +++ b/tests/libyml/mod.rs @@ -0,0 +1,17 @@ +/// This module contains the tests for the `emitter` module. +pub mod test_emitter; + +/// This module contains the tests for the `error` module. +pub mod test_error; + +/// This module contains the tests for the `parser` module. +pub mod test_parser; + +/// This module contains the tests for the `test_safe_cstr` module. +pub mod test_safe_cstr; + +/// This module contains the tests for the `test_tag` module. +pub mod test_tag; + +/// This module contains the tests for the `util` module. +pub mod test_util; diff --git a/tests/libyml/test_emitter.rs b/tests/libyml/test_emitter.rs new file mode 100644 index 00000000..1f030b36 --- /dev/null +++ b/tests/libyml/test_emitter.rs @@ -0,0 +1,409 @@ +#[cfg(test)] +mod tests { + use serde_yml::libyml::emitter::{ + Emitter, Event, Mapping, Scalar, ScalarStyle, Sequence, + }; + use std::io::Cursor; + + #[test] + fn test_emitter_stream_start_end() { + let mut buffer = Cursor::new(Vec::with_capacity(100)); + { + let mut emitter = Emitter::new(Box::new(&mut buffer)); + emitter.emit(Event::StreamStart).unwrap(); + emitter.emit(Event::StreamEnd).unwrap(); + } + + let result = + String::from_utf8_lossy(&buffer.into_inner()).to_string(); + assert_eq!(result, ""); + } + + #[test] + fn test_emitter_document_start_end() { + let mut buffer = Cursor::new(Vec::with_capacity(100)); + { + let mut emitter = Emitter::new(Box::new(&mut buffer)); + emitter.emit(Event::StreamStart).unwrap(); + emitter.emit(Event::DocumentStart).unwrap(); + emitter + .emit(Event::Scalar(Scalar { + tag: None, + value: "", + style: ScalarStyle::Plain, + })) + .unwrap(); + emitter.emit(Event::DocumentEnd).unwrap(); + emitter.emit(Event::StreamEnd).unwrap(); + } + + let result = + String::from_utf8_lossy(&buffer.into_inner()).to_string(); + assert_eq!(result, "\n"); + } + + #[test] + fn test_emitter_scalar() { + let mut buffer = Cursor::new(Vec::with_capacity(100)); + { + let mut emitter = Emitter::new(Box::new(&mut buffer)); + emitter.emit(Event::StreamStart).unwrap(); + emitter.emit(Event::DocumentStart).unwrap(); + emitter + .emit(Event::Scalar(Scalar { + tag: None, + value: "hello", + style: ScalarStyle::Plain, + })) + .unwrap(); + emitter.emit(Event::DocumentEnd).unwrap(); + emitter.emit(Event::StreamEnd).unwrap(); + } + buffer.set_position(0); + + let result = + String::from_utf8_lossy(&buffer.into_inner()).to_string(); + assert_eq!(result, "hello\n"); + } + + #[test] + fn test_emitter_sequence() { + let mut buffer = Cursor::new(Vec::with_capacity(100)); + { + let mut emitter = Emitter::new(Box::new(&mut buffer)); + emitter.emit(Event::StreamStart).unwrap(); + emitter.emit(Event::DocumentStart).unwrap(); + emitter + .emit(Event::SequenceStart(Sequence { tag: None })) + .unwrap(); + emitter + .emit(Event::Scalar(Scalar { + tag: None, + value: "item1", + style: ScalarStyle::Plain, + })) + .unwrap(); + emitter + .emit(Event::Scalar(Scalar { + tag: None, + value: "item2", + style: ScalarStyle::Plain, + })) + .unwrap(); + emitter.emit(Event::SequenceEnd).unwrap(); + emitter.emit(Event::DocumentEnd).unwrap(); + emitter.emit(Event::StreamEnd).unwrap(); + } + buffer.set_position(0); + + let result = + String::from_utf8_lossy(&buffer.into_inner()).to_string(); + assert_eq!(result, "- item1\n- item2\n"); + } + + #[test] + fn test_emitter_mapping() { + let mut buffer = Cursor::new(Vec::with_capacity(100)); + { + let mut emitter = Emitter::new(Box::new(&mut buffer)); + emitter.emit(Event::StreamStart).unwrap(); + emitter.emit(Event::DocumentStart).unwrap(); + emitter + .emit(Event::MappingStart(Mapping { tag: None })) + .unwrap(); + emitter + .emit(Event::Scalar(Scalar { + tag: None, + value: "key1", + style: ScalarStyle::Plain, + })) + .unwrap(); + emitter + .emit(Event::Scalar(Scalar { + tag: None, + value: "value1", + style: ScalarStyle::Plain, + })) + .unwrap(); + emitter + .emit(Event::Scalar(Scalar { + tag: None, + value: "key2", + style: ScalarStyle::Plain, + })) + .unwrap(); + emitter + .emit(Event::Scalar(Scalar { + tag: None, + value: "value2", + style: ScalarStyle::Plain, + })) + .unwrap(); + emitter.emit(Event::MappingEnd).unwrap(); + emitter.emit(Event::DocumentEnd).unwrap(); + emitter.emit(Event::StreamEnd).unwrap(); + } + buffer.set_position(0); + + let result = + String::from_utf8_lossy(&buffer.into_inner()).to_string(); + assert_eq!(result, "key1: value1\nkey2: value2\n"); + } + + #[test] + fn test_emitter_flush() { + let mut buffer = Cursor::new(Vec::with_capacity(100)); + { + let mut emitter = Emitter::new(Box::new(&mut buffer)); + emitter.emit(Event::StreamStart).unwrap(); + emitter.emit(Event::DocumentStart).unwrap(); + emitter + .emit(Event::Scalar(Scalar { + tag: None, + value: "hello", + style: ScalarStyle::Plain, + })) + .unwrap(); + emitter.flush().unwrap(); + emitter.emit(Event::DocumentEnd).unwrap(); + emitter.emit(Event::StreamEnd).unwrap(); + } + buffer.set_position(0); + + let result = + String::from_utf8_lossy(&buffer.into_inner()).to_string(); + assert_eq!(result, "hello\n"); + } + + #[test] + fn test_emitter_scalar_with_tag() { + let mut buffer = Cursor::new(Vec::with_capacity(100)); + { + let mut emitter = Emitter::new(Box::new(&mut buffer)); + emitter.emit(Event::StreamStart).unwrap(); + emitter.emit(Event::DocumentStart).unwrap(); + emitter + .emit(Event::Scalar(Scalar { + tag: Some("!mytag".to_string()), + value: "hello", + style: ScalarStyle::Plain, + })) + .unwrap(); + emitter.emit(Event::DocumentEnd).unwrap(); + emitter.emit(Event::StreamEnd).unwrap(); + } + buffer.set_position(0); + + let result = + String::from_utf8_lossy(&buffer.into_inner()).to_string(); + assert_eq!(result, "!mytag hello\n"); + } + + #[test] + fn test_emitter_sequence_with_tag() { + let mut buffer = Cursor::new(Vec::with_capacity(100)); + { + let mut emitter = Emitter::new(Box::new(&mut buffer)); + emitter.emit(Event::StreamStart).unwrap(); + emitter.emit(Event::DocumentStart).unwrap(); + emitter + .emit(Event::SequenceStart(Sequence { + tag: Some("!mytag".to_string()), + })) + .unwrap(); + emitter + .emit(Event::Scalar(Scalar { + tag: None, + value: "item1", + style: ScalarStyle::Plain, + })) + .unwrap(); + emitter + .emit(Event::Scalar(Scalar { + tag: None, + value: "item2", + style: ScalarStyle::Plain, + })) + .unwrap(); + emitter.emit(Event::SequenceEnd).unwrap(); + emitter.emit(Event::DocumentEnd).unwrap(); + emitter.emit(Event::StreamEnd).unwrap(); + } + buffer.set_position(0); + + let result = + String::from_utf8_lossy(&buffer.into_inner()).to_string(); + assert_eq!(result, "!mytag\n- item1\n- item2\n"); + } + + #[test] + fn test_emitter_mapping_with_tag() { + let mut buffer = Cursor::new(Vec::with_capacity(100)); + { + let mut emitter = Emitter::new(Box::new(&mut buffer)); + emitter.emit(Event::StreamStart).unwrap(); + emitter.emit(Event::DocumentStart).unwrap(); + emitter + .emit(Event::MappingStart(Mapping { + tag: Some("!mytag".to_string()), + })) + .unwrap(); + emitter + .emit(Event::Scalar(Scalar { + tag: None, + value: "key1", + style: ScalarStyle::Plain, + })) + .unwrap(); + emitter + .emit(Event::Scalar(Scalar { + tag: None, + value: "value1", + style: ScalarStyle::Plain, + })) + .unwrap(); + emitter + .emit(Event::Scalar(Scalar { + tag: None, + value: "key2", + style: ScalarStyle::Plain, + })) + .unwrap(); + emitter + .emit(Event::Scalar(Scalar { + tag: None, + value: "value2", + style: ScalarStyle::Plain, + })) + .unwrap(); + emitter.emit(Event::MappingEnd).unwrap(); + emitter.emit(Event::DocumentEnd).unwrap(); + emitter.emit(Event::StreamEnd).unwrap(); + } + buffer.set_position(0); + + let result = + String::from_utf8_lossy(&buffer.into_inner()).to_string(); + assert_eq!(result, "!mytag\nkey1: value1\nkey2: value2\n"); + } + + #[test] + fn test_emitter_empty_sequence() { + let mut buffer = Cursor::new(Vec::with_capacity(100)); + { + let mut emitter = Emitter::new(Box::new(&mut buffer)); + emitter.emit(Event::StreamStart).unwrap(); + emitter.emit(Event::DocumentStart).unwrap(); + emitter + .emit(Event::SequenceStart(Sequence { tag: None })) + .unwrap(); + emitter.emit(Event::SequenceEnd).unwrap(); + emitter.emit(Event::DocumentEnd).unwrap(); + emitter.emit(Event::StreamEnd).unwrap(); + } + buffer.set_position(0); + + let result = + String::from_utf8_lossy(&buffer.into_inner()).to_string(); + assert_eq!(result, "[]\n"); + } + + #[test] + fn test_emitter_empty_mapping() { + let mut buffer = Cursor::new(Vec::with_capacity(100)); + { + let mut emitter = Emitter::new(Box::new(&mut buffer)); + emitter.emit(Event::StreamStart).unwrap(); + emitter.emit(Event::DocumentStart).unwrap(); + emitter + .emit(Event::MappingStart(Mapping { tag: None })) + .unwrap(); + emitter.emit(Event::MappingEnd).unwrap(); + emitter.emit(Event::DocumentEnd).unwrap(); + emitter.emit(Event::StreamEnd).unwrap(); + } + buffer.set_position(0); + + let result = + String::from_utf8_lossy(&buffer.into_inner()).to_string(); + assert_eq!(result, "{}\n"); + } + + #[test] + fn test_emitter_nested_sequence() { + let mut buffer = Cursor::new(Vec::with_capacity(100)); + { + let mut emitter = Emitter::new(Box::new(&mut buffer)); + emitter.emit(Event::StreamStart).unwrap(); + emitter.emit(Event::DocumentStart).unwrap(); + emitter + .emit(Event::SequenceStart(Sequence { tag: None })) + .unwrap(); + emitter + .emit(Event::SequenceStart(Sequence { tag: None })) + .unwrap(); + emitter + .emit(Event::Scalar(Scalar { + tag: None, + value: "nested", + style: ScalarStyle::Plain, + })) + .unwrap(); + emitter.emit(Event::SequenceEnd).unwrap(); + emitter.emit(Event::SequenceEnd).unwrap(); + emitter.emit(Event::DocumentEnd).unwrap(); + emitter.emit(Event::StreamEnd).unwrap(); + } + buffer.set_position(0); + + let result = + String::from_utf8_lossy(&buffer.into_inner()).to_string(); + assert_eq!(result, "- - nested\n"); + } + + #[test] + fn test_emitter_nested_mapping() { + let mut buffer = Cursor::new(Vec::with_capacity(100)); + { + let mut emitter = Emitter::new(Box::new(&mut buffer)); + emitter.emit(Event::StreamStart).unwrap(); + emitter.emit(Event::DocumentStart).unwrap(); + emitter + .emit(Event::MappingStart(Mapping { tag: None })) + .unwrap(); + emitter + .emit(Event::Scalar(Scalar { + tag: None, + value: "key", + style: ScalarStyle::Plain, + })) + .unwrap(); + emitter + .emit(Event::MappingStart(Mapping { tag: None })) + .unwrap(); + emitter + .emit(Event::Scalar(Scalar { + tag: None, + value: "nested_key", + style: ScalarStyle::Plain, + })) + .unwrap(); + emitter + .emit(Event::Scalar(Scalar { + tag: None, + value: "nested_value", + style: ScalarStyle::Plain, + })) + .unwrap(); + emitter.emit(Event::MappingEnd).unwrap(); + emitter.emit(Event::MappingEnd).unwrap(); + emitter.emit(Event::DocumentEnd).unwrap(); + emitter.emit(Event::StreamEnd).unwrap(); + } + buffer.set_position(0); + + let result = + String::from_utf8_lossy(&buffer.into_inner()).to_string(); + assert_eq!(result, "key:\n nested_key: nested_value\n"); + } +} diff --git a/tests/libyml/test_error.rs b/tests/libyml/test_error.rs new file mode 100644 index 00000000..c018380f --- /dev/null +++ b/tests/libyml/test_error.rs @@ -0,0 +1,25 @@ +#[cfg(test)] +mod tests { + // use libyml as sys; + use serde_yml::libyml::error::Result; + + #[test] + fn test_result_ok() { + let value: Result = Ok(42); + match value { + Ok(v) => assert_eq!(v, 42), + Err(_) => panic!("Expected Ok(42), but got an Err"), + } + } + + #[test] + fn test_result_ok_with_string() { + let value: Result = Ok(String::from("Hello, world!")); + match value { + Ok(v) => assert_eq!(v, "Hello, world!"), + Err(_) => { + panic!("Expected Ok(\"Hello, world!\"), but got an Err") + } + } + } +} diff --git a/tests/libyml/test_parser.rs b/tests/libyml/test_parser.rs new file mode 100644 index 00000000..e321e10e --- /dev/null +++ b/tests/libyml/test_parser.rs @@ -0,0 +1,187 @@ +#[cfg(test)] +mod tests { + use serde_yml::libyml::parser::Event; + use serde_yml::libyml::parser::Parser; + use std::borrow::Cow; + + /// Tests the creation of a new `Parser` instance with valid input. + /// Verifies that the parser is created successfully and initializes correctly. + #[test] + fn test_parser_creation() { + let input = Cow::Borrowed(b"foo: bar\n"); + let mut parser = Parser::new(Cow::Borrowed(input.as_ref())); + assert!(matches!( + parser.parse_next_event().unwrap().0, + Event::StreamStart + )); + } + + /// Tests the `parse_next_event` method for a stream start event. + /// Verifies that the event is correctly parsed and returned. + #[test] + fn test_parse_stream_start_event() { + let input = Cow::Borrowed(b"foo: bar\n"); + let mut parser = Parser::new(Cow::Borrowed(input.as_ref())); + let (event, _mark) = parser.parse_next_event().unwrap(); + assert!(matches!(event, Event::StreamStart)); + } + + /// Tests the `parse_next_event` method for a stream end event. + /// Verifies that the event is correctly parsed and returned. + #[test] + fn test_parse_stream_end_event() { + let input = Cow::Borrowed(b"foo: bar\n").as_ref(); + let mut parser = Parser::new(Cow::Borrowed(input)); + let mut stream_end_reached = false; + + while let Ok((event, _mark)) = parser.parse_next_event() { + if matches!(event, Event::StreamEnd) { + stream_end_reached = true; + break; + } + } + + assert!(stream_end_reached, "StreamEnd event was not reached"); + } + + /// Tests the `parse_next_event` method for a document start event. + /// Verifies that the event is correctly parsed and returned. + #[test] + fn test_parse_document_start_event() { + let input = Cow::Borrowed(b"---\nfoo: bar\n").as_ref(); + let mut parser = Parser::new(Cow::Borrowed(input)); + let mut found_start = false; + while let Ok((event, _mark)) = parser.parse_next_event() { + if matches!(event, Event::DocumentStart) { + found_start = true; + break; + } + } + assert!(found_start); + } + + /// Tests the `parse_next_event` method for a document end event. + /// Verifies that the event is correctly parsed and returned. + #[test] + fn test_parse_document_end_event() { + let input = + Cow::Borrowed(b"foo: bar\n---\nbaz: qux\n").as_ref(); + let mut parser = Parser::new(Cow::Borrowed(input)); + let mut found_end = false; + while let Ok((event, _mark)) = parser.parse_next_event() { + if matches!(event, Event::DocumentEnd) { + found_end = true; + break; + } + } + assert!(found_end); + } + + /// Tests the `parse_next_event` method for a scalar event. + /// Verifies that the event is correctly parsed and returned. + #[test] + fn test_parse_scalar_event() { + let input = Cow::Borrowed(b"bar\n").as_ref(); + let mut parser = Parser::new(Cow::Borrowed(input)); + let mut found_scalar = false; + while let Ok((event, _mark)) = parser.parse_next_event() { + if let Event::Scalar(scalar) = event { + assert_eq!(scalar.value.as_ref(), b"bar"); + found_scalar = true; + break; + } + } + assert!(found_scalar); + } + + /// Tests the `parse_next_event` method for a sequence start event. + /// Verifies that the event is correctly parsed and returned. + #[test] + fn test_parse_sequence_start_event() { + let input = Cow::Borrowed(b"- item1\n- item2\n").as_ref(); + let mut parser = Parser::new(Cow::Borrowed(input)); + let mut found_start = false; + while let Ok((event, _mark)) = parser.parse_next_event() { + if matches!(event, Event::SequenceStart(_)) { + found_start = true; + break; + } + } + assert!(found_start); + } + + /// Tests the `parse_next_event` method for a sequence end event. + /// Verifies that the event is correctly parsed and returned. + #[test] + fn test_parse_sequence_end_event() { + let input = Cow::Borrowed(b"- item1\n- item2\n").as_ref(); + let mut parser = Parser::new(Cow::Borrowed(input)); + let mut found_end = false; + while let Ok((event, _mark)) = parser.parse_next_event() { + if matches!(event, Event::SequenceEnd) { + found_end = true; + break; + } + } + assert!(found_end); + } + + /// Tests the `parse_next_event` method for a mapping start event. + /// Verifies that the event is correctly parsed and returned. + #[test] + fn test_parse_mapping_start_event() { + let input = Cow::Borrowed(b"key: value\n").as_ref(); + let mut parser = Parser::new(Cow::Borrowed(input)); + let mut found_start = false; + while let Ok((event, _mark)) = parser.parse_next_event() { + if matches!(event, Event::MappingStart(_)) { + found_start = true; + break; + } + } + assert!(found_start); + } + + /// Tests the `parse_next_event` method for a mapping end event. + /// Verifies that the event is correctly parsed and returned. + #[test] + fn test_parse_mapping_end_event() { + let input = Cow::Borrowed(b"key: value\n").as_ref(); + let mut parser = Parser::new(Cow::Borrowed(input)); + let mut found_end = false; + while let Ok((event, _mark)) = parser.parse_next_event() { + if matches!(event, Event::MappingEnd) { + found_end = true; + break; + } + } + assert!(found_end); + } + + /// Tests the `parse_next_event` method for nested sequences. + /// Verifies that the events are correctly parsed and returned. + #[test] + fn test_parse_nested_sequences() { + let input = + Cow::Borrowed(b"- item1\n- - nested1\n - nested2\n") + .as_ref(); + let mut parser = Parser::new(Cow::Borrowed(input)); + let mut found_nested_start = false; + + while let Ok((event, _mark)) = parser.parse_next_event() { + if matches!(event, Event::SequenceStart(_)) { + if found_nested_start { + // Found the nested sequence start + break; + } else { + found_nested_start = true; + } + } + } + + assert!( + found_nested_start, + "Nested sequence start event was not found" + ); + } +} diff --git a/tests/libyml/test_safe_cstr.rs b/tests/libyml/test_safe_cstr.rs new file mode 100644 index 00000000..1f1366ec --- /dev/null +++ b/tests/libyml/test_safe_cstr.rs @@ -0,0 +1,251 @@ +#[cfg(test)] +mod tests { + use serde_yml::libyml::safe_cstr::{CStr, CStrError}; + use std::{ffi::CString, ptr::NonNull, sync::Arc, thread}; + + /// Tests creating a `CStr` from a static byte slice with a null terminator. + /// Verifies that the `CStr` is created successfully. + #[test] + fn test_from_bytes_with_nul() { + let bytes: &'static [u8] = b"hello\0"; + let cstr = CStr::from_bytes_with_nul(bytes); + assert!(cstr.is_ok()); + let cstr = cstr.unwrap(); + assert_eq!(cstr.to_bytes(), b"hello"); + } + + /// Tests creating a `CStr` from a static byte slice without a null terminator. + /// Verifies that an error is returned. + #[test] + fn test_from_bytes_without_nul() { + let bytes: &'static [u8] = b"hello"; + let cstr = CStr::from_bytes_with_nul(bytes); + assert!(cstr.is_err()); + } + + /// Tests creating a `CStr` from an empty byte slice. + /// Verifies that an error is returned. + #[test] + fn test_from_bytes_empty() { + let bytes: &'static [u8] = b""; + let cstr = CStr::from_bytes_with_nul(bytes); + assert!(cstr.is_err()); + } + + /// Tests creating a `CStr` from a byte slice with only a null terminator. + /// Verifies that the `CStr` is created successfully and is empty. + #[test] + fn test_from_bytes_with_only_nul() { + let bytes: &'static [u8] = b"\0"; + let cstr = CStr::from_bytes_with_nul(bytes); + assert!(cstr.is_ok()); + let cstr = cstr.unwrap(); + assert!(cstr.is_empty()); + } + + /// Tests creating a `CStr` from a byte slice with one character and a null terminator. + /// Verifies that the `CStr` is created successfully and has the correct length. + #[test] + fn test_from_bytes_with_one_char() { + let bytes: &'static [u8] = b"a\0"; + let cstr = CStr::from_bytes_with_nul(bytes); + assert!(cstr.is_ok()); + let cstr = cstr.unwrap(); + assert_eq!(cstr.to_bytes(), b"a"); + } + + /// Tests creating a `CStr` from a non-null pointer. + /// Verifies that the `CStr` is created successfully. + #[test] + fn test_from_ptr() { + let c_string = CString::new("hello").unwrap(); + let ptr = NonNull::new(c_string.into_raw()).unwrap(); + let cstr = CStr::from_ptr(ptr); + assert_eq!(cstr.to_bytes(), b"hello"); + } + + /// Tests calculating the length of the `CStr`. + /// Verifies that the length is correct. + #[test] + fn test_len() { + let bytes: &'static [u8] = b"hello\0"; + let cstr = CStr::from_bytes_with_nul(bytes).unwrap(); + assert_eq!(cstr.len(), 5); + } + + /// Tests checking if the `CStr` is empty. + /// Verifies that the correct result is returned. + #[test] + fn test_is_empty() { + let bytes: &'static [u8] = b"\0"; + let cstr = CStr::from_bytes_with_nul(bytes).unwrap(); + assert!(cstr.is_empty()); + } + + /// Tests retrieving the underlying byte slice of the `CStr`. + /// Verifies that the byte slice is correct. + #[test] + fn test_to_bytes() { + let bytes: &'static [u8] = b"hello\0"; + let cstr = CStr::from_bytes_with_nul(bytes).unwrap(); + assert_eq!(cstr.to_bytes(), b"hello"); + } + + /// Tests the `Display` implementation for `CStr`. + /// Verifies that the formatted string is correct. + #[test] + fn test_display() { + let bytes: &'static [u8] = b"hello\0"; + let cstr = CStr::from_bytes_with_nul(bytes).unwrap(); + let display = format!("{}", cstr); + assert_eq!(display, "hello"); + } + + /// Tests the `Debug` implementation for `CStr`. + /// Verifies that the debug representation is correct. + #[test] + fn test_debug() { + let bytes: &'static [u8] = b"hello\0"; + let cstr = CStr::from_bytes_with_nul(bytes).unwrap(); + let debug = format!("{:?}", cstr); + assert_eq!(debug, "\"hello\""); + } + + /// Tests the `Display` implementation for `CStr` with invalid UTF-8. + /// Verifies that the formatted string uses replacement characters. + #[test] + fn test_display_invalid_utf8() { + let bytes: &'static [u8] = b"hello\xFFworld\0"; + let cstr = CStr::from_bytes_with_nul(bytes).unwrap(); + let display = format!("{}", cstr); + assert_eq!(display, "hello�world"); + } + + /// Tests the `Debug` implementation for `CStr` with invalid UTF-8. + /// Verifies that the debug representation uses escape sequences. + #[test] + fn test_debug_invalid_utf8() { + let bytes: &'static [u8] = b"hello\xFFworld\0"; + let cstr = CStr::from_bytes_with_nul(bytes).unwrap(); + let debug = format!("{:?}", cstr); + assert_eq!(debug, "\"hello\\xffworld\""); + } + + /// Tests thread safety of `CStr` struct by sending it between threads. + #[test] + fn test_send_sync() { + let cstr = CStr::from_bytes_with_nul(b"hello\0").unwrap(); + let arc_cstr = Arc::new(cstr); + + let arc_clone = Arc::clone(&arc_cstr); + let handle = thread::spawn(move || { + assert_eq!(arc_clone.to_bytes(), b"hello"); + }); + + handle.join().unwrap(); + } + + /// Tests proper handling of null pointers. + /// Verifies that creating a `CStr` from a null pointer is not allowed. + #[test] + fn test_null_pointer() { + let ptr = NonNull::new(std::ptr::null_mut::()); + assert!(ptr.is_none()); + } + + /// Tests that allocated resources are properly released. + #[test] + fn test_resource_release() { + let c_string = CString::new("hello").unwrap(); + let raw = c_string.into_raw(); + unsafe { + let _ = CString::from_raw(raw); + } + } + + /// Tests the custom `CStrError` error type. + /// Verifies that the error message is correct. + #[test] + fn test_cstr_error() { + let error = CStrError; + assert_eq!(format!("{}", error), "CStr error occurred"); + } + + /// Tests the `to_bytes` method with a complex byte array. + /// Verifies that the byte slice is correct. + #[test] + fn test_to_bytes_complex() { + let bytes: &'static [u8] = b"hello\xFFworld\0"; + let cstr = CStr::from_bytes_with_nul(bytes).unwrap(); + assert_eq!(cstr.to_bytes(), b"hello\xFFworld"); + } + + /// Tests the `to_bytes` method with an empty byte array. + /// Verifies that the byte slice is correct. + #[test] + fn test_to_bytes_empty() { + let bytes: &'static [u8] = b"\0"; + let cstr = CStr::from_bytes_with_nul(bytes).unwrap(); + assert_eq!(cstr.to_bytes(), b""); + } + + /// Tests the `to_bytes` method with a byte array containing a single character. + /// Verifies that the byte slice is correct. + #[test] + fn test_to_bytes_single_char() { + let bytes: &'static [u8] = b"a\0"; + let cstr = CStr::from_bytes_with_nul(bytes).unwrap(); + assert_eq!(cstr.to_bytes(), b"a"); + } + + /// Tests the `to_bytes` method with a byte array containing a single character and a null terminator. + /// Verifies that the byte slice is correct. + #[test] + fn test_to_bytes_single_char_with_nul() { + let bytes: &'static [u8] = b"a\0"; + let cstr = CStr::from_bytes_with_nul(bytes).unwrap(); + assert_eq!(cstr.to_bytes(), b"a"); + } + + /// Tests the `to_bytes` method with a byte array containing a single null terminator. + /// Verifies that the byte slice is correct. + #[test] + fn test_to_bytes_single_nul() { + let bytes: &'static [u8] = b"\0"; + let cstr = CStr::from_bytes_with_nul(bytes).unwrap(); + assert_eq!(cstr.to_bytes(), b""); + } + + /// Tests the `to_bytes` method with a very long byte array. + /// Verifies that the byte slice is correct. + #[test] + fn test_to_bytes_long_string() { + const LONG_STRING_SIZE: usize = 10_000; + let mut long_string = Vec::with_capacity(LONG_STRING_SIZE + 1); + long_string + .extend(std::iter::repeat(b'a').take(LONG_STRING_SIZE)); + long_string.push(b'\0'); + let bytes = Box::leak(long_string.into_boxed_slice()); + + let cstr = CStr::from_bytes_with_nul(bytes).unwrap(); + assert_eq!(cstr.to_bytes().len(), LONG_STRING_SIZE); + } + + /// Tests the `to_bytes` method with Unicode characters. + /// Verifies that the byte slice is correct. + #[test] + fn test_to_bytes_unicode() { + let bytes: &'static [u8] = "hello🌍\0".as_bytes(); + let cstr = CStr::from_bytes_with_nul(bytes).unwrap(); + assert_eq!(cstr.to_bytes(), "hello🌍".as_bytes()); + } + + /// Tests the `to_bytes` method with multiple null terminators within the byte array. + /// Verifies that the byte slice is correct up to the first null terminator. + #[test] + fn test_to_bytes_multiple_nulls() { + let bytes: &'static [u8] = b"hello\0world\0"; + let cstr = CStr::from_bytes_with_nul(bytes).unwrap(); + assert_eq!(cstr.to_bytes(), b"hello"); + } +} diff --git a/tests/libyml/test_tag.rs b/tests/libyml/test_tag.rs new file mode 100644 index 00000000..2d74184a --- /dev/null +++ b/tests/libyml/test_tag.rs @@ -0,0 +1,84 @@ +#[cfg(test)] +mod tests { + use serde_yml::libyml::tag::{Tag, TagFormatError}; + + /// Tests the creation of a new Tag instance using the NULL constant. + /// Verifies that the created Tag instance is not null. + #[test] + fn test_new_tag_null() { + let tag_null = Tag::new(Tag::NULL); + assert!(!tag_null.is_empty()); + } + + /// Tests the creation of a new Tag instance with a custom tag. + /// Verifies that the created Tag instance matches the provided custom tag. + #[test] + fn test_new_custom_tag() { + let custom_tag = Tag::new("tag:example.org,2024:custom"); + assert_eq!(custom_tag, "tag:example.org,2024:custom"); + } + + /// Tests if a Tag starts with a given prefix. + /// Verifies that the method returns true for a matching prefix and false otherwise. + #[test] + fn test_tag_starts_with() { + let custom_tag = Tag::new("tag:example.org,2024:custom"); + assert!(custom_tag.starts_with("tag:example.org").unwrap()); + assert!(!custom_tag.starts_with("tag:example.com").unwrap()); + } + + /// Tests the handling of TagFormatError when the prefix is longer than the tag. + /// Verifies that the method returns an error for a longer prefix. + #[test] + fn test_tag_starts_with_error() { + let custom_tag = Tag::new("tag:example.org,2024:custom"); + let result = + custom_tag.starts_with("tag:example.org,2024:custom:extra"); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), TagFormatError); + } + + /// Tests the comparison of a Tag with a &str. + /// Verifies that the comparison returns true for matching values and false otherwise. + #[test] + fn test_tag_comparison() { + let custom_tag = Tag::new("tag:example.org,2024:custom"); + let comparison_str = "tag:example.org,2024:custom"; + assert_eq!(custom_tag, comparison_str); + + let non_matching_str = "tag:example.org,2024:other"; + assert_ne!(custom_tag, non_matching_str); + } + + /// Tests the Deref implementation to access the underlying byte slice. + /// Verifies that the dereferenced value matches the original tag string. + #[test] + fn test_tag_deref() { + let custom_tag = Tag::new("tag:example.org,2024:custom"); + let tag_bytes: &[u8] = &custom_tag; + assert_eq!(tag_bytes, b"tag:example.org,2024:custom"); + } + + /// Tests the Debug implementation for a Tag instance. + /// Verifies that the debug representation of the Tag instance is correct. + #[test] + fn test_tag_debug() { + let custom_tag = Tag::new("tag:example.org,2024:custom"); + let debug_str = format!("{:?}", custom_tag); + assert_eq!(debug_str, "\"tag:example.org,2024:custom\""); + } + + /// Tests the creation of Tag instances using Tag constants for BOOL, INT, and FLOAT. + /// Verifies that the created Tag instances match the respective constants. + #[test] + fn test_tag_constants() { + let tag_bool = Tag::new(Tag::BOOL); + assert_eq!(tag_bool, Tag::BOOL); + + let tag_int = Tag::new(Tag::INT); + assert_eq!(tag_int, Tag::INT); + + let tag_float = Tag::new(Tag::FLOAT); + assert_eq!(tag_float, Tag::FLOAT); + } +} diff --git a/tests/libyml/test_util.rs b/tests/libyml/test_util.rs new file mode 100644 index 00000000..60ed549f --- /dev/null +++ b/tests/libyml/test_util.rs @@ -0,0 +1,108 @@ +#[cfg(test)] +mod tests { + use serde_yml::libyml::util::{InitPtr, Owned}; + use std::mem::MaybeUninit; + use std::ops::Deref; + + /// Tests that a new uninitialized `Owned` instance can be created. + /// Verifies that the pointer in the `Owned` instance is not null. + #[test] + fn test_new_uninit() { + let uninit_owned: Owned, i32> = + Owned::new_uninit(); + assert!(!uninit_owned.ptr.is_null()); + } + + /// Tests the `assume_init` function to ensure that it correctly converts + /// an uninitialized `Owned` instance to an initialized one. + /// Verifies that the pointer in the initialized `Owned` instance is not null. + #[test] + fn test_assume_init() { + let uninit_owned: Owned, i32> = + Owned::new_uninit(); + let init_owned: Owned = + unsafe { Owned::assume_init(uninit_owned) }; + assert!(!init_owned.ptr.is_null()); + } + + /// Tests the `deref` implementation for `Owned`. + /// Verifies that the dereferenced pointer matches the original pointer. + #[test] + fn test_deref() { + let uninit_owned: Owned, i32> = + Owned::new_uninit(); + let init_ptr = uninit_owned.ptr; + assert_eq!( + uninit_owned.deref().ptr as *mut MaybeUninit, + init_ptr as *mut MaybeUninit + ); + } + + /// Tests the `drop` implementation for `Owned`. + /// Ensures that dropping an uninitialized `Owned` instance does not cause a panic. + #[test] + fn test_drop() { + let uninit_owned: Owned, i32> = + Owned::new_uninit(); + drop(uninit_owned); + } + + /// Tests that an `InitPtr` instance is correctly created and its pointer is not null. + #[test] + fn test_init_ptr() { + let mut value: i32 = 42; + let init_ptr = InitPtr { ptr: &mut value }; + assert!(!init_ptr.ptr.is_null()); + assert_eq!(unsafe { *init_ptr.ptr }, 42); + } + + /// Tests the `deref` implementation for initialized `Owned`. + /// Verifies that the dereferenced pointer matches the original pointer after initialization. + #[test] + fn test_deref_after_init() { + let uninit_owned: Owned, i32> = + Owned::new_uninit(); + let init_owned: Owned = + unsafe { Owned::assume_init(uninit_owned) }; + let init_ptr = init_owned.ptr; + assert_eq!(init_owned.deref().ptr, init_ptr); + } + + /// Tests creating and initializing an `Owned` instance with a different type (f64). + #[test] + fn test_new_uninit_f64() { + let uninit_owned: Owned, f64> = + Owned::new_uninit(); + assert!(!uninit_owned.ptr.is_null()); + } + + /// Tests the `assume_init` function with a different type (f64). + #[test] + fn test_assume_init_f64() { + let uninit_owned: Owned, f64> = + Owned::new_uninit(); + let init_owned: Owned = + unsafe { Owned::assume_init(uninit_owned) }; + assert!(!init_owned.ptr.is_null()); + } + + /// Tests the `deref` implementation for `Owned` with a different type (f64). + #[test] + fn test_deref_f64() { + let uninit_owned: Owned, f64> = + Owned::new_uninit(); + let init_ptr = uninit_owned.ptr; + assert_eq!( + uninit_owned.deref().ptr as *mut MaybeUninit, + init_ptr as *mut MaybeUninit + ); + } + + /// Tests the `drop` implementation for `Owned` with a different type (f64). + #[test] + fn test_drop_f64() { + let uninit_owned: Owned, f64> = + Owned::new_uninit(); + drop(uninit_owned); + } +} diff --git a/tests/mod.rs b/tests/mod.rs index 6995e2a3..25891795 100644 --- a/tests/mod.rs +++ b/tests/mod.rs @@ -1,6 +1,12 @@ +/// This module contains the tests for the `libyml` module. +pub mod libyml; + /// This module contains the tests for the `macros` module. pub mod macros; +/// This module contains the tests for the `modules` module. +pub mod modules; + /// This module contains the tests for the `utilities` module. pub mod utilities; diff --git a/tests/modules/mod.rs b/tests/modules/mod.rs new file mode 100644 index 00000000..eff6afb5 --- /dev/null +++ b/tests/modules/mod.rs @@ -0,0 +1,2 @@ +/// This module contains the tests for the `path` module. +pub mod test_path; diff --git a/tests/modules/test_path.rs b/tests/modules/test_path.rs new file mode 100644 index 00000000..f859651a --- /dev/null +++ b/tests/modules/test_path.rs @@ -0,0 +1,121 @@ +#[cfg(test)] +mod tests { + use serde_yml::modules::path::Path; + + /// Test the Path::Root variant. + /// + /// This test ensures that the root path is correctly formatted as ".". + #[test] + fn test_path_root() { + let path = Path::Root; + assert_eq!(format!("{}", path), "."); + } + + /// Test the Path::Seq variant. + /// + /// This test checks that a sequence path with a given index is correctly formatted. + #[test] + fn test_path_seq() { + let root = Path::Root; + let path = Path::Seq { + parent: &root, + index: 42, + }; + assert_eq!(format!("{}", path), "\\[42\\]"); + } + + /// Test the Path::Map variant. + /// + /// This test checks that a map path with a given key is correctly formatted. + #[test] + fn test_path_map() { + let root = Path::Root; + let path = Path::Map { + parent: &root, + key: "key", + }; + assert_eq!(format!("{}", path), "key"); + } + + /// Test the Path::Alias variant. + /// + /// This test checks that an alias path is correctly formatted. + #[test] + fn test_path_alias() { + let root = Path::Root; + let path = Path::Alias { parent: &root }; + assert_eq!(format!("{}", path), ""); + } + + /// Test the Path::Unknown variant. + /// + /// This test checks that an unknown path is correctly formatted. + #[test] + fn test_path_unknown() { + let root = Path::Root; + let path = Path::Unknown { parent: &root }; + assert_eq!(format!("{}", path), "?"); + } + + /// Test nested paths. + /// + /// This test ensures that nested paths with various combinations of variants are correctly formatted. + #[test] + fn test_path_nested() { + let root = Path::Root; + let seq = Path::Seq { + parent: &root, + index: 0, + }; + let map = Path::Map { + parent: &seq, + key: "key", + }; + let alias = Path::Alias { parent: &map }; + let unknown = Path::Unknown { parent: &alias }; + assert_eq!(format!("{}", unknown), "\\[0\\].key..?"); + } + + /// Test deeply nested paths. + /// + /// This test checks the formatting of a deeply nested path with multiple levels of sequences and maps. + #[test] + fn test_deeply_nested_path() { + let root = Path::Root; + let seq1 = Path::Seq { + parent: &root, + index: 1, + }; + let map1 = Path::Map { + parent: &seq1, + key: "first", + }; + let seq2 = Path::Seq { + parent: &map1, + index: 2, + }; + let map2 = Path::Map { + parent: &seq2, + key: "second", + }; + let alias = Path::Alias { parent: &map2 }; + let unknown = Path::Unknown { parent: &alias }; + assert_eq!( + format!("{}", unknown), + "\\[1\\].first.\\[2\\].second..?" + ); + } + + /// Test empty key in Path::Map. + /// + /// This test ensures that a map path with an empty key is correctly handled and formatted. + #[test] + fn test_path_map_empty_key() { + let root = Path::Root; + let path = Path::Map { + parent: &root, + key: "", + }; + assert_eq!(format!("{}", path), ""); + } +} diff --git a/tests/test_path.rs b/tests/test_path.rs deleted file mode 100644 index fcf2c04c..00000000 --- a/tests/test_path.rs +++ /dev/null @@ -1,66 +0,0 @@ -#[cfg(test)] -mod tests { - use serde_yml::modules::path::Path; - - // Tests for Path::Root variant - #[test] - fn test_path_root() { - let path = Path::Root; - assert_eq!(format!("{}", path), "."); - } - - // Tests for Path::Seq variant - #[test] - fn test_path_seq() { - let root = Path::Root; - let path = Path::Seq { - parent: &root, - index: 42, - }; - assert_eq!(format!("{}", path), "\\[42\\]"); - } - - // Tests for Path::Map variant - #[test] - fn test_path_map() { - let root = Path::Root; - let path = Path::Map { - parent: &root, - key: "key", - }; - assert_eq!(format!("{}", path), "key"); - } - - // Tests for Path::Alias variant - #[test] - fn test_path_alias() { - let root = Path::Root; - let path = Path::Alias { parent: &root }; - assert_eq!(format!("{}", path), ""); - } - - // Tests for Path::Unknown variant - #[test] - fn test_path_unknown() { - let root = Path::Root; - let path = Path::Unknown { parent: &root }; - assert_eq!(format!("{}", path), "?"); - } - - // Tests for nested paths - #[test] - fn test_path_nested() { - let root = Path::Root; - let seq = Path::Seq { - parent: &root, - index: 0, - }; - let map = Path::Map { - parent: &seq, - key: "key", - }; - let alias = Path::Alias { parent: &map }; - let unknown = Path::Unknown { parent: &alias }; - assert_eq!(format!("{}", unknown), "\\[0\\].key..?"); - } -} diff --git a/tests/test_safe_cstr.rs b/tests/test_safe_cstr.rs deleted file mode 100644 index 17760464..00000000 --- a/tests/test_safe_cstr.rs +++ /dev/null @@ -1,63 +0,0 @@ -#[cfg(test)] -mod tests { - use serde_yml::libyml::safe_cstr::CStr; - use std::ptr::NonNull; - - #[test] - fn test_from_ptr() { - let valid_bytes = b"hello\0"; - let ptr = NonNull::from(valid_bytes).cast(); - let cstr = CStr::from_ptr(ptr); - assert_eq!( - cstr.to_bytes(), - &valid_bytes[..valid_bytes.len() - 1] - ); - } - - #[test] - fn test_len() { - let valid_bytes = b"hello\0"; - let cstr = CStr::from_bytes_with_nul(valid_bytes).unwrap(); - assert_eq!(cstr.len(), 5); - - let empty_bytes = b""; - let result = CStr::from_bytes_with_nul(empty_bytes); - assert!(result.is_err()); - } - - #[test] - fn test_to_bytes() { - let valid_bytes = b"hello\0"; - let cstr = CStr::from_bytes_with_nul(valid_bytes).unwrap(); - assert_eq!( - cstr.to_bytes(), - &valid_bytes[..valid_bytes.len() - 1] - ); - - let empty_bytes = b""; - let result = CStr::from_bytes_with_nul(empty_bytes); - assert!(result.is_err()); - } - - #[test] - fn test_display() { - let valid_bytes = b"hello\0"; - let cstr = CStr::from_bytes_with_nul(valid_bytes).unwrap(); - assert_eq!(format!("{}", cstr), "hello"); - - let invalid_bytes = b"hello\xff\0"; - let cstr = CStr::from_bytes_with_nul(invalid_bytes).unwrap(); - assert_eq!(format!("{}", cstr), "hello�"); - } - - #[test] - fn test_debug() { - let valid_bytes = b"hello\0"; - let cstr = CStr::from_bytes_with_nul(valid_bytes).unwrap(); - assert_eq!(format!("{:?}", cstr), "\"hello\""); - - let invalid_bytes = b"hello\xff\0"; - let cstr = CStr::from_bytes_with_nul(invalid_bytes).unwrap(); - assert_eq!(format!("{:?}", cstr), "\"hello\\xff\""); - } -} diff --git a/tests/value/mod.rs b/tests/value/mod.rs index eb060588..c9f35328 100644 --- a/tests/value/mod.rs +++ b/tests/value/mod.rs @@ -1,6 +1,15 @@ +/// The `test_de` module contains tests for the `Deserialize` trait implementations. +pub mod test_de; + +/// The `test_debug` module contains tests for the `Debug` trait implementations. +pub mod test_debug; + /// The `test_from` module contains tests for the `From` trait implementations. pub mod test_from; +/// The `test_index` module contains tests for the `Index` trait implementations. +pub mod test_index; + /// The `test_partial_eq` module contains tests for the `PartialEq` trait implementations. pub mod test_partial_eq; diff --git a/tests/value/test_de.rs b/tests/value/test_de.rs new file mode 100644 index 00000000..47a78917 --- /dev/null +++ b/tests/value/test_de.rs @@ -0,0 +1,495 @@ +#[cfg(test)] +mod tests { + use serde::Deserialize; + use serde_yml::value::{Tag, TaggedValue, Value}; + + /// Test deserialization of a `null` value into `Option<()>`. + #[test] + fn test_deserialize_null() { + let value = Value::Null; + let result: Option<()> = serde_yml::from_value(value).unwrap(); + assert_eq!(result, None); + } + + /// Test deserialization of a `bool` value. + #[test] + fn test_deserialize_bool() { + let value = Value::Bool(true); + let result: bool = serde_yml::from_value(value).unwrap(); + assert!(result); + } + + /// Test deserialization of an `i64` value. + #[test] + fn test_deserialize_i64() { + let value = Value::Number(42.into()); + let result: i64 = serde_yml::from_value(value).unwrap(); + assert_eq!(result, 42); + } + + /// Test deserialization of a `u64` value. + #[test] + fn test_deserialize_u64() { + let value = Value::Number(42.into()); + let result: u64 = serde_yml::from_value(value).unwrap(); + assert_eq!(result, 42); + } + + /// Test deserialization of a `f64` value. + #[test] + fn test_deserialize_f64() { + let value = Value::Number(42.5.into()); + let result: f64 = serde_yml::from_value(value).unwrap(); + assert_eq!(result, 42.5); + } + + /// Test deserialization of a `String` value. + #[test] + fn test_deserialize_string() { + let value = Value::String("hello".to_string()); + let result: String = serde_yml::from_value(value).unwrap(); + assert_eq!(result, "hello"); + } + + /// Test deserialization of a sequence into a `Vec`. + #[test] + fn test_deserialize_sequence() { + let value = Value::Sequence(vec![ + Value::Number(1.into()), + Value::Number(2.into()), + ]); + let result: Vec = serde_yml::from_value(value).unwrap(); + assert_eq!(result, vec![1, 2]); + } + + /// Test deserialization of a tagged enum variant. + #[test] + fn test_deserialize_enum() { + let value = Value::Tagged(Box::new(TaggedValue { + tag: Tag::new("B"), + value: Value::Number(42.into()), + })); + #[derive(Deserialize, PartialEq, Debug)] + enum E { + A, + B(i32), + C { x: i32 }, + } + let result: E = serde_yml::from_value(value).unwrap(); + assert_eq!(result, E::B(42)); + } + + /// Test deserialization of a newtype struct. + #[test] + fn test_deserialize_newtype_struct() { + let value = Value::Number(42.into()); + #[derive(Deserialize, PartialEq, Debug)] + struct Newtype(i32); + let result: Newtype = serde_yml::from_value(value).unwrap(); + assert_eq!(result, Newtype(42)); + } + + /// Test deserialization of a tuple. + #[test] + fn test_deserialize_tuple() { + let value = Value::Sequence(vec![ + Value::Number(1.into()), + Value::Number(2.into()), + ]); + let result: (i32, i32) = serde_yml::from_value(value).unwrap(); + assert_eq!(result, (1, 2)); + } + + /// Test deserialization of a tuple struct. + #[test] + fn test_deserialize_tuple_struct() { + let value = Value::Sequence(vec![ + Value::Number(1.into()), + Value::Number(2.into()), + ]); + #[derive(Deserialize, PartialEq, Debug)] + struct TupleStruct(i32, i32); + let result: TupleStruct = serde_yml::from_value(value).unwrap(); + assert_eq!(result, TupleStruct(1, 2)); + } + + /// Test deserialization of a sequence into a `Vec`. + #[test] + fn test_deserialize_bytes() { + let value = Value::Sequence(vec![ + Value::Number(1.into()), + Value::Number(2.into()), + ]); + let result: Vec = serde_yml::from_value(value).unwrap(); + assert_eq!(result, vec![1, 2]); + } + + /// Test deserialization of an identifier (string). + #[test] + fn test_deserialize_identifier() { + let value = Value::String("hello".to_string()); + let result: String = serde_yml::from_value(value).unwrap(); + assert_eq!(result, "hello"); + } + + /// Test deserialization of a struct. + #[test] + fn test_deserialize_struct() { + let value = Value::Mapping( + vec![ + ( + Value::String("x".to_string()), + Value::Number(1.into()), + ), + ( + Value::String("y".to_string()), + Value::Number(2.into()), + ), + ] + .into_iter() + .collect(), + ); + #[derive(Deserialize, PartialEq, Debug)] + struct Point { + x: i32, + y: i32, + } + let result: Point = serde_yml::from_value(value).unwrap(); + assert_eq!(result, Point { x: 1, y: 2 }); + } + + /// Test deserialization of a map. + #[test] + fn test_deserialize_map() { + let value = Value::Mapping( + vec![ + ( + Value::String("x".to_string()), + Value::Number(1.into()), + ), + ( + Value::String("y".to_string()), + Value::Number(2.into()), + ), + ] + .into_iter() + .collect(), + ); + let result: std::collections::HashMap = + serde_yml::from_value(value).unwrap(); + let mut expected = std::collections::HashMap::new(); + expected.insert("x".to_string(), 1); + expected.insert("y".to_string(), 2); + assert_eq!(result, expected); + } + + /// Test deserialization of `Option` with `Some` value. + #[test] + fn test_deserialize_option_some() { + let value = Value::Number(42.into()); + let result: Option = serde_yml::from_value(value).unwrap(); + assert_eq!(result, Some(42)); + } + + /// Test deserialization of `Option` with `None` value. + #[test] + fn test_deserialize_option_none() { + let value = Value::Null; + let result: Option = serde_yml::from_value(value).unwrap(); + assert_eq!(result, None); + } + + /// Test deserialization of a `char` value. + #[test] + fn test_deserialize_char() { + let value = Value::String("a".to_string()); + let result: char = serde_yml::from_value(value).unwrap(); + assert_eq!(result, 'a'); + } + + /// Test deserialization of a unit value. + #[test] + fn test_deserialize_unit() { + let value = Value::Null; + let result: () = serde_yml::from_value(value).unwrap(); + println!( + "✅ Deserialized unit value successfully. {:?}", + result + ); + } + /// Test deserialization of a unit struct. + #[test] + fn test_deserialize_unit_struct() { + let value = Value::Null; + #[derive(Deserialize, PartialEq, Debug)] + struct Unit; + let result: Unit = serde_yml::from_value(value).unwrap(); + assert_eq!(result, Unit); + } + /// Test deserialization of an empty tuple struct. + #[test] + fn test_deserialize_empty_tuple_struct() { + let yaml_str = "---"; + let value: Value = serde_yml::from_str(yaml_str).unwrap(); + + #[derive(Deserialize, PartialEq, Debug)] + struct Empty; + + let result: Empty = serde_yml::from_value(value).unwrap(); + println!("\n✅ Deserialized Empty tuple struct: {:?}", result); + } + + /// Test deserialization of an empty tuple. + #[test] + fn test_deserialize_empty_tuple() { + let yaml_str = "---"; + let value: Value = serde_yml::from_str(yaml_str).unwrap(); + + let result: () = serde_yml::from_value(value).unwrap(); + println!("\n✅ Deserialized Empty tuple: {:?}", result); + } + + /// Test deserialization of an empty struct. + #[test] + fn test_deserialize_empty_struct() { + let value = Value::Null; + #[derive(Deserialize, PartialEq, Debug)] + struct Empty; + let result: Empty = serde_yml::from_value(value).unwrap(); + assert_eq!(result, Empty); + } + /// Test deserialization of a unit variant. + #[test] + fn test_deserialize_unit_variant() { + let value = Value::String("Variant".to_string()); + #[derive(Deserialize, PartialEq, Debug)] + enum E { + Variant, + } + let result: E = serde_yml::from_value(value).unwrap(); + assert_eq!(result, E::Variant); + } + /// Test deserialization of a newtype variant. + #[test] + fn test_deserialize_newtype_variant() { + let yaml_str = "!Variant 0"; + let value: Value = serde_yml::from_str(yaml_str).unwrap(); + + #[derive(Deserialize, PartialEq, Debug)] + enum E { + Variant(i32), + } + + let result: E = serde_yml::from_value(value).unwrap(); + println!("\n✅ Deserialized newtype variant: {:?}", result); + } + + /// Test deserialization of a tuple variant. + #[test] + fn test_deserialize_tuple_variant() { + // YAML representation of the enum variant + let yaml_str = "---\n!Variant\n- 1\n- 2\n"; + let value: Value = serde_yml::from_str(yaml_str).unwrap(); + #[derive(Deserialize, PartialEq, Debug)] + enum E { + Variant(i32, i32), + } + let result: E = serde_yml::from_value(value).unwrap(); + assert_eq!(result, E::Variant(1, 2)); + } + + /// Test deserialization of a struct variant. + #[test] + fn test_deserialize_struct_variant() { + // YAML representation of the enum variant + let yaml_str = "---\n!Variant\nx: 1\ny: 2\n"; + let value: Value = serde_yml::from_str(yaml_str).unwrap(); + #[derive(Deserialize, PartialEq, Debug)] + enum E { + Variant { x: i32, y: i32 }, + } + let result: E = serde_yml::from_value(value).unwrap(); + assert_eq!(result, E::Variant { x: 1, y: 2 }); + } + /// Test deserialization of a sequence variant. + #[test] + fn test_deserialize_sequence_variant() { + // YAML representation of the enum variant + let yaml_str = "---\n!Variant\n- 1\n- 2\n"; + let value: Value = serde_yml::from_str(yaml_str).unwrap(); + #[derive(Deserialize, PartialEq, Debug)] + enum E { + Variant(Vec), + } + let result: E = serde_yml::from_value(value).unwrap(); + assert_eq!(result, E::Variant(vec![1, 2])); + } + /// Test deserialization of a map variant. + #[test] + fn test_deserialize_map_variant() { + // YAML representation of the enum variant + let yaml_str = "---\n!Variant\nx: 1\ny: 2\n"; + let value: Value = serde_yml::from_str(yaml_str).unwrap(); + #[derive(Deserialize, PartialEq, Debug)] + enum E { + Variant(std::collections::HashMap), + } + let result: E = serde_yml::from_value(value).unwrap(); + let mut expected = std::collections::HashMap::new(); + expected.insert("x".to_string(), 1); + expected.insert("y".to_string(), 2); + assert_eq!(result, E::Variant(expected)); + } + /// Test deserialization of a tagged unit variant. + #[test] + fn test_deserialize_tagged_unit_variant() { + // YAML representation of the enum variant + let yaml_str = "---\n!Variant\n"; + let value: Value = serde_yml::from_str(yaml_str).unwrap(); + #[derive(Deserialize, PartialEq, Debug)] + enum E { + Variant, + } + let result: E = serde_yml::from_value(value).unwrap(); + assert_eq!(result, E::Variant); + } + /// Test deserialization of a tagged newtype variant. + #[test] + fn test_deserialize_tagged_newtype_variant() { + // YAML representation of the enum variant + let yaml_str = "---\n!Variant 0\n"; + let value: Value = serde_yml::from_str(yaml_str).unwrap(); + #[derive(Deserialize, PartialEq, Debug)] + enum E { + Variant(i32), + } + let result: E = serde_yml::from_value(value).unwrap(); + assert_eq!(result, E::Variant(0)); + } + /// Test deserialization of a tagged tuple variant. + #[test] + fn test_deserialize_tagged_tuple_variant() { + // YAML representation of the enum variant + let yaml_str = "---\n!Variant\n- 1\n- 2\n"; + let value: Value = serde_yml::from_str(yaml_str).unwrap(); + #[derive(Deserialize, PartialEq, Debug)] + enum E { + Variant(i32, i32), + } + let result: E = serde_yml::from_value(value).unwrap(); + assert_eq!(result, E::Variant(1, 2)); + } + /// Test deserialization of a tagged struct variant. + #[test] + fn test_deserialize_tagged_struct_variant() { + // YAML representation of the enum variant + let yaml_str = "---\n!Variant\nx: 1\ny: 2\n"; + let value: Value = serde_yml::from_str(yaml_str).unwrap(); + #[derive(Deserialize, PartialEq, Debug)] + enum E { + Variant { x: i32, y: i32 }, + } + let result: E = serde_yml::from_value(value).unwrap(); + assert_eq!(result, E::Variant { x: 1, y: 2 }); + } + /// Test deserialization of a tagged sequence variant. + #[test] + fn test_deserialize_tagged_sequence_variant() { + // YAML representation of the enum variant + let yaml_str = "---\n!Variant\n- 1\n- 2\n"; + let value: Value = serde_yml::from_str(yaml_str).unwrap(); + #[derive(Deserialize, PartialEq, Debug)] + enum E { + Variant(Vec), + } + let result: E = serde_yml::from_value(value).unwrap(); + assert_eq!(result, E::Variant(vec![1, 2])); + } + /// Test deserialization of a `f32` value. + #[test] + fn test_deserialize_f32() { + let value = Value::Number(serde_yml::Number::from(42.5f32)); + let result: f32 = serde_yml::from_value(value).unwrap(); + assert_eq!(result, 42.5f32); + } + /// Test deserialization of a `()` value. + #[test] + fn test_deserialize_unit_value() { + let value = Value::Null; + let result: () = serde_yml::from_value(value).unwrap(); + println!( + "✅ Deserialized unit value successfully. {:?}", + result + ); + } + + /// Test deserialization of a byte array. + #[test] + fn test_deserialize_byte_array() { + let value = Value::Sequence(vec![ + Value::Number(1.into()), + Value::Number(2.into()), + Value::Number(3.into()), + ]); + let result: [u8; 3] = serde_yml::from_value(value).unwrap(); + assert_eq!(result, [1, 2, 3]); + } + + /// Test deserialization of an optional byte array. + #[test] + fn test_deserialize_optional_byte_array() { + let value = Value::Sequence(vec![ + Value::Number(1.into()), + Value::Number(2.into()), + Value::Number(3.into()), + ]); + let result: Option<[u8; 3]> = + serde_yml::from_value(value).unwrap(); + assert_eq!(result, Some([1, 2, 3])); + } + + /// Test deserialization of a unit struct variant. + #[test] + fn test_deserialize_unit_struct_variant() { + #[derive(Deserialize, PartialEq, Debug)] + enum E { + V, + } + let value = Value::String("V".to_string()); + let result: E = serde_yml::from_value(value).unwrap(); + assert_eq!(result, E::V); + } + + /// Test deserialization of a newtype struct variant. + #[test] + fn test_deserialize_newtype_struct_variant() { + #[derive(Deserialize, PartialEq, Debug)] + enum E { + V(i32), + } + let value = Value::Tagged(Box::new(TaggedValue { + tag: Tag::new("V"), + value: Value::Number(42.into()), + })); + let result: E = serde_yml::from_value(value).unwrap(); + assert_eq!(result, E::V(42)); + } + + /// Test deserialization of a tuple struct variant. + #[test] + fn test_deserialize_tuple_struct_variant() { + #[derive(Deserialize, PartialEq, Debug)] + enum E { + V(i32, i32), + } + let value = Value::Tagged(Box::new(TaggedValue { + tag: Tag::new("V"), + value: Value::Sequence(vec![ + Value::Number(1.into()), + Value::Number(2.into()), + ]), + })); + let result: E = serde_yml::from_value(value).unwrap(); + assert_eq!(result, E::V(1, 2)); + } +} diff --git a/tests/test_debug.rs b/tests/value/test_debug.rs similarity index 100% rename from tests/test_debug.rs rename to tests/value/test_debug.rs diff --git a/tests/value/test_index.rs b/tests/value/test_index.rs new file mode 100644 index 00000000..51fb5c74 --- /dev/null +++ b/tests/value/test_index.rs @@ -0,0 +1,728 @@ +#[cfg(test)] +mod tests { + use serde_yml::value::Index; + use serde_yml::Value; + + /// Test for `index_into` method of `usize` implementation. + /// This test verifies that `index_into` correctly indexes into a `Value::Sequence`. + #[test] + fn test_usize_index_into_sequence() { + let sequence = Value::Sequence(vec![ + Value::Number(1.into()), + Value::Number(2.into()), + ]); + let index = 1; + assert_eq!( + index.index_into(&sequence), + Some(&Value::Number(2.into())) + ); + } + + /// Test for `index_into` method of `usize` implementation with a `Value::Mapping`. + /// This test verifies that `index_into` correctly indexes into a `Value::Mapping` with a numeric key. + #[test] + fn test_usize_index_into_mapping() { + let mut mapping = serde_yml::Mapping::new(); + mapping.insert( + Value::Number(1.into()), + Value::String("one".into()), + ); + let value = Value::Mapping(mapping); + let index = 1; + assert_eq!( + index.index_into(&value), + Some(&Value::String("one".into())) + ); + } + + /// Test for `index_into` method of `usize` implementation with an out-of-bounds index in `Value::Sequence`. + /// This test verifies that `index_into` returns None for an out-of-bounds index. + #[test] + fn test_usize_index_into_sequence_out_of_bounds() { + let sequence = Value::Sequence(vec![ + Value::Number(1.into()), + Value::Number(2.into()), + ]); + let index = 3; + assert_eq!(index.index_into(&sequence), None); + } + + /// Test for `index_into` method of `usize` implementation with a non-numeric key in `Value::Mapping`. + /// This test verifies that `index_into` returns None for a non-numeric key. + #[test] + fn test_usize_index_into_mapping_non_numeric_key() { + let mut mapping = serde_yml::Mapping::new(); + mapping.insert( + Value::String("key".into()), + Value::String("value".into()), + ); + let value = Value::Mapping(mapping); + let index = 1; + assert_eq!(index.index_into(&value), None); + } + + /// Test for `index_into_mut` method of `usize` implementation. + /// This test verifies that `index_into_mut` correctly indexes into a mutable `Value::Sequence`. + #[test] + fn test_usize_index_into_mut_sequence() { + let mut sequence = Value::Sequence(vec![ + Value::Number(1.into()), + Value::Number(2.into()), + ]); + let index = 1; + if let Some(value) = index.index_into_mut(&mut sequence) { + *value = Value::Number(3.into()); + } + assert_eq!( + sequence, + Value::Sequence(vec![ + Value::Number(1.into()), + Value::Number(3.into()) + ]) + ); + } + + /// Test for `index_into_mut` method of `usize` implementation with a `Value::Mapping`. + /// This test verifies that `index_into_mut` correctly indexes into a mutable `Value::Mapping` with a numeric key. + #[test] + fn test_usize_index_into_mut_mapping() { + let mut mapping = serde_yml::Mapping::new(); + mapping.insert( + Value::Number(1.into()), + Value::String("one".into()), + ); + let mut value = Value::Mapping(mapping); + let index = 1; + if let Some(value) = index.index_into_mut(&mut value) { + *value = Value::String("two".into()); + } + let mut expected_mapping = serde_yml::Mapping::new(); + expected_mapping.insert( + Value::Number(1.into()), + Value::String("two".into()), + ); + assert_eq!(value, Value::Mapping(expected_mapping)); + } + + /// Test for `index_into_mut` method of `usize` implementation with an out-of-bounds index in `Value::Sequence`. + /// This test verifies that `index_into_mut` returns None for an out-of-bounds index. + #[test] + fn test_usize_index_into_mut_sequence_out_of_bounds() { + let mut sequence = Value::Sequence(vec![ + Value::Number(1.into()), + Value::Number(2.into()), + ]); + let index = 3; + assert_eq!(index.index_into_mut(&mut sequence), None); + } + + /// Test for `index_into_mut` method of `usize` implementation with a non-numeric key in `Value::Mapping`. + /// This test verifies that `index_into_mut` returns None for a non-numeric key. + #[test] + fn test_usize_index_into_mut_mapping_non_numeric_key() { + let mut mapping = serde_yml::Mapping::new(); + mapping.insert( + Value::String("key".into()), + Value::String("value".into()), + ); + let mut value = Value::Mapping(mapping); + let index = 1; + assert_eq!(index.index_into_mut(&mut value), None); + } + + /// Test for `index_or_insert` method of `usize` implementation. + /// This test verifies that `index_or_insert` correctly indexes or inserts into a `Value::Sequence`. + #[test] + fn test_usize_index_or_insert_sequence() { + let mut sequence = + Value::Sequence(vec![Value::Number(1.into())]); + let index = 1; + + // Extend the sequence to ensure the index is in bounds + if index >= sequence.as_sequence().unwrap().len() { + for _ in sequence.as_sequence().unwrap().len()..=index { + sequence.as_sequence_mut().unwrap().push(Value::Null); + } + } + + index + .index_or_insert(&mut sequence) + .clone_from(&Value::Number(2.into())); + assert_eq!( + sequence, + Value::Sequence(vec![ + Value::Number(1.into()), + Value::Number(2.into()) + ]) + ); + } + + /// Test for `index_or_insert` method of `usize` implementation with a `Value::Mapping`. + /// This test verifies that `index_or_insert` correctly indexes or inserts into a `Value::Mapping` with a numeric key. + #[test] + fn test_usize_index_or_insert_mapping() { + let mapping = serde_yml::Mapping::new(); + let mut value = Value::Mapping(mapping); + let index = 1; + index + .index_or_insert(&mut value) + .clone_from(&Value::String("one".into())); + let mut expected_mapping = serde_yml::Mapping::new(); + expected_mapping.insert( + Value::Number(1.into()), + Value::String("one".into()), + ); + assert_eq!(value, Value::Mapping(expected_mapping)); + } + + /// Test for `index_or_insert` method of `usize` implementation with an out-of-bounds index in `Value::Sequence`. + /// This test verifies that `index_or_insert` inserts a default value for an out-of-bounds index without panicking. + #[test] + fn test_usize_index_or_insert_sequence_out_of_bounds() { + let mut sequence = + Value::Sequence(vec![Value::Number(1.into())]); + let index = 1; + if index >= sequence.as_sequence().unwrap().len() { + for _ in sequence.as_sequence().unwrap().len()..=index { + sequence.as_sequence_mut().unwrap().push(Value::Null); + } + } + index + .index_or_insert(&mut sequence) + .clone_from(&Value::Number(2.into())); + assert_eq!( + sequence, + Value::Sequence(vec![ + Value::Number(1.into()), + Value::Number(2.into()) + ]) + ); + } + + /// Test for `index_into` method of `usize` implementation with a `Value` other than `Sequence` or `Mapping`. + /// This test verifies that `index_into` returns None for a non-indexable `Value`. + #[test] + fn test_usize_index_into_non_indexable() { + let value = Value::String("hello".into()); + let index = 1; + assert_eq!(index.index_into(&value), None); + } + + /// Test for `index_into_mut` method of `usize` implementation with a `Value` other than `Sequence` or `Mapping`. + /// This test verifies that `index_into_mut` returns None for a non-indexable `Value`. + #[test] + fn test_usize_index_into_mut_non_indexable() { + let mut value = Value::String("hello".into()); + let index = 1; + assert_eq!(index.index_into_mut(&mut value), None); + } + + /// Test for `index_or_insert` method of `usize` implementation with a `Value` other than `Sequence` or `Mapping`. + /// This test verifies that `index_or_insert` panics for a non-indexable `Value`. + #[test] + #[should_panic(expected = "cannot access index 1 of YAML string")] + fn test_usize_index_or_insert_non_indexable() { + let mut value = Value::String("hello".into()); + let index = 1; + index.index_or_insert(&mut value); + } + + /// Test for `index_or_insert` method of `usize` implementation with a `Value::Null`. + /// This test verifies that `index_or_insert` panics for a `Value::Null`. + #[test] + #[should_panic(expected = "cannot access index 1 of YAML null")] + fn test_usize_index_or_insert_null() { + let mut value = Value::Null; + let index = 1; + index.index_or_insert(&mut value); + } + + /// Test `index_into` with a `Value::Mapping`. + #[test] + fn test_value_index_into_mapping() { + let mut mapping = serde_yml::Mapping::new(); + mapping.insert( + Value::String("key".into()), + Value::String("value".into()), + ); + let value = Value::Mapping(mapping); + let index = Value::String("key".into()); + assert_eq!( + index.index_into(&value), + Some(&Value::String("value".into())) + ); + } + + /// Test `index_into` with a `Value` other than `Mapping`. + #[test] + fn test_value_index_into_non_mapping() { + let value = Value::String("hello".into()); + let index = Value::String("key".into()); + assert_eq!(index.index_into(&value), None); + } + + /// Test `index_into_mut` with a `Value::Mapping`. + #[test] + fn test_value_index_into_mut_mapping() { + let mut mapping = serde_yml::Mapping::new(); + mapping.insert( + Value::String("key".into()), + Value::String("value".into()), + ); + let mut value = Value::Mapping(mapping); + let index = Value::String("key".into()); + assert_eq!( + index.index_into_mut(&mut value), + Some(&mut Value::String("value".into())) + ); + } + + /// Test `index_into_mut` with a `Value` other than `Mapping`. + #[test] + fn test_value_index_into_mut_non_mapping() { + let mut value = Value::String("hello".into()); + let index = Value::String("key".into()); + assert_eq!(index.index_into_mut(&mut value), None); + } + + /// Test `index_or_insert` with a `Value::Mapping`. + #[test] + fn test_value_index_or_insert_mapping() { + let mut mapping = serde_yml::Mapping::new(); + mapping.insert( + Value::String("key".into()), + Value::String("value".into()), + ); + let mut value = Value::Mapping(mapping); + let index = Value::String("new_key".into()); + index + .index_or_insert(&mut value) + .clone_from(&Value::String("new_value".into())); + assert_eq!( + value.get(&Value::String("new_key".into())), + Some(&Value::String("new_value".into())) + ); + } + + /// Test `index_or_insert` with a `Value` other than `Mapping`. + #[test] + #[should_panic( + expected = "cannot access key String(\"key\") in YAML string" + )] + fn test_value_index_or_insert_non_mapping() { + let mut value = Value::String("hello".into()); + let index = Value::String("key".into()); + index.index_or_insert(&mut value); + } + + // Tests for the `str` implementation of `Index` + + /// Test `index_into` with a `Value::Mapping`. + #[test] + fn test_str_index_into_mapping() { + let mut mapping = serde_yml::Mapping::new(); + mapping.insert( + Value::String("key".into()), + Value::String("value".into()), + ); + let value = Value::Mapping(mapping); + let index = "key"; + assert_eq!( + index.index_into(&value), + Some(&Value::String("value".into())) + ); + } + + /// Test `index_into` with a `Value` other than `Mapping`. + #[test] + fn test_str_index_into_non_mapping() { + let value = Value::String("hello".into()); + let index = "key"; + assert_eq!(index.index_into(&value), None); + } + + /// Test `index_into_mut` with a `Value::Mapping`. + #[test] + fn test_str_index_into_mut_mapping() { + let mut mapping = serde_yml::Mapping::new(); + mapping.insert( + Value::String("key".into()), + Value::String("value".into()), + ); + let mut value = Value::Mapping(mapping); + let index = "key"; + assert_eq!( + index.index_into_mut(&mut value), + Some(&mut Value::String("value".into())) + ); + } + + /// Test `index_into_mut` with a `Value` other than `Mapping`. + #[test] + fn test_str_index_into_mut_non_mapping() { + let mut value = Value::String("hello".into()); + let index = "key"; + assert_eq!(index.index_into_mut(&mut value), None); + } + + /// Test `index_or_insert` with a `Value::Mapping`. + #[test] + fn test_str_index_or_insert_mapping() { + let mut mapping = serde_yml::Mapping::new(); + mapping.insert( + Value::String("key".into()), + Value::String("value".into()), + ); + let mut value = Value::Mapping(mapping); + let index = "new_key"; + index + .index_or_insert(&mut value) + .clone_from(&Value::String("new_value".into())); + assert_eq!( + value.get(&Value::String("new_key".into())), + Some(&Value::String("new_value".into())) + ); + } + + /// Test `index_or_insert` with a `Value` other than `Mapping`. + #[test] + #[should_panic( + expected = "cannot access key \"key\" in YAML string" + )] + fn test_str_index_or_insert_non_mapping() { + let mut value = Value::String("hello".into()); + let index = "key"; + index.index_or_insert(&mut value); + } + + // Tests for the `String` implementation of `Index` + + /// Test `index_into` with a `Value::Mapping`. + #[test] + fn test_string_index_into_mapping() { + let mut mapping = serde_yml::Mapping::new(); + mapping.insert( + Value::String("key".into()), + Value::String("value".into()), + ); + let value = Value::Mapping(mapping); + let index = String::from("key"); + assert_eq!( + index.index_into(&value), + Some(&Value::String("value".into())) + ); + } + + /// Test `index_into` with a `Value` other than `Mapping`. + #[test] + fn test_string_index_into_non_mapping() { + let value = Value::String("hello".into()); + let index = String::from("key"); + assert_eq!(index.index_into(&value), None); + } + + /// Test `index_into_mut` with a `Value::Mapping`. + #[test] + fn test_string_index_into_mut_mapping() { + let mut mapping = serde_yml::Mapping::new(); + mapping.insert( + Value::String("key".into()), + Value::String("value".into()), + ); + let mut value = Value::Mapping(mapping); + let index = String::from("key"); + assert_eq!( + index.index_into_mut(&mut value), + Some(&mut Value::String("value".into())) + ); + } + + /// Test `index_into_mut` with a `Value` other than `Mapping`. + #[test] + fn test_string_index_into_mut_non_mapping() { + let mut value = Value::String("hello".into()); + let index = String::from("key"); + assert_eq!(index.index_into_mut(&mut value), None); + } + + /// Test `index_or_insert` with a `Value::Mapping`. + #[test] + fn test_string_index_or_insert_mapping() { + let mut mapping = serde_yml::Mapping::new(); + mapping.insert( + Value::String("key".into()), + Value::String("value".into()), + ); + let mut value = Value::Mapping(mapping); + let index = String::from("new_key"); + index + .index_or_insert(&mut value) + .clone_from(&Value::String("new_value".into())); + assert_eq!( + value.get(&Value::String("new_key".into())), + Some(&Value::String("new_value".into())) + ); + } + + /// Test `index_or_insert` with a `Value` other than `Mapping`. + #[test] + #[should_panic( + expected = "cannot access key \"key\" in YAML string" + )] + fn test_string_index_or_insert_non_mapping() { + let mut value = Value::String("hello".into()); + let index = String::from("key"); + index.index_or_insert(&mut value); + } + + // Tests for the reference implementation of `Index` + + /// Test `index_into` with a reference to `usize`. + #[test] + fn test_ref_usize_index_into() { + let sequence = Value::Sequence(vec![ + Value::Number(1.into()), + Value::Number(2.into()), + ]); + let index = &1; + assert_eq!( + index.index_into(&sequence), + Some(&Value::Number(2.into())) + ); + } + + /// Test `index_into` with a reference to `Value`. + #[test] + fn test_ref_value_index_into() { + let mut mapping = serde_yml::Mapping::new(); + mapping.insert( + Value::String("key".into()), + Value::String("value".into()), + ); + let value = Value::Mapping(mapping); + let index = &Value::String("key".into()); + assert_eq!( + index.index_into(&value), + Some(&Value::String("value".into())) + ); + } + + /// Test `index_into` with a reference to `str`. + #[test] + fn test_ref_str_index_into() { + let mut mapping = serde_yml::Mapping::new(); + mapping.insert( + Value::String("key".into()), + Value::String("value".into()), + ); + let value = Value::Mapping(mapping); + let index = &"key"; + assert_eq!( + index.index_into(&value), + Some(&Value::String("value".into())) + ); + } + + /// Test `index_into` with a reference to `String`. + #[test] + fn test_ref_string_index_into() { + let mut mapping = serde_yml::Mapping::new(); + mapping.insert( + Value::String("key".into()), + Value::String("value".into()), + ); + let value = Value::Mapping(mapping); + let index = &String::from("key"); + assert_eq!( + index.index_into(&value), + Some(&Value::String("value".into())) + ); + } + + /// Test `index_into_mut` with a reference to `usize`. + #[test] + fn test_ref_usize_index_into_mut() { + let mut sequence = Value::Sequence(vec![ + Value::Number(1.into()), + Value::Number(2.into()), + ]); + let index = &1; + assert_eq!( + index.index_into_mut(&mut sequence), + Some(&mut Value::Number(2.into())) + ); + } + + /// Test `index_into_mut` with a reference to `Value`. + #[test] + fn test_ref_value_index_into_mut() { + let mut mapping = serde_yml::Mapping::new(); + mapping.insert( + Value::String("key".into()), + Value::String("value".into()), + ); + let mut value = Value::Mapping(mapping); + let index = &Value::String("key".into()); + assert_eq!( + index.index_into_mut(&mut value), + Some(&mut Value::String("value".into())) + ); + } + + /// Test `index_into_mut` with a reference to `str`. + #[test] + fn test_ref_str_index_into_mut() { + let mut mapping = serde_yml::Mapping::new(); + mapping.insert( + Value::String("key".into()), + Value::String("value".into()), + ); + let mut value = Value::Mapping(mapping); + let index = &"key"; + assert_eq!( + index.index_into_mut(&mut value), + Some(&mut Value::String("value".into())) + ); + } + + /// Test `index_into_mut` with a reference to `String`. + #[test] + fn test_ref_string_index_into_mut() { + let mut mapping = serde_yml::Mapping::new(); + mapping.insert( + Value::String("key".into()), + Value::String("value".into()), + ); + let mut value = Value::Mapping(mapping); + let index = &String::from("key"); + assert_eq!( + index.index_into_mut(&mut value), + Some(&mut Value::String("value".into())) + ); + } + + /// Test `index_or_insert` with a reference to `usize`. + #[test] + fn test_ref_usize_index_or_insert() { + let mut sequence = + Value::Sequence(vec![Value::Number(1.into())]); + let index = &1; + + // Extend the sequence to ensure the index is in bounds + if *index >= sequence.as_sequence().unwrap().len() { + for _ in sequence.as_sequence().unwrap().len()..=*index { + sequence.as_sequence_mut().unwrap().push(Value::Null); + } + } + + index + .index_or_insert(&mut sequence) + .clone_from(&Value::Number(2.into())); + assert_eq!( + sequence, + Value::Sequence(vec![ + Value::Number(1.into()), + Value::Number(2.into()) + ]) + ); + } + + /// Test `index_or_insert` with a reference to `Value`. + #[test] + fn test_ref_value_index_or_insert() { + let mut mapping = serde_yml::Mapping::new(); + mapping.insert( + Value::String("key".into()), + Value::String("value".into()), + ); + let mut value = Value::Mapping(mapping); + let index = &Value::String("new_key".into()); + index + .index_or_insert(&mut value) + .clone_from(&Value::String("new_value".into())); + assert_eq!( + value.get(&Value::String("new_key".into())), + Some(&Value::String("new_value".into())) + ); + } + + /// Test `index_or_insert` with a reference to `str`. + #[test] + fn test_ref_str_index_or_insert() { + let mut mapping = serde_yml::Mapping::new(); + mapping.insert( + Value::String("key".into()), + Value::String("value".into()), + ); + let mut value = Value::Mapping(mapping); + let index = &"new_key"; + index + .index_or_insert(&mut value) + .clone_from(&Value::String("new_value".into())); + assert_eq!( + value.get(&Value::String("new_key".into())), + Some(&Value::String("new_value".into())) + ); + } + + // Tests for the `ops::Index` implementation + + /// Test indexing with an invalid index. + #[test] + fn test_index_invalid_index() { + let value = Value::Sequence(vec![Value::Number(1.into())]); + assert_eq!(value[2], Value::Null); + } + + /// Test indexing with an invalid key. + #[test] + fn test_index_invalid_key() { + let mut mapping = serde_yml::Mapping::new(); + mapping.insert( + Value::String("key".into()), + Value::String("value".into()), + ); + let value = Value::Mapping(mapping); + assert_eq!(value["invalid"], Value::Null); + } + + // Tests for the `ops::IndexMut` implementation + + /// Test mutating with an invalid index. + #[test] + #[should_panic( + expected = "cannot access index 2 of YAML sequence of length 1" + )] + fn test_index_mut_invalid_index() { + let mut value = Value::Sequence(vec![Value::Number(1.into())]); + value[2] = Value::Number(2.into()); + } + + /// Test mutating with an invalid key. + #[test] + fn test_index_mut_invalid_key() { + let mut mapping = serde_yml::Mapping::new(); + mapping.insert( + Value::String("key".into()), + Value::String("value".into()), + ); + let mut value = Value::Mapping(mapping); + value["invalid"] = Value::String("new_value".into()); + + assert_eq!( + value, + Value::Mapping({ + let mut mapping = serde_yml::Mapping::new(); + mapping.insert( + Value::String("key".into()), + Value::String("value".into()), + ); + mapping.insert( + Value::String("invalid".into()), + Value::String("new_value".into()), + ); + mapping + }) + ); + } +}