|
|
|
|
@@ -599,27 +599,24 @@ macro_rules! passed{
|
|
|
|
|
Name:$name,
|
|
|
|
|
Summary:String::new(),
|
|
|
|
|
Passed:true,
|
|
|
|
|
Details:serde_json::Value::Null,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
macro_rules! summary{
|
|
|
|
|
($name:literal,$summary:expr,$details:expr)=>{
|
|
|
|
|
($name:literal,$summary:expr)=>{
|
|
|
|
|
Check{
|
|
|
|
|
Name:$name,
|
|
|
|
|
Summary:$summary,
|
|
|
|
|
Passed:false,
|
|
|
|
|
Details:serde_json::to_value($details)?,
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
macro_rules! summary_format{
|
|
|
|
|
($name:literal,$fmt:literal,$details:expr)=>{
|
|
|
|
|
($name:literal,$fmt:literal)=>{
|
|
|
|
|
Check{
|
|
|
|
|
Name:$name,
|
|
|
|
|
Summary:format!($fmt),
|
|
|
|
|
Passed:false,
|
|
|
|
|
Details:serde_json::to_value($details)?,
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
@@ -630,34 +627,34 @@ impl MapCheck<'_>{
|
|
|
|
|
fn itemize(&self)->Result<MapCheckList,serde_json::Error>{
|
|
|
|
|
let model_class=match &self.model_class{
|
|
|
|
|
StringCheck(Ok(()))=>passed!("ModelClass"),
|
|
|
|
|
StringCheck(Err(context))=>summary_format!("ModelClass","Invalid model class: {context}",()),
|
|
|
|
|
StringCheck(Err(context))=>summary_format!("ModelClass","Invalid model class: {context}"),
|
|
|
|
|
};
|
|
|
|
|
let model_name=match &self.model_name{
|
|
|
|
|
StringCheck(Ok(()))=>passed!("ModelName"),
|
|
|
|
|
StringCheck(Err(context))=>summary_format!("ModelName","Model name must have snake_case: {context}",()),
|
|
|
|
|
StringCheck(Err(context))=>summary_format!("ModelName","Model name must have snake_case: {context}"),
|
|
|
|
|
};
|
|
|
|
|
let display_name=match &self.display_name{
|
|
|
|
|
Ok(Ok(StringCheck(Ok(_))))=>passed!("DisplayName"),
|
|
|
|
|
Ok(Ok(StringCheck(Err(context))))=>summary_format!("DisplayName","DisplayName must have Title Case: {context}",()),
|
|
|
|
|
Ok(Err(context))=>summary_format!("DisplayName","Invalid DisplayName: {context}",()),
|
|
|
|
|
Err(StringValueError::ObjectNotFound)=>summary!("DisplayName","Missing DisplayName StringValue".to_owned(),()),
|
|
|
|
|
Err(StringValueError::ValueNotSet)=>summary!("DisplayName","DisplayName Value not set".to_owned(),()),
|
|
|
|
|
Err(StringValueError::NonStringValue)=>summary!("DisplayName","DisplayName Value is not a String".to_owned(),()),
|
|
|
|
|
Ok(Ok(StringCheck(Err(context))))=>summary_format!("DisplayName","DisplayName must have Title Case: {context}"),
|
|
|
|
|
Ok(Err(context))=>summary_format!("DisplayName","Invalid DisplayName: {context}"),
|
|
|
|
|
Err(StringValueError::ObjectNotFound)=>summary!("DisplayName","Missing DisplayName StringValue".to_owned()),
|
|
|
|
|
Err(StringValueError::ValueNotSet)=>summary!("DisplayName","DisplayName Value not set".to_owned()),
|
|
|
|
|
Err(StringValueError::NonStringValue)=>summary!("DisplayName","DisplayName Value is not a String".to_owned()),
|
|
|
|
|
};
|
|
|
|
|
let creator=match &self.creator{
|
|
|
|
|
Ok(Ok(_))=>passed!("Creator"),
|
|
|
|
|
Ok(Err(context))=>summary_format!("Creator","Invalid Creator: {context}",()),
|
|
|
|
|
Err(StringValueError::ObjectNotFound)=>summary!("Creator","Missing Creator StringValue".to_owned(),()),
|
|
|
|
|
Err(StringValueError::ValueNotSet)=>summary!("Creator","Creator Value not set".to_owned(),()),
|
|
|
|
|
Err(StringValueError::NonStringValue)=>summary!("Creator","Creator Value is not a String".to_owned(),()),
|
|
|
|
|
Ok(Err(context))=>summary_format!("Creator","Invalid Creator: {context}"),
|
|
|
|
|
Err(StringValueError::ObjectNotFound)=>summary!("Creator","Missing Creator StringValue".to_owned()),
|
|
|
|
|
Err(StringValueError::ValueNotSet)=>summary!("Creator","Creator Value not set".to_owned()),
|
|
|
|
|
Err(StringValueError::NonStringValue)=>summary!("Creator","Creator Value is not a String".to_owned()),
|
|
|
|
|
};
|
|
|
|
|
let game_id=match &self.game_id{
|
|
|
|
|
Ok(_)=>passed!("GameID"),
|
|
|
|
|
Err(ParseGameIDError)=>summary!("GameID","Model name must be prefixed with bhop_ surf_ or flytrials_".to_owned(),()),
|
|
|
|
|
Err(ParseGameIDError)=>summary!("GameID","Model name must be prefixed with bhop_ surf_ or flytrials_".to_owned()),
|
|
|
|
|
};
|
|
|
|
|
let mapstart=match &self.mapstart{
|
|
|
|
|
Ok(Exists)=>passed!("MapStart"),
|
|
|
|
|
Err(Absent)=>summary_format!("MapStart","Model has no MapStart",()),
|
|
|
|
|
Err(Absent)=>summary_format!("MapStart","Model has no MapStart"),
|
|
|
|
|
};
|
|
|
|
|
let duplicate_start=match &self.mode_start_counts{
|
|
|
|
|
DuplicateCheck(Ok(()))=>passed!("DuplicateStart"),
|
|
|
|
|
@@ -665,7 +662,7 @@ impl MapCheck<'_>{
|
|
|
|
|
let context=Separated::new(", ",||context.iter().map(|(&mode_id,names)|
|
|
|
|
|
Duplicates::new(ModeElement{zone:Zone::Start,mode_id},names.len())
|
|
|
|
|
));
|
|
|
|
|
summary_format!("DuplicateStart","Duplicate start zones: {context}",())
|
|
|
|
|
summary_format!("DuplicateStart","Duplicate start zones: {context}")
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
let (extra_finish,missing_finish)=match &self.mode_finish_counts{
|
|
|
|
|
@@ -678,7 +675,7 @@ impl MapCheck<'_>{
|
|
|
|
|
let context=Separated::new(", ",||context.extra.iter().map(|(&mode_id,_names)|
|
|
|
|
|
ModeElement{zone:Zone::Finish,mode_id}
|
|
|
|
|
));
|
|
|
|
|
summary_format!("ExtraFinish","No matching start zone for finish {plural}: {context}",())
|
|
|
|
|
summary_format!("ExtraFinish","No matching start zone for finish {plural}: {context}")
|
|
|
|
|
},
|
|
|
|
|
if context.missing.is_empty(){
|
|
|
|
|
passed!("MissingFinish")
|
|
|
|
|
@@ -687,7 +684,7 @@ impl MapCheck<'_>{
|
|
|
|
|
let context=Separated::new(", ",||context.missing.iter().map(|&mode_id|
|
|
|
|
|
ModeElement{zone:Zone::Finish,mode_id}
|
|
|
|
|
));
|
|
|
|
|
summary_format!("MissingFinish","Missing finish {plural}: {context}",())
|
|
|
|
|
summary_format!("MissingFinish","Missing finish {plural}: {context}")
|
|
|
|
|
}
|
|
|
|
|
),
|
|
|
|
|
};
|
|
|
|
|
@@ -701,13 +698,13 @@ impl MapCheck<'_>{
|
|
|
|
|
let context=Separated::new(", ",||context.extra.iter().map(|(&mode_id,_names)|
|
|
|
|
|
ModeElement{zone:Zone::Anticheat,mode_id}
|
|
|
|
|
));
|
|
|
|
|
summary_format!("DanglingAnticheat","No matching start zone for anticheat {plural}: {context}",())
|
|
|
|
|
summary_format!("DanglingAnticheat","No matching start zone for anticheat {plural}: {context}")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
let spawn1=match &self.spawn1{
|
|
|
|
|
Ok(Exists)=>passed!("Spawn1"),
|
|
|
|
|
Err(Absent)=>summary_format!("Spawn1","Model has no Spawn1",()),
|
|
|
|
|
Err(Absent)=>summary_format!("Spawn1","Model has no Spawn1"),
|
|
|
|
|
};
|
|
|
|
|
let dangling_teleport=match &self.teleport_counts{
|
|
|
|
|
SetDifferenceCheck(Ok(()))=>passed!("DanglingTeleport"),
|
|
|
|
|
@@ -715,7 +712,7 @@ impl MapCheck<'_>{
|
|
|
|
|
let unique_names:HashSet<_>=context.extra.values().flat_map(|names|names.iter().copied()).collect();
|
|
|
|
|
let plural=if unique_names.len()==1{"object"}else{"objects"};
|
|
|
|
|
let context=Separated::new(", ",||&unique_names);
|
|
|
|
|
summary_format!("DanglingTeleport","No matching Spawn for {plural}: {context}",())
|
|
|
|
|
summary_format!("DanglingTeleport","No matching Spawn for {plural}: {context}")
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
let duplicate_spawns=match &self.spawn_counts{
|
|
|
|
|
@@ -724,7 +721,7 @@ impl MapCheck<'_>{
|
|
|
|
|
let context=Separated::new(", ",||context.iter().map(|(&stage_id,&names)|
|
|
|
|
|
Duplicates::new(StageElement{behaviour:StageElementBehaviour::Spawn,stage_id},names as usize)
|
|
|
|
|
));
|
|
|
|
|
summary_format!("DuplicateSpawn","Duplicate Spawn: {context}",())
|
|
|
|
|
summary_format!("DuplicateSpawn","Duplicate Spawn: {context}")
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
let (extra_wormhole_in,missing_wormhole_in)=match &self.wormhole_in_counts{
|
|
|
|
|
@@ -736,7 +733,7 @@ impl MapCheck<'_>{
|
|
|
|
|
let context=Separated::new(", ",||context.extra.iter().map(|(&wormhole_id,_names)|
|
|
|
|
|
WormholeElement{behaviour:WormholeBehaviour::In,wormhole_id}
|
|
|
|
|
));
|
|
|
|
|
summary_format!("ExtraWormholeIn","WormholeIn with no matching WormholeOut: {context}",())
|
|
|
|
|
summary_format!("ExtraWormholeIn","WormholeIn with no matching WormholeOut: {context}")
|
|
|
|
|
},
|
|
|
|
|
if context.missing.is_empty(){
|
|
|
|
|
passed!("MissingWormholeIn")
|
|
|
|
|
@@ -746,7 +743,7 @@ impl MapCheck<'_>{
|
|
|
|
|
let context=Separated::new(", ",||context.missing.iter().map(|&wormhole_id|
|
|
|
|
|
WormholeElement{behaviour:WormholeBehaviour::Out,wormhole_id}
|
|
|
|
|
));
|
|
|
|
|
summary_format!("MissingWormholeIn","WormholeOut with no matching WormholeIn: {context}",())
|
|
|
|
|
summary_format!("MissingWormholeIn","WormholeOut with no matching WormholeIn: {context}")
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
};
|
|
|
|
|
@@ -756,7 +753,7 @@ impl MapCheck<'_>{
|
|
|
|
|
let context=Separated::new(", ",||context.iter().map(|(&wormhole_id,&names)|
|
|
|
|
|
Duplicates::new(WormholeElement{behaviour:WormholeBehaviour::Out,wormhole_id},names as usize)
|
|
|
|
|
));
|
|
|
|
|
summary_format!("DuplicateWormholeOut","Duplicate WormholeOut: {context}",())
|
|
|
|
|
summary_format!("DuplicateWormholeOut","Duplicate WormholeOut: {context}")
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
Ok(MapCheckList{checks:Box::new([
|
|
|
|
|
|