1use std::collections::HashMap;
2
3use btree_indexmap::{BTreeIndexMultiSet, BTreeIndexSet};
4use json_syntax::ryu_js;
5use rdf_syntax::{BlankIdBuf, Generator, Id};
6
7use crate::{
8 JsonLdLenient,
9 object::{JsonLdObjectMut, JsonLdObjectRef},
10};
11
12pub trait JsonLdVisit {
13 fn visit_with(&self, f: &mut impl FnMut(JsonLdObjectRef));
14
15 fn visit(&self, mut f: impl FnMut(JsonLdObjectRef)) {
16 self.visit_with(&mut f);
17 }
18
19 fn visit_mut_with(&mut self, f: &mut impl FnMut(JsonLdObjectMut));
20
21 fn visit_mut(&mut self, mut f: impl FnMut(JsonLdObjectMut)) {
22 self.visit_mut_with(&mut f);
23 }
24
25 fn relabel_with(&mut self, relabeling: &mut JsonLdRelabeling<impl Generator>) {
26 self.visit_mut(|object| {
27 relabel_object(object, relabeling);
28 })
29 }
30
31 fn relabel<G: Generator>(&mut self, generator: G) -> JsonLdRelabeling<G> {
32 let mut relabeling = JsonLdRelabeling::new(generator);
33 self.relabel_with(&mut relabeling);
34 relabeling
35 }
36
37 fn canonicalize_with(&mut self, buffer: &mut ryu_js::Buffer) {
38 self.visit_mut(|object| {
39 canonicalize_object(object, buffer);
40 });
41 }
42
43 fn canonicalize(&mut self) {
44 let mut buffer = ryu_js::Buffer::new();
45 self.canonicalize_with(&mut buffer);
46 }
47
48 fn relabel_and_canonicalize_with(
49 &mut self,
50 relabeling: &mut JsonLdRelabeling<impl Generator>,
51 buffer: &mut ryu_js::Buffer,
52 ) {
53 self.visit_mut(|mut object| {
54 relabel_object(object.reborrow(), relabeling);
55 canonicalize_object(object, buffer);
56 });
57 }
58
59 fn relabel_and_canonicalize<G: Generator>(&mut self, generator: G) -> JsonLdRelabeling<G> {
60 let mut relabeling = JsonLdRelabeling::new(generator);
61 let mut buffer = ryu_js::Buffer::new();
62 self.relabel_and_canonicalize_with(&mut relabeling, &mut buffer);
63 relabeling
64 }
65}
66
67fn relabel_object(object: JsonLdObjectMut, relabeling: &mut JsonLdRelabeling<impl Generator>) {
68 if let JsonLdObjectMut::Node(node) = object {
69 node.id = Some(relabeling.relabel(node.id.take()));
70
71 if let Some(types) = node.types.take() {
72 node.types = Some(
73 types
74 .into_iter()
75 .map(|ty| relabeling.relabel(Some(ty)))
76 .collect(),
77 )
78 }
79 }
80}
81
82fn canonicalize_object(object: JsonLdObjectMut, buffer: &mut ryu_js::Buffer) {
83 if let JsonLdObjectMut::Value(value) = object {
84 value.canonicalize_with(buffer);
85 }
86}
87
88impl<T> JsonLdVisit for [T]
89where
90 T: JsonLdVisit,
91{
92 fn visit_with(&self, f: &mut impl FnMut(JsonLdObjectRef)) {
93 for t in self {
94 t.visit_with(f);
95 }
96 }
97
98 fn visit_mut_with(&mut self, f: &mut impl FnMut(JsonLdObjectMut)) {
99 for t in self {
100 t.visit_mut_with(f);
101 }
102 }
103}
104
105impl<T> JsonLdVisit for BTreeIndexSet<T>
106where
107 T: JsonLdVisit + Ord,
108{
109 fn visit_with(&self, f: &mut impl FnMut(JsonLdObjectRef)) {
110 for t in self {
111 t.visit_with(f);
112 }
113 }
114
115 fn visit_mut_with(&mut self, f: &mut impl FnMut(JsonLdObjectMut)) {
116 for mut t in std::mem::take(self) {
117 t.visit_mut_with(f);
118 self.insert(t);
119 }
120 }
121}
122
123impl<T> JsonLdVisit for BTreeIndexMultiSet<T>
124where
125 T: JsonLdVisit + Ord,
126{
127 fn visit_with(&self, f: &mut impl FnMut(JsonLdObjectRef)) {
128 for t in self {
129 t.visit_with(f);
130 }
131 }
132
133 fn visit_mut_with(&mut self, f: &mut impl FnMut(JsonLdObjectMut)) {
134 for mut t in std::mem::take(self) {
135 t.visit_mut_with(f);
136 self.insert(t);
137 }
138 }
139}
140
141impl<T> JsonLdVisit for Option<T>
142where
143 T: JsonLdVisit,
144{
145 fn visit_with(&self, f: &mut impl FnMut(JsonLdObjectRef)) {
146 if let Some(t) = self {
147 t.visit_with(f);
148 }
149 }
150
151 fn visit_mut_with(&mut self, f: &mut impl FnMut(JsonLdObjectMut)) {
152 if let Some(t) = self {
153 t.visit_mut_with(f);
154 }
155 }
156}
157
158pub struct JsonLdRelabeling<G> {
159 generator: G,
160 map: HashMap<BlankIdBuf, Id>,
161}
162
163impl<G> JsonLdRelabeling<G> {
164 pub fn new(generator: G) -> Self {
165 Self {
166 generator,
167 map: HashMap::new(),
168 }
169 }
170}
171
172impl<G> JsonLdRelabeling<G>
173where
174 G: Generator,
175{
176 pub fn relabel(&mut self, id: Option<JsonLdLenient<Id>>) -> JsonLdLenient<Id> {
177 match id {
178 Some(JsonLdLenient::Valid(Id::BlankId(b))) => JsonLdLenient::Valid(
179 self.map
180 .entry(b)
181 .or_insert_with(|| self.generator.next_id())
182 .clone(),
183 ),
184 Some(id) => id,
185 None => JsonLdLenient::Valid(self.generator.next_id()),
186 }
187 }
188}