Skip to content

Commit

Permalink
Treat Item::custom as a free-form object
Browse files Browse the repository at this point in the history
  • Loading branch information
SamWilsn committed Feb 21, 2025
1 parent 0c6ab57 commit 446fd86
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 13 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ jobs:
git clone --depth 1 https://github.com/citation-style-language/locales
- uses: Swatinem/rust-cache@v2
- run: cargo build
- run: cargo test
- run: cargo test --no-default-features
- run: cargo test --all-features

checks:
name: Check clippy, formatting, and documentation
Expand Down
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@ exclude = ["assets/*"]

[features]
default = []
json = ["unscanny"] # adds support for CSL-json parsing
json = ["unscanny", "serde_json" ] # adds support for CSL-json parsing

[dependencies]
quick-xml = { version = "0.36.2", features = ["serialize", "overlapped-lists"] }
serde = { version = "1.0", features = ["derive"] }
unscanny = { version = "0.1.0", optional = true }
serde_json = { version = "1.0.107", optional = true }

[dev-dependencies]
ciborium = "0.2.1"
Expand Down
60 changes: 49 additions & 11 deletions src/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,45 @@ use std::{collections::BTreeMap, str::FromStr};
use serde::{Deserialize, Serialize};
use unscanny::Scanner;

/// Potential values of [`Item::custom`].
pub type CustomValue = serde_json::Value;

/// A free-form object storing custom key-value pairs.
pub type Custom = serde_json::Map<String, CustomValue>;

/// A CSL-JSON item.
#[derive(Debug, Serialize, Deserialize, Hash, PartialEq, Eq)]
#[serde(transparent)]
pub struct Item(pub BTreeMap<String, Value>);
pub struct Item {
/// Custom key-value pairs.
///
/// Used to store additional information that does not have a designated CSL JSON field.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub custom: Option<Custom>,

/// All fields of the item except for [`custom`][Self::custom].
#[serde(flatten)]
pub fields: BTreeMap<String, Value>,
}

impl Item {
/// The item's ID.
pub fn id(&self) -> Option<Cow<str>> {
self.0.get("id")?.to_str()
self.fields.get("id")?.to_str()
}

/// The item type.
pub fn type_(&self) -> Option<Cow<str>> {
self.0.get("type")?.to_str()
self.fields.get("type")?.to_str()
}

/// Whether any of the fields values contains any HTML.
pub fn has_html(&self) -> bool {
self.0.values().any(|v| v.has_html())
self.fields.values().any(|v| v.has_html())
}

/// Whether this entry may contain "cheater syntax" for odd fields.
pub fn may_have_hack(&self) -> bool {
self.0.contains_key("note")
self.fields.contains_key("note")
}
}

Expand Down Expand Up @@ -459,9 +474,9 @@ mod tests {

#[test]
fn test_serialize() {
let mut map = BTreeMap::new();
map.insert("title".to_string(), Value::String("The Title".to_string()));
map.insert(
let mut fields = BTreeMap::new();
fields.insert("title".to_string(), Value::String("The Title".to_string()));
fields.insert(
"author".to_string(),
Value::Names(vec![NameValue::Item(NameItem {
family: "Doe".to_string(),
Expand All @@ -471,7 +486,7 @@ mod tests {
suffix: None,
})]),
);
map.insert(
fields.insert(
"date".to_string(),
Value::Date(DateValue::Raw {
raw: FixedDateRange::from_str("2021-09-10/2022-01-01").unwrap(),
Expand All @@ -480,7 +495,30 @@ mod tests {
}),
);

let item = Item(map);
let item = Item { custom: None, fields };
println!("{}", serde_json::to_string_pretty(&item).unwrap());
}

#[test]
fn test_roundtrip_custom() {
let mut fields = BTreeMap::new();
fields.insert("foo".into(), Value::String("bar".into()));

let custom = serde_json::json! {{
"bool": true,
"float": 35.6,
"null": null,
}}
.as_object()
.cloned()
.unwrap()
.into();

let item = Item { custom, fields };

let serialized = serde_json::to_string(&item).unwrap();
let deserialized: Item = serde_json::from_str(&serialized).unwrap();

assert_eq!(deserialized, item);
}
}

0 comments on commit 446fd86

Please sign in to comment.