Skip to content

Commit bb7b03f

Browse files
committed
rename merge -> intersect and have it take ownership
1 parent 0e971cc commit bb7b03f

File tree

1 file changed

+84
-82
lines changed

1 file changed

+84
-82
lines changed

parser/src/json/schema.rs

Lines changed: 84 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -434,7 +434,7 @@ fn compile_contents_inner(ctx: &Context, contents: &Value) -> Result<Schema> {
434434
.iter()
435435
.map(|value| compile_resource(&ctx, ctx.as_resource_ref(value)))
436436
.collect::<Result<Vec<_>>>()?;
437-
let merged = merge(options.iter().chain(vec![&siblings]).collect())?;
437+
let merged = intersect(options.into_iter().chain(vec![siblings]).collect())?;
438438
return Ok(merged);
439439
}
440440

@@ -448,8 +448,9 @@ fn compile_contents_inner(ctx: &Context, contents: &Value) -> Result<Schema> {
448448
return Ok(siblings);
449449
}
450450
let options = any_of
451-
.iter()
451+
.into_iter()
452452
.map(|value| compile_resource(&ctx, ctx.as_resource_ref(value)))
453+
.map(|res| res.and_then(|schema| intersect_two(schema, siblings.clone())))
453454
.collect::<Result<Vec<_>>>()?;
454455
return Ok(Schema::AnyOf { options });
455456
}
@@ -465,8 +466,9 @@ fn compile_contents_inner(ctx: &Context, contents: &Value) -> Result<Schema> {
465466
return Ok(siblings);
466467
}
467468
let options = one_of
468-
.iter()
469+
.into_iter()
469470
.map(|value| compile_resource(&ctx, ctx.as_resource_ref(value)))
471+
.map(|res| res.and_then(|schema| intersect_two(schema, siblings.clone())))
470472
.collect::<Result<Vec<_>>>()?;
471473
return Ok(Schema::OneOf { options });
472474
}
@@ -496,7 +498,7 @@ fn compile_contents_inner(ctx: &Context, contents: &Value) -> Result<Schema> {
496498
}
497499
let resource = ctx.lookup_resource(&reference)?;
498500
let resolved_schema = compile_resource(ctx, resource)?;
499-
return Ok(merge_two(&siblings, &resolved_schema)?);
501+
return Ok(intersect_two(resolved_schema, siblings)?);
500502
}
501503
}
502504

@@ -740,24 +742,21 @@ fn compile_object(
740742
})
741743
}
742744

743-
fn merge(schemas: Vec<&Schema>) -> Result<Schema> {
744-
if schemas.is_empty() {
745-
bail!("merge called with empty list")
746-
}
747-
if schemas.iter().all(|schema| matches!(schema, Schema::Any)) {
748-
return Ok(Schema::Any);
749-
}
750-
if let Some(unsat) = schemas
751-
.iter()
752-
.find(|schema| matches!(schema, Schema::Unsatisfiable { .. }))
753-
{
754-
// Return the first unsatisfiable schema for debug-ability
755-
return Ok((*unsat).to_owned());
745+
fn intersect(schemas: Vec<Schema>) -> Result<Schema> {
746+
let (schemas, unsatisfiable) = schemas
747+
.into_iter()
748+
// "Any" schemas can be ignored
749+
.filter(|schema| !matches!(schema, Schema::Any))
750+
// Split into unsatisfiable and satisfiable schemas
751+
.partition::<Vec<_>, _>(|schema| !matches!(schema, Schema::Unsatisfiable { .. }));
752+
753+
if let Some(schema) = unsatisfiable.into_iter().next() {
754+
return Ok(schema);
756755
}
757-
// TODO: can we avoid cloning here?
758-
let mut merged = schemas[0].to_owned();
759-
for subschema in &schemas[1..] {
760-
merged = merge_two(&merged, subschema)?;
756+
757+
let mut merged = Schema::Any;
758+
for subschema in schemas.into_iter() {
759+
merged = intersect_two(merged, subschema)?;
761760
if matches!(merged, Schema::Unsatisfiable { .. }) {
762761
// Early exit if the schema is already unsatisfiable
763762
break;
@@ -766,36 +765,36 @@ fn merge(schemas: Vec<&Schema>) -> Result<Schema> {
766765
Ok(merged)
767766
}
768767

769-
fn merge_two(schema0: &Schema, schema1: &Schema) -> Result<Schema> {
768+
fn intersect_two(schema0: Schema, schema1: Schema) -> Result<Schema> {
770769
match (schema0, schema1) {
771-
(Schema::Any, _) => Ok(schema1.to_owned()),
772-
(_, Schema::Any) => Ok(schema0.to_owned()),
773-
(Schema::Unsatisfiable { .. }, _) => Ok(schema0.to_owned()),
774-
(_, Schema::Unsatisfiable { .. }) => Ok(schema1.to_owned()),
770+
(Schema::Any, schema1) => Ok(schema1),
771+
(schema0, Schema::Any) => Ok(schema0),
772+
(Schema::Unsatisfiable { reason }, _) => Ok(Schema::Unsatisfiable { reason }),
773+
(_, Schema::Unsatisfiable { reason }) => Ok(Schema::Unsatisfiable { reason }),
775774
(Schema::Ref { .. }, _) => Err(anyhow!("$ref with siblings not implemented")),
776775
(_, Schema::Ref { .. }) => Err(anyhow!("$ref with siblings not implemented")),
777-
(Schema::OneOf { options }, _) => Ok(Schema::OneOf {
776+
(Schema::OneOf { options }, schema1) => Ok(Schema::OneOf {
778777
options: options
779-
.iter()
780-
.map(|opt| merge_two(opt, schema1))
778+
.into_iter()
779+
.map(|opt| intersect_two(opt, schema1.clone()))
781780
.collect::<Result<Vec<_>>>()?,
782781
}),
783-
(_, Schema::OneOf { options }) => Ok(Schema::OneOf {
782+
(schema0, Schema::OneOf { options }) => Ok(Schema::OneOf {
784783
options: options
785-
.iter()
786-
.map(|opt| merge_two(schema0, opt))
784+
.into_iter()
785+
.map(|opt| intersect_two(schema0.clone(), opt))
787786
.collect::<Result<Vec<_>>>()?,
788787
}),
789-
(Schema::AnyOf { options }, _) => Ok(Schema::AnyOf {
788+
(Schema::AnyOf { options }, schema1) => Ok(Schema::AnyOf {
790789
options: options
791-
.iter()
792-
.map(|opt| merge_two(opt, schema1))
790+
.into_iter()
791+
.map(|opt| intersect_two(opt, schema1.clone()))
793792
.collect::<Result<Vec<_>>>()?,
794793
}),
795-
(_, Schema::AnyOf { options }) => Ok(Schema::AnyOf {
794+
(schema0, Schema::AnyOf { options }) => Ok(Schema::AnyOf {
796795
options: options
797-
.iter()
798-
.map(|opt| merge_two(schema0, opt))
796+
.into_iter()
797+
.map(|opt| intersect_two(schema0.clone(), opt))
799798
.collect::<Result<Vec<_>>>()?,
800799
}),
801800
(Schema::Null, Schema::Null) => Ok(Schema::Null),
@@ -816,11 +815,11 @@ fn merge_two(schema0: &Schema, schema1: &Schema) -> Result<Schema> {
816815
integer: int2,
817816
},
818817
) => Ok(Schema::Number {
819-
minimum: opt_max(*min1, *min2),
820-
maximum: opt_min(*max1, *max2),
821-
exclusive_minimum: opt_max(*emin1, *emin2),
822-
exclusive_maximum: opt_min(*emax1, *emax2),
823-
integer: *int1 || *int2,
818+
minimum: opt_max(min1, min2),
819+
maximum: opt_min(max1, max2),
820+
exclusive_minimum: opt_max(emin1, emin2),
821+
exclusive_maximum: opt_min(emax1, emax2),
822+
integer: int1 || int2,
824823
}),
825824
(
826825
Schema::String {
@@ -836,27 +835,27 @@ fn merge_two(schema0: &Schema, schema1: &Schema) -> Result<Schema> {
836835
format: format2,
837836
},
838837
) => Ok(Schema::String {
839-
min_length: *min1.max(min2),
840-
max_length: opt_min(*max1, *max2),
838+
min_length: min1.max(min2),
839+
max_length: opt_min(max1, max2),
841840
pattern: match (pattern1, pattern2) {
842841
(None, None) => None,
843-
(None, Some(r)) => Some(r.clone()),
844-
(Some(r), None) => Some(r.clone()),
842+
(None, Some(r)) => Some(r),
843+
(Some(r), None) => Some(r),
845844
(Some(r1), Some(r2)) => {
846845
if r1 == r2 {
847-
Some(r1.clone())
846+
Some(r1)
848847
} else {
849848
bail!("intersection of patterns not implemented")
850849
}
851850
}
852851
},
853852
format: match (format1, format2) {
854853
(None, None) => None,
855-
(None, Some(fmt)) => Some(fmt.clone()),
856-
(Some(fmt), None) => Some(fmt.clone()),
854+
(None, Some(fmt)) => Some(fmt),
855+
(Some(fmt), None) => Some(fmt),
857856
(Some(fmt1), Some(fmt2)) => {
858857
if fmt1 == fmt2 {
859-
Some(fmt1.clone())
858+
Some(fmt1)
860859
} else {
861860
bail!("intersection of formats not implemented")
862861
}
@@ -867,32 +866,33 @@ fn merge_two(schema0: &Schema, schema1: &Schema) -> Result<Schema> {
867866
Schema::Array {
868867
min_items: min1,
869868
max_items: max1,
870-
prefix_items: prefix1,
869+
prefix_items: mut prefix1,
871870
items: items1,
872871
},
873872
Schema::Array {
874873
min_items: min2,
875874
max_items: max2,
876-
prefix_items: prefix2,
875+
prefix_items: mut prefix2,
877876
items: items2,
878877
},
879878
) => Ok(Schema::Array {
880-
min_items: *min1.max(min2),
881-
max_items: opt_min(*max1, *max2),
882-
prefix_items: zip_default(
883-
prefix1,
884-
prefix2,
885-
items2.as_deref().unwrap_or(&Schema::Any),
886-
items1.as_deref().unwrap_or(&Schema::Any),
887-
)
888-
.iter()
889-
.map(|(item1, item2)| merge_two(item1, item2))
890-
.collect::<Result<Vec<Schema>>>()?,
879+
min_items: min1.max(min2),
880+
max_items: opt_min(max1, max2),
881+
prefix_items: {
882+
let len = prefix1.len().max(prefix2.len());
883+
prefix1.resize(len, items2.as_deref().cloned().unwrap_or(Schema::Any));
884+
prefix2.resize(len, items1.as_deref().cloned().unwrap_or(Schema::Any));
885+
prefix1
886+
.into_iter()
887+
.zip(prefix2.into_iter())
888+
.map(|(item1, item2)| intersect_two(item1, item2))
889+
.collect::<Result<Vec<_>>>()?
890+
},
891891
items: match (items1, items2) {
892892
(None, None) => None,
893-
(None, Some(item)) => Some(Box::new(*item.clone())),
894-
(Some(item), None) => Some(Box::new(*item.clone())),
895-
(Some(item1), Some(item2)) => Some(Box::new(merge_two(&item1, &item2)?)),
893+
(None, Some(item)) => Some(item),
894+
(Some(item), None) => Some(item),
895+
(Some(item1), Some(item2)) => Some(Box::new(intersect_two(*item1, *item2)?)),
896896
},
897897
}),
898898
(
@@ -902,32 +902,34 @@ fn merge_two(schema0: &Schema, schema1: &Schema) -> Result<Schema> {
902902
required: req1,
903903
},
904904
Schema::Object {
905-
properties: props2,
905+
properties: mut props2,
906906
additional_properties: add2,
907907
required: req2,
908908
},
909909
) => {
910910
let mut new_props = IndexMap::new();
911-
for key in props1.keys().chain(props2.keys()) {
912-
let new_schema = match (props1.get(key), props2.get(key), add1, add2) {
913-
(Some(schema1), Some(schema2), _, _) => merge_two(schema1, schema2)?,
914-
(Some(schema1), None, _, Some(add)) => merge_two(schema1, &add)?,
915-
(None, Some(schema2), Some(add), _) => merge_two(&add, schema2)?,
916-
(Some(schema1), None, _, None) => schema1.to_owned(),
917-
(None, Some(schema2), None, _) => schema2.to_owned(),
918-
(None, None, _, _) => bail!("should not happen"),
919-
};
920-
new_props.insert(key.clone(), new_schema);
911+
for (key, prop1) in props1.into_iter() {
912+
let prop2 = props2
913+
.shift_remove(&key)
914+
.or_else(|| add2.as_deref().cloned())
915+
.unwrap_or(Schema::Any);
916+
new_props.insert(key, intersect_two(prop1, prop2)?);
917+
}
918+
for (key, prop2) in props2.into_iter() {
919+
let prop1 = add1.as_deref().cloned().unwrap_or(Schema::Any);
920+
new_props.insert(key, intersect_two(prop1, prop2)?);
921921
}
922+
let mut required = req1;
923+
required.extend(req2);
922924
Ok(Schema::Object {
923925
properties: new_props,
924926
additional_properties: match (add1, add2) {
925927
(None, None) => None,
926-
(None, Some(add)) => Some(Box::new(*add.clone())),
927-
(Some(add), None) => Some(Box::new(*add.clone())),
928-
(Some(add1), Some(add2)) => Some(Box::new(merge_two(&add1, &add2)?)),
928+
(None, Some(add2)) => Some(add2),
929+
(Some(add1), None) => Some(add1),
930+
(Some(add1), Some(add2)) => Some(Box::new(intersect_two(*add1, *add2)?)),
929931
},
930-
required: req1.union(req2).cloned().collect(),
932+
required,
931933
})
932934
}
933935
//TODO: get types for error message

0 commit comments

Comments
 (0)