Skip to content

Commit

Permalink
Add support for json lines
Browse files Browse the repository at this point in the history
  • Loading branch information
cactusdualcore committed Jul 18, 2024
1 parent 7618bc5 commit c444890
Show file tree
Hide file tree
Showing 7 changed files with 193 additions and 4 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Changelog

## v0.12.0 - 18.07.2024
- Support for [Json Lines](https://jsonlines.org/)

## v0.11.0 - 04.07.2024
- Update to Bevy 0.14
- Update `quick-xml` to `0.34`
Expand Down
12 changes: 9 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "bevy_common_assets"
version = "0.11.0"
version = "0.12.0"
authors = ["Niklas Eicker <git@nikl.me>"]
edition = "2021"
license = "MIT OR Apache-2.0"
Expand All @@ -17,6 +17,7 @@ ron = ["dep:serde_ron"]
toml = ["dep:serde_toml"]
yaml = ["dep:serde_yaml"]
json = ["dep:serde_json"]
jsonl = ["dep:serde_json"]
msgpack = ["dep:rmp-serde"]
xml = ["dep:quick-xml"]
csv = ["dep:csv"]
Expand All @@ -31,10 +32,10 @@ serde_json = { version = "1", optional = true }
rmp-serde = { version = "1", optional = true }
csv = { version = "1", optional = true }
thiserror = "1.0"
quick-xml = { version = "0.34", features = [ "serialize" ], optional = true }
quick-xml = { version = "0.34", features = ["serialize"], optional = true }
serde = { version = "1" }
anyhow = { version = "1" }
postcard = {version = "1.0", features = ["use-std"], optional = true}
postcard = { version = "1.0", features = ["use-std"], optional = true }

[dev-dependencies]
bevy = { version = "0.14.0-rc.4" }
Expand Down Expand Up @@ -74,6 +75,11 @@ name = "json"
path = "examples/json.rs"
required-features = ["json"]

[[example]]
name = "jsonl"
path = "examples/jsonl.rs"
required-features = ["jsonl"]

[[example]]
name = "xml"
path = "examples/xml.rs"
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Supported formats:
| `xml` | `xml` | [`xml.rs`](./examples/xml.rs) |
| `yaml` | `yaml` | [`yaml.rs`](./examples/yaml.rs) |
| `csv` | `csv` | [`csv.rs`](./examples/csv.rs) |
| `jsonl` | `jsonl` | [`jsonl.rs`](./examples/jsonl.rs) |

## Usage

Expand Down Expand Up @@ -49,6 +50,7 @@ fn main() {
.add_plugins((
DefaultPlugins,
JsonAssetPlugin::<Level>::new(&["level.json", "custom.json"]),
JsonLinesAssetPlugin::<Level>::new(&["level.jsonl", "custom.jsonl"]),
RonAssetPlugin::<Level>::new(&["level.ron"]),
MsgPackAssetPlugin::<Level>::new(&["level.msgpack"]),
PostcardAssetPlugin::<Level>::new(&["level.postcard"]),
Expand Down Expand Up @@ -79,7 +81,7 @@ Compatibility of `bevy_common_assets` versions:

| `bevy_common_assets` | `bevy` |
|:---------------------|:-------|
| `0.11` | `0.14` |
| `0.11` - `0.12` | `0.14` |
| `0.10` | `0.13` |
| `0.8` - `0.9` | `0.12` |
| `0.7` | `0.11` |
Expand Down
6 changes: 6 additions & 0 deletions assets/trees.level.jsonl
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[42.0, 42.0, 0.0]
[4.0, 32.0, 0.0]
[54.0, 7.0, 0.0]
[-61.0, 4.0, 0.0]
[-6.0, -72.0, 0.0]
[6.0, -89.0, 0.0]
74 changes: 74 additions & 0 deletions examples/jsonl.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
use bevy::prelude::*;
use bevy::reflect::TypePath;
use bevy_common_assets::jsonl::JsonLinesAssetPlugin;

fn main() {
App::new()
.add_plugins((
DefaultPlugins,
JsonLinesAssetPlugin::<Level, TreePosition>::new(&["level.jsonl"]),
))
.insert_resource(Msaa::Off)
.init_state::<AppState>()
.add_systems(Startup, setup)
.add_systems(Update, spawn_level.run_if(in_state(AppState::Loading)))
.run();
}

fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
let level = LevelHandle(asset_server.load("trees.level.jsonl"));
commands.insert_resource(level);
let tree = ImageHandle(asset_server.load("tree.png"));
commands.insert_resource(tree);

commands.spawn(Camera2dBundle::default());
}

fn spawn_level(
mut commands: Commands,
level: Res<LevelHandle>,
tree: Res<ImageHandle>,
mut levels: ResMut<Assets<Level>>,
mut state: ResMut<NextState<AppState>>,
) {
if let Some(level) = levels.remove(level.0.id()) {
for position in level.positions {
commands.spawn(SpriteBundle {
transform: Transform::from_translation(position.0.into()),
texture: tree.0.clone(),
..default()
});
}

state.set(AppState::Level);
}
}

#[derive(serde::Deserialize)]
#[serde(transparent)]
struct TreePosition([f32; 3]);

#[derive(serde::Deserialize, Asset, TypePath)]
struct Level {
positions: Vec<TreePosition>,
}

impl FromIterator<TreePosition> for Level {
fn from_iter<T: IntoIterator<Item = TreePosition>>(iter: T) -> Self {
let positions = Vec::from_iter(iter);
Self { positions }
}
}

#[derive(Debug, Clone, Copy, Default, Eq, PartialEq, Hash, States)]
enum AppState {
#[default]
Loading,
Level,
}

#[derive(Resource)]
struct ImageHandle(Handle<Image>);

#[derive(Resource)]
struct LevelHandle(Handle<Level>);
93 changes: 93 additions & 0 deletions src/jsonl.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
use bevy::app::{App, Plugin};
use bevy::asset::io::Reader;
use bevy::asset::{Asset, AssetApp, AssetLoader, AsyncReadExt, LoadContext};
use serde_json::from_str;
use std::io::BufRead;
use std::marker::PhantomData;
use thiserror::Error;

/// Plugin to load your asset type `A` via a list of ` from jsonl files.
pub struct JsonLinesAssetPlugin<A, D> {
extensions: Vec<&'static str>,
_marker: PhantomData<(A, D)>,
}

impl<A, D> Plugin for JsonLinesAssetPlugin<A, D>
where
for<'de> D: serde::Deserialize<'de> + Sync + Send + 'de,
for<'de> A: FromIterator<D> + Asset + Sync + Send + 'de,
{
fn build(&self, app: &mut App) {
app.init_asset::<A>()
.register_asset_loader(JsonLinesAssetLoader::<A, D> {
extensions: self.extensions.clone(),
_marker: PhantomData,
});
}
}

impl<A, D> JsonLinesAssetPlugin<A, D>
where
for<'de> D: serde::Deserialize<'de> + Sync + Send + 'de,
for<'de> A: FromIterator<D> + Asset + Sync + Send + 'de,
{
/// Create a new plugin that will load assets from files with the given extensions.
pub fn new(extensions: &[&'static str]) -> Self {
Self {
extensions: extensions.to_owned(),
_marker: PhantomData,
}
}
}

/// Loads your asset type `A` from jsonl files
pub struct JsonLinesAssetLoader<A, D> {
extensions: Vec<&'static str>,
_marker: PhantomData<(A, D)>,
}

/// Possible errors that can be produced by [`JsonLinesAssetLoader`]
#[non_exhaustive]
#[derive(Debug, Error)]
pub enum JsonLinesLoaderError {
/// An [IO Error](std::io::Error)
#[error("Could not read the file: {0}")]
Io(#[from] std::io::Error),
/// A [JSON Error](serde_json::error::Error)
#[error("Could not parse the JSON: {0}")]
JsonError(#[from] serde_json::error::Error),
}

impl<A, D> AssetLoader for JsonLinesAssetLoader<A, D>
where
for<'de> D: serde::Deserialize<'de> + Sync + Send + 'de,
for<'de> A: FromIterator<D> + Asset + Sync + Send + 'de,
{
type Asset = A;
type Settings = ();
type Error = JsonLinesLoaderError;

async fn load<'a>(
&'a self,
reader: &'a mut Reader<'_>,
_settings: &'a (),
_load_context: &'a mut LoadContext<'_>,
) -> Result<Self::Asset, Self::Error> {
let mut bytes = Vec::new();
reader.read_to_end(&mut bytes).await?;

if let Some(b'\n') = bytes.last() {
// Json Lines may optionally end with a line break.
let _ = bytes.pop();
}

bytes
.lines()
.map(|line| Ok::<D, JsonLinesLoaderError>(from_str(line?.as_str())?))
.collect()
}

fn extensions(&self) -> &[&str] {
&self.extensions
}
}
5 changes: 5 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ pub mod csv;
#[cfg_attr(docsrs, doc(cfg(feature = "json")))]
#[cfg(feature = "json")]
pub mod json;
/// Module containing a Bevy plugin to load assets from `jsonl` files with custom file extensions.
#[cfg_attr(docsrs, doc(cfg(feature = "jsonl")))]
#[cfg(feature = "jsonl")]
pub mod jsonl;
/// Module containing a Bevy plugin to load assets from `MessagePack` files with custom file extensions.
#[cfg_attr(docsrs, doc(cfg(feature = "msgpack")))]
#[cfg(feature = "msgpack")]
Expand Down Expand Up @@ -83,6 +87,7 @@ pub mod yaml;

#[cfg(all(
feature = "json",
feature = "jsonl",
feature = "msgpack",
feature = "ron",
feature = "toml",
Expand Down

0 comments on commit c444890

Please sign in to comment.