diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a8706399..3ade4428 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,6 +21,11 @@ jobs: steps: - uses: actions/checkout@v3 + - name: set up python + uses: actions/setup-python@v4 + with: + python-version: '3.11' + - uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust-version }} @@ -33,7 +38,7 @@ jobs: - run: rustup component add llvm-tools-preview - - run: cargo test --test main + - run: cargo test -F python env: RUST_BACKTRACE: 1 RUSTFLAGS: '-C instrument-coverage' diff --git a/.gitignore b/.gitignore index 43d9bf49..ed2fa4e5 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ /profiling/ /scratch/ /*.bm +/env*/ diff --git a/.rustfmt.toml b/.rustfmt.toml index 50f8a598..75306517 100644 --- a/.rustfmt.toml +++ b/.rustfmt.toml @@ -1,2 +1 @@ -imports_granularity = "Module" max_width = 120 diff --git a/Cargo.lock b/Cargo.lock index 9522e182..e694d826 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,6 +20,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "cfg-if" version = "1.0.0" @@ -65,6 +71,12 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "indoc" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa799dd5ed20a7e349f3b4639aa80d74549c81716d9ec4f994c9b5815598306" + [[package]] name = "itoa" version = "1.0.9" @@ -73,13 +85,14 @@ checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "jiter" -version = "0.0.1" +version = "0.0.2" dependencies = [ "ahash", "indexmap", "num-bigint", "num-traits", "paste", + "pyo3", "serde", "serde_json", "smallvec", @@ -93,6 +106,25 @@ version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +[[package]] +name = "lock_api" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + [[package]] name = "num-bigint" version = "0.4.4" @@ -129,6 +161,29 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + [[package]] name = "paste" version = "1.0.14" @@ -144,6 +199,67 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "pyo3" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e681a6cfdc4adcc93b4d3cf993749a4552018ee0a9b65fc0ccfad74352c72a38" +dependencies = [ + "cfg-if", + "indoc", + "libc", + "memoffset", + "num-bigint", + "parking_lot", + "pyo3-build-config", + "pyo3-ffi", + "pyo3-macros", + "unindent", +] + +[[package]] +name = "pyo3-build-config" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "076c73d0bc438f7a4ef6fdd0c3bb4732149136abd952b110ac93e4edb13a6ba5" +dependencies = [ + "once_cell", + "target-lexicon", +] + +[[package]] +name = "pyo3-ffi" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e53cee42e77ebe256066ba8aa77eff722b3bb91f3419177cf4cd0f304d3284d9" +dependencies = [ + "libc", + "pyo3-build-config", +] + +[[package]] +name = "pyo3-macros" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfeb4c99597e136528c6dd7d5e3de5434d1ceaf487436a3f03b2d56b6fc9efd1" +dependencies = [ + "proc-macro2", + "pyo3-macros-backend", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "pyo3-macros-backend" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "947dc12175c254889edc0c02e399476c2f652b4b9ebd123aa655c224de259536" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "quote" version = "1.0.33" @@ -153,6 +269,15 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags", +] + [[package]] name = "rustversion" version = "1.0.14" @@ -165,6 +290,12 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "serde" version = "1.0.188" @@ -182,7 +313,7 @@ checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.31", ] [[package]] @@ -222,7 +353,18 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn", + "syn 2.0.31", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", ] [[package]] @@ -236,12 +378,24 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "target-lexicon" +version = "0.12.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d0e916b1148c8e263850e1ebcbd046f333e0683c724876bb0da63ea4373dc8a" + [[package]] name = "unicode-ident" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" +[[package]] +name = "unindent" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1766d682d402817b5ac4490b3c3002d91dfa0d22812f341609f97b08757359c" + [[package]] name = "version_check" version = "0.9.4" @@ -253,3 +407,60 @@ name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" diff --git a/Cargo.toml b/Cargo.toml index ede21049..1f6e0b88 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "jiter" -version = "0.0.1" +version = "0.0.2" edition = "2021" description = "Iterable JSON parser" readme = "README.md" @@ -18,11 +18,16 @@ num-bigint = "0.4.3" num-traits = "0.2.16" ahash = "0.8.0" smallvec = "1.11.0" +pyo3 = { version = "0.19.2", features = ["num-bigint"], optional = true } + +[features] +python = ["dep:pyo3"] [dev-dependencies] paste = "1.0.7" serde_json = {version = "1.0.87", features = ["preserve_order", "arbitrary_precision"]} serde = "1.0.147" +pyo3 = { version = "0.19.2", features = ["num-bigint", "auto-initialize"] } [profile.bench] lto = true diff --git a/src/value.rs b/src/value.rs index b57e9ef0..102ea4f8 100644 --- a/src/value.rs +++ b/src/value.rs @@ -20,6 +20,28 @@ pub enum JsonValue { Object(Box>), } +#[cfg(feature = "python")] +impl pyo3::ToPyObject for JsonValue { + fn to_object(&self, py: pyo3::Python<'_>) -> pyo3::PyObject { + match self { + Self::Null => py.None(), + Self::Bool(b) => b.to_object(py), + Self::Int(i) => i.to_object(py), + Self::BigInt(b) => b.to_object(py), + Self::Float(f) => f.to_object(py), + Self::String(s) => s.to_object(py), + Self::Array(v) => pyo3::types::PyList::new(py, v.iter().map(|v| v.to_object(py))).to_object(py), + Self::Object(o) => { + let dict = pyo3::types::PyDict::new(py); + for (k, v) in o.iter() { + dict.set_item(k, v.to_object(py)).unwrap(); + } + dict.to_object(py) + } + } + } +} + impl JsonValue { pub fn parse(data: &[u8]) -> Result { let mut parser = Parser::new(data); diff --git a/tests/test_python.rs b/tests/test_python.rs new file mode 100644 index 00000000..a1611e99 --- /dev/null +++ b/tests/test_python.rs @@ -0,0 +1,28 @@ +use pyo3::prelude::*; +use pyo3::ToPyObject; + +use jiter::JsonValue; + +#[test] +fn test_to_py_object_numeric() { + let value = + JsonValue::parse(br#" { "int": 1, "bigint": 123456789012345678901234567890, "float": 1.2} "#).unwrap(); + Python::with_gil(|py| { + let python_value = value.to_object(py); + let string = python_value.as_ref(py).to_string(); + assert_eq!( + string, + "{'int': 1, 'bigint': 123456789012345678901234567890, 'float': 1.2}" + ); + }) +} + +#[test] +fn test_to_py_object_other() { + let value = JsonValue::parse(br#"["string", true, false, null]"#).unwrap(); + Python::with_gil(|py| { + let python_value = value.to_object(py); + let string = python_value.as_ref(py).to_string(); + assert_eq!(string, "['string', True, False, None]"); + }) +}