diff --git a/.codespellignore b/.codespellignore
index 9ac17d5..d84f1c4 100644
--- a/.codespellignore
+++ b/.codespellignore
@@ -1 +1,2 @@
crate
+ser
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index ecfb4ed..6c6a9a6 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -26,7 +26,7 @@ repos:
--ignore-words=.codespellignore
]
# We cannot fix spelling errors in past commit message without force-push.
- exclude: ^CHANGELOG.md$
+ exclude: ^(CHANGELOG\.md|data/.*)$
- repo: https://github.com/doublify/pre-commit-rust
rev: v1.0
hooks:
@@ -56,6 +56,7 @@ repos:
rev: v0.0.4
hooks:
- id: sourceheaders
+ exclude: ^data/.*$
- repo: https://github.com/jorisroovers/gitlint
rev: v0.19.1
hooks:
diff --git a/Cargo.lock b/Cargo.lock
index a1388b3..5d9a738 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1,6 +1,6 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
-version = 3
+version = 4
[[package]]
name = "aho-corasick"
@@ -11,6 +11,21 @@ dependencies = [
"memchr",
]
+[[package]]
+name = "android-tzdata"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
[[package]]
name = "anstream"
version = "0.6.18"
@@ -52,11 +67,12 @@ dependencies = [
[[package]]
name = "anstyle-wincon"
-version = "3.0.6"
+version = "3.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125"
+checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e"
dependencies = [
"anstyle",
+ "once_cell",
"windows-sys",
]
@@ -66,6 +82,12 @@ version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d62b7694a562cdf5a74227903507c56ab2cc8bdd1f781ed5cb4cf9c9f810bfc"
+[[package]]
+name = "autocfg"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
+
[[package]]
name = "binrw"
version = "0.14.1"
@@ -90,17 +112,52 @@ dependencies = [
"syn 1.0.109",
]
+[[package]]
+name = "bumpalo"
+version = "3.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
+
[[package]]
name = "bytemuck"
-version = "1.19.0"
+version = "1.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d"
+checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3"
+
+[[package]]
+name = "cc"
+version = "1.2.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13208fcbb66eaeffe09b99fffbe1af420f00a7b35aa99ad683dfc1aa76145229"
+dependencies = [
+ "shlex",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "chrono"
+version = "0.4.39"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825"
+dependencies = [
+ "android-tzdata",
+ "iana-time-zone",
+ "js-sys",
+ "num-traits",
+ "wasm-bindgen",
+ "windows-targets",
+]
[[package]]
name = "clap"
-version = "4.5.26"
+version = "4.5.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a8eb5e908ef3a6efbe1ed62520fb7287959888c88485abe072543190ecc66783"
+checksum = "769b0145982b4b48713e01ec42d61614425f27b7058bda7180a3a41f30104796"
dependencies = [
"clap_builder",
"clap_derive",
@@ -108,9 +165,9 @@ dependencies = [
[[package]]
name = "clap_builder"
-version = "4.5.26"
+version = "4.5.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "96b01801b5fc6a0a232407abc821660c9c6d25a1cafc0d4f85f29fb8d9afc121"
+checksum = "1b26884eb4b57140e4d2d93652abfa49498b938b3c9179f9fc487b0acc3edad7"
dependencies = [
"anstream",
"anstyle",
@@ -127,7 +184,7 @@ dependencies = [
"heck",
"proc-macro2",
"quote",
- "syn 2.0.87",
+ "syn 2.0.96",
]
[[package]]
@@ -142,6 +199,12 @@ version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
+
[[package]]
name = "crc16"
version = "0.4.0"
@@ -172,12 +235,57 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+[[package]]
+name = "iana-time-zone"
+version = "0.1.61"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "wasm-bindgen",
+ "windows-core",
+]
+
+[[package]]
+name = "iana-time-zone-haiku"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
+dependencies = [
+ "cc",
+]
+
[[package]]
name = "is_terminal_polyfill"
version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
+[[package]]
+name = "js-sys"
+version = "0.3.77"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
+dependencies = [
+ "once_cell",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.169"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
+
+[[package]]
+name = "log"
+version = "0.4.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f"
+
[[package]]
name = "memchr"
version = "2.7.4"
@@ -205,6 +313,21 @@ dependencies = [
"syn 1.0.109",
]
+[[package]]
+name = "num-traits"
+version = "0.2.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.20.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
+
[[package]]
name = "owo-colors"
version = "3.5.0"
@@ -233,7 +356,7 @@ dependencies = [
"regex",
"regex-syntax",
"structmeta",
- "syn 2.0.87",
+ "syn 2.0.96",
]
[[package]]
@@ -254,18 +377,28 @@ dependencies = [
[[package]]
name = "proc-macro2"
-version = "1.0.89"
+version = "1.0.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
+checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
dependencies = [
"unicode-ident",
]
+[[package]]
+name = "quick-xml"
+version = "0.37.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "165859e9e55f79d67b96c5d96f4e88b6f2695a1972849c15a6a3f5c59fc2c003"
+dependencies = [
+ "memchr",
+ "serde",
+]
+
[[package]]
name = "quote"
-version = "1.0.37"
+version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
+checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
dependencies = [
"proc-macro2",
]
@@ -304,6 +437,7 @@ name = "rekordcrate"
version = "0.2.1"
dependencies = [
"binrw",
+ "chrono",
"clap",
"crc16",
"glob",
@@ -311,9 +445,43 @@ dependencies = [
"parse-display",
"pretty-hex",
"pretty_assertions",
+ "quick-xml",
+ "serde",
"thiserror",
]
+[[package]]
+name = "rustversion"
+version = "1.0.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4"
+
+[[package]]
+name = "serde"
+version = "1.0.217"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.217"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.96",
+]
+
+[[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
[[package]]
name = "static_assertions"
version = "1.1.0"
@@ -335,7 +503,7 @@ dependencies = [
"proc-macro2",
"quote",
"structmeta-derive",
- "syn 2.0.87",
+ "syn 2.0.96",
]
[[package]]
@@ -346,7 +514,7 @@ checksum = "152a0b65a590ff6c3da95cabe2353ee04e6167c896b28e3b14478c2636c922fc"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.87",
+ "syn 2.0.96",
]
[[package]]
@@ -362,9 +530,9 @@ dependencies = [
[[package]]
name = "syn"
-version = "2.0.87"
+version = "2.0.96"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d"
+checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80"
dependencies = [
"proc-macro2",
"quote",
@@ -388,14 +556,14 @@ checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.87",
+ "syn 2.0.96",
]
[[package]]
name = "unicode-ident"
-version = "1.0.13"
+version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
+checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
[[package]]
name = "utf8parse"
@@ -403,6 +571,73 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+ "rustversion",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
+dependencies = [
+ "bumpalo",
+ "log",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.96",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.96",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "windows-core"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
+dependencies = [
+ "windows-targets",
+]
+
[[package]]
name = "windows-sys"
version = "0.59.0"
diff --git a/Cargo.toml b/Cargo.toml
index 941bd1f..519d2c1 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -18,7 +18,9 @@ crc16 = "0.4"
clap = { version = "4.5", features = ["derive"], optional = true }
parse-display = "0.10"
thiserror = "2.0"
-
+quick-xml = { version = "0.37.2", features = ["serialize", "serde-types"] }
+serde = { version = "1.0", features = ["derive"] }
+chrono = "0.4"
[build-dependencies]
glob = "0.3"
diff --git a/build.rs b/build.rs
index 6e02d04..74b2629 100644
--- a/build.rs
+++ b/build.rs
@@ -77,4 +77,13 @@ use rekordcrate::setting::Setting;
"#,
"./tests/tests_setting.rs.in"
);
+
+ write_test!(
+ out_dir.join("tests_xml.rs"),
+ "data/xml/*.xml",
+ r#"// THIS FILE IS AUTOGENERATED - DO NOT EDIT!
+use rekordcrate::xml::Document;
+"#,
+ "./tests/tests_xml.rs.in"
+ );
}
diff --git a/data/xml/database.xml b/data/xml/database.xml
new file mode 100644
index 0000000..dd29426
--- /dev/null
+++ b/data/xml/database.xml
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/lib.rs b/src/lib.rs
index 444fc4b..d4530fd 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -27,6 +27,7 @@ pub mod anlz;
pub mod pdb;
pub mod setting;
pub mod util;
+pub mod xml;
pub(crate) mod xor;
pub use crate::util::RekordcrateError as Error;
diff --git a/src/main.rs b/src/main.rs
index 5040619..b4f66cb 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -11,6 +11,7 @@ use clap::{Parser, Subcommand};
use rekordcrate::anlz::ANLZ;
use rekordcrate::pdb::{Header, PageType, Row};
use rekordcrate::setting::Setting;
+use rekordcrate::xml::Document;
use std::path::PathBuf;
#[derive(Parser)]
@@ -47,6 +48,12 @@ enum Commands {
#[arg(value_name = "SETTING_FILE")]
path: PathBuf,
},
+ /// Parse and dump a Pioneer XML (`*.xml`) file.
+ DumpXML {
+ /// File to parse.
+ #[arg(value_name = "XML_FILE")]
+ path: PathBuf,
+ },
}
fn list_playlists(path: &PathBuf) -> rekordcrate::Result<()> {
@@ -159,6 +166,15 @@ fn dump_setting(path: &PathBuf) -> rekordcrate::Result<()> {
Ok(())
}
+fn dump_xml(path: &PathBuf) -> rekordcrate::Result<()> {
+ let file = std::fs::File::open(path)?;
+ let reader = std::io::BufReader::new(file);
+ let document: Document = quick_xml::de::from_reader(reader).expect("failed to deserialize XML");
+ println!("{:#?}", document);
+
+ Ok(())
+}
+
fn main() -> rekordcrate::Result<()> {
let cli = Cli::parse();
@@ -167,5 +183,6 @@ fn main() -> rekordcrate::Result<()> {
Commands::DumpPDB { path } => dump_pdb(path),
Commands::DumpANLZ { path } => dump_anlz(path),
Commands::DumpSetting { path } => dump_setting(path),
+ Commands::DumpXML { path } => dump_xml(path),
}
}
diff --git a/src/xml.rs b/src/xml.rs
new file mode 100644
index 0000000..1ef2422
--- /dev/null
+++ b/src/xml.rs
@@ -0,0 +1,454 @@
+// Copyright (c) 2025 Jan Holthuis
+//
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy
+// of the MPL was not distributed with this file, You can obtain one at
+// http://mozilla.org/MPL/2.0/.
+//
+// SPDX-License-Identifier: MPL-2.0
+
+//! Parser for the Rekordbox XML file format for playlists sharing.
+//!
+//! The XML format includes all playlists information.
+//!
+//! # References
+//!
+//! -
+//! -
+//! -
+type NaiveDate = String; //Replace with "use chrono::naive::NaiveDate;"
+use serde::{de::Error, ser::Serializer, Deserialize, Serialize};
+use std::borrow::Cow;
+
+/// The XML root element of a rekordbox XML file.
+#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
+#[serde(rename = "DJ_PLAYLISTS")]
+pub struct Document {
+ /// Version of the XML format for share the playlists.
+ ///
+ /// The latest version is 1,0,0.
+ #[serde(rename = "@Version")]
+ version: String,
+ #[serde(rename = "PRODUCT")]
+ product: Product,
+ #[serde(rename = "COLLECTION")]
+ collection: Collection,
+ #[serde(rename = "PLAYLISTS")]
+ playlists: Playlists,
+}
+
+#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
+struct Product {
+ /// Name of product
+ ///
+ /// This name will be displayed in each application software.
+ #[serde(rename = "@Name")]
+ name: String,
+ /// Version of application
+ #[serde(rename = "@Version")]
+ version: String,
+ /// Name of company
+ #[serde(rename = "@Company")]
+ company: String,
+}
+
+/// The information of the tracks who are not included in any playlist are unnecessary.
+#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
+struct Collection {
+ /// Number of TRACK in COLLECTION
+ #[serde(rename = "@Entries")]
+ entries: i32,
+ #[serde(rename = "TRACK")]
+ track: Vec