Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
strategy:
fail-fast: false
matrix:
toolchain: [stable, 1.85.0]
toolchain: [stable, 1.88.0]
uses: wrightkit/.github/.github/workflows/rust-quality.yml@main
with:
toolchain: ${{ matrix.toolchain }}
Expand Down Expand Up @@ -67,7 +67,7 @@ jobs:
- name: Set up Rust
uses: wrightkit/.github/.github/actions/setup-rust@main
with:
toolchain: 1.85.0
toolchain: 1.88.0
cache-shared-key: quality
- name: Catalog check
run: cargo run --locked -p workshop-rs --bin workshop-catalog-gen -- check
Expand Down
8 changes: 4 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,10 @@ cargo run -p workshop-rs --bin workshop-catalog-gen -- check
git diff --check
```

The workspace declares `rust-version = "1.85.0"`. Before pushing, also run
`cargo +1.85.0 clippy --workspace --all-targets -- -D warnings` and
`cargo +1.85.0 test --workspace --all-targets`; newer syntax such as
let-chains passes on stable and is caught only by the `Rust (1.85.0)` CI job.
The workspace declares `rust-version = "1.88.0"`. Before pushing, also run
`cargo +1.88.0 clippy --workspace --all-targets -- -D warnings` and
`cargo +1.88.0 test --workspace --all-targets`; newer syntax such as
let-chains passes on stable and is caught only by the `Rust (1.88.0)` CI job.

A local pass is not proof of live-client behavior. Claims about current
Workshop acceptance or seasonal behavior require the corresponding client or
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ resolver = "3"
[workspace.package]
version = "1.20.0"
edition = "2024"
rust-version = "1.85.0"
rust-version = "1.88.0"
license = "MIT"
repository = "https://github.com/wrightkit/workshop-rs"
description = "The canonical multi-locale Overwatch Workshop semantic core."
Expand Down
14 changes: 7 additions & 7 deletions crates/workshop-rs-cli/src/conformance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -416,13 +416,13 @@ fn validate_artifact(
"must identify an immutable revision",
));
}
if let Some(digest) = &artifact.sha256 {
if !is_sha256_digest(digest) {
return Err(ConformanceError::invalid(
format!("{field}.sha256"),
"must be a 64-character hexadecimal SHA-256 digest",
));
}
if let Some(digest) = &artifact.sha256
&& !is_sha256_digest(digest)
{
return Err(ConformanceError::invalid(
format!("{field}.sha256"),
"must be a 64-character hexadecimal SHA-256 digest",
));
}
if require_digest && artifact.sha256.is_none() {
return Err(ConformanceError::invalid(
Expand Down
12 changes: 6 additions & 6 deletions crates/workshop-rs-cli/src/live_capture.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,12 @@ impl LiveCapture {
if normalize_game(&self.catalog.target.game) != normalize_game(&self.game) {
return Err(invalid("capture game does not match catalog target game"));
}
if let Some(catalog) = catalog {
if self.catalog != catalog.identity() {
return Err(invalid(
"capture catalog identity does not match the loaded catalog",
));
}
if let Some(catalog) = catalog
&& self.catalog != catalog.identity()
{
return Err(invalid(
"capture catalog identity does not match the loaded catalog",
));
}
validate_census(&self.census)?;
validate_artifact("rawArtifact", &self.raw_artifact, true)?;
Expand Down
48 changes: 22 additions & 26 deletions crates/workshop-rs/src/actions/emitter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,7 @@ impl EmitContext<'_> {
self.line(
level,
&format!(
"{}({player_text}, {name}, {start_text}, {stop_text}, {step_text});",
keyword
"{keyword}({player_text}, {name}, {start_text}, {stop_text}, {step_text});"
),
)?;
for action in body {
Expand Down Expand Up @@ -324,13 +323,13 @@ impl EmitContext<'_> {
if index > 0 {
args_text.push_str(", ");
}
if index == 0 && player_pair_merged {
if let Some(wir::Value::PlayerVariable { player, variable }) =
if index == 0
&& player_pair_merged
&& let Some(wir::Value::PlayerVariable { player, variable }) =
self.program.values.get(*arg).map(|node| &node.value)
{
self.emit_variable_pair(*player, *variable, &mut args_text)?;
continue;
}
{
self.emit_variable_pair(*player, *variable, &mut args_text)?;
continue;
}
let variable_position = match name.as_str() {
"setGlobalVariableAtIndex" | "modifyGlobalVariableAtIndex" => {
Expand All @@ -341,19 +340,17 @@ impl EmitContext<'_> {
}
_ => false,
};
if variable_position {
if let Some(node) = self.program.values.get(*arg) {
match &node.value {
wir::Value::GlobalVariable(variable) => {
args_text.push_str(self.global_name(*variable)?);
continue;
}
wir::Value::PlayerVariable { variable, .. } => {
args_text.push_str(self.player_name(*variable)?);
continue;
}
_ => {}
if variable_position && let Some(node) = self.program.values.get(*arg) {
match &node.value {
wir::Value::GlobalVariable(variable) => {
args_text.push_str(self.global_name(*variable)?);
continue;
}
wir::Value::PlayerVariable { variable, .. } => {
args_text.push_str(self.player_name(*variable)?);
continue;
}
_ => {}
}
}
let operation_name = if matches!(
Expand All @@ -373,15 +370,14 @@ impl EmitContext<'_> {
} else {
None
};
if let Some(operation_name) = operation_name {
if self
if let Some(operation_name) = operation_name
&& self
.catalog
.entry(Kind::Operator, &operation_name)
.is_some()
{
args_text.push_str(self.spelling(Kind::Operator, &operation_name)?);
continue;
}
{
args_text.push_str(self.spelling(Kind::Operator, &operation_name)?);
continue;
}
self.value(*arg, &mut args_text)?;
}
Expand Down
7 changes: 3 additions & 4 deletions crates/workshop-rs/src/actions/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,11 +270,10 @@ impl ParseContext<'_> {
kind: TokenKind::Word(word),
..
}) = self.peek()
&& self.settings_name_matches("tokens", "disabled", &word)
{
if self.settings_name_matches("tokens", "disabled", &word) {
self.pos += 1;
return true;
}
self.pos += 1;
return true;
}
false
}
Expand Down
30 changes: 13 additions & 17 deletions crates/workshop-rs/src/bin/workshop-catalog-gen/corpus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ fn settings_surface(catalog: &Catalog) -> SettingsSurface {
} else {
enum_spelling(catalog, "Gamemode", &key, &locale).unwrap_or_else(|| key.clone())
};
(format!("mode.{}.name", key), name)
(format!("mode.{key}.name"), name)
})
.collect();
let mut maps = catalog_name_surface(catalog, "Map", "map", &locale);
Expand Down Expand Up @@ -796,26 +796,22 @@ fn en_aliases(entry: &Value) -> Result<Vec<&str>, String> {
let alias = entry
.get("aliases")
.and_then(|aliases| aliases.get("en-US"))
.ok_or_else(|| format!("catalog entry '{}' without en-US alias", entry))?;
.ok_or_else(|| format!("catalog entry '{entry}' without en-US alias"))?;
match alias {
Value::String(spelling) => Ok(vec![spelling.as_str()]),
Value::Array(spellings) => {
let spellings = spellings
.iter()
.map(Value::as_str)
.collect::<Option<Vec<&str>>>()
.ok_or_else(|| format!("catalog entry '{}' with non-string en-US alias", entry))?;
.ok_or_else(|| format!("catalog entry '{entry}' with non-string en-US alias"))?;
if spellings.is_empty() {
return Err(format!(
"catalog entry '{}' with empty en-US aliases",
entry
));
return Err(format!("catalog entry '{entry}' with empty en-US aliases"));
}
Ok(spellings)
}
_ => Err(format!(
"catalog entry '{}' with non-string en-US alias",
entry
"catalog entry '{entry}' with non-string en-US alias"
)),
}
}
Expand Down Expand Up @@ -1259,10 +1255,10 @@ fn settings_corpus(
);
}

if let Ok(previous) = std::fs::read_to_string(settings_out) {
if let Ok(previous) = serde_json::from_str::<Value>(&previous) {
preserve_existing_settings(&mut entries, &previous);
}
if let Ok(previous) = std::fs::read_to_string(settings_out)
&& let Ok(previous) = serde_json::from_str::<Value>(&previous)
{
preserve_existing_settings(&mut entries, &previous);
}

let mut labels = Map::new();
Expand Down Expand Up @@ -1469,10 +1465,10 @@ fn confirmed_settings_identity_match(
let mut template_locales = template_translations.as_object()?.clone();
let hero_locales = hero_translations.as_object()?.clone();
for (locale, value) in &mut template_locales {
if let Some(template) = value.as_str() {
if let Some(hero_name) = hero_locales.get(locale).and_then(Value::as_str) {
*value = Value::String(template.replace("%1$s", hero_name));
}
if let Some(template) = value.as_str()
&& let Some(hero_name) = hero_locales.get(locale).and_then(Value::as_str)
{
*value = Value::String(template.replace("%1$s", hero_name));
}
}
Some(vec![
Expand Down
14 changes: 7 additions & 7 deletions crates/workshop-rs/src/catalog/descriptions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,14 +226,14 @@ fn validated(catalog: &Catalog, file: DescriptionsFile) -> Result<Descriptions>
"descriptions gap '{key}' names no catalog action or value"
))
})?;
if let Gap::Param(_, position) = &gap {
if *position >= catalog.entries[index].params.len() {
return Err(CatalogError::validation(format!(
"descriptions gap '{key}' names parameter {position}, \
if let Gap::Param(_, position) = &gap
&& *position >= catalog.entries[index].params.len()
{
return Err(CatalogError::validation(format!(
"descriptions gap '{key}' names parameter {position}, \
out of {} params",
catalog.entries[index].params.len()
)));
}
catalog.entries[index].params.len()
)));
}
parsed.push(gap);
}
Expand Down
3 changes: 1 addition & 2 deletions crates/workshop-rs/src/catalog/load.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,7 @@ impl AliasFile {
};
if spellings.is_empty() || spellings.iter().any(String::is_empty) {
return Err(CatalogError::validation(format!(
"catalog entry '{}' declares an empty alias for locale '{}'",
id, locale
"catalog entry '{id}' declares an empty alias for locale '{locale}'"
)));
}
Ok(spellings)
Expand Down
8 changes: 4 additions & 4 deletions crates/workshop-rs/src/catalog/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -958,10 +958,10 @@ impl Catalog {
impl ExpectedDomain for Catalog {
fn expected_domain(&self, catalog_id: &str, arg_index: usize) -> Option<&str> {
for kind in [Kind::Action, Kind::Value] {
if let Some(entry) = self.entry(kind, catalog_id) {
if let Some(domain) = entry.param_domain(arg_index) {
return Some(domain);
}
if let Some(entry) = self.entry(kind, catalog_id)
&& let Some(domain) = entry.param_domain(arg_index)
{
return Some(domain);
}
}
None
Expand Down
32 changes: 15 additions & 17 deletions crates/workshop-rs/src/events/validate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,25 +76,23 @@ pub(crate) fn validate_event(
None
}
};
if let Some(target_member) = target_member {
if catalog
if let Some(target_member) = target_member
&& catalog
.enum_spelling("EventPlayer", &en, &target_member)
.is_none()
{
return Err(
WorkshopError::unknown("event player", target_member.clone(), en, span)
.with_candidates(crate::core::suggest::nearest(
&target_member,
catalog
.canonical_member_spellings("EventPlayer", catalog.primary_locale())
.chain(
catalog
.canonical_member_spellings("Hero", catalog.primary_locale()),
),
crate::core::suggest::CANDIDATE_LIMIT,
)),
);
}
{
return Err(
WorkshopError::unknown("event player", target_member.clone(), en, span)
.with_candidates(crate::core::suggest::nearest(
&target_member,
catalog
.canonical_member_spellings("EventPlayer", catalog.primary_locale())
.chain(
catalog.canonical_member_spellings("Hero", catalog.primary_locale()),
),
crate::core::suggest::CANDIDATE_LIMIT,
)),
);
}
Ok(())
}
44 changes: 22 additions & 22 deletions crates/workshop-rs/src/frontend/lexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,22 +110,22 @@ pub(crate) fn tokenize(input: &str) -> Result<Vec<Token>, LexError> {
let mut content = String::new();
let mut closed = false;
while let Some(c) = cursor.peek() {
if c == '\\' {
if let Some(escaped) = cursor.peek_at(1) {
let decoded = match escaped {
'"' => Some('"'),
'\\' => Some('\\'),
'n' => Some('\n'),
'r' => Some('\r'),
't' => Some('\t'),
_ => None,
};
if let Some(decoded) = decoded {
cursor.advance();
cursor.advance();
content.push(decoded);
continue;
}
if c == '\\'
&& let Some(escaped) = cursor.peek_at(1)
{
let decoded = match escaped {
'"' => Some('"'),
'\\' => Some('\\'),
'n' => Some('\n'),
'r' => Some('\r'),
't' => Some('\t'),
_ => None,
};
if let Some(decoded) = decoded {
cursor.advance();
cursor.advance();
content.push(decoded);
continue;
}
}
if c == '"' {
Expand Down Expand Up @@ -330,13 +330,13 @@ fn take_word(cursor: &mut Cursor) -> Token {
let start = cursor.pos();
let mut word = String::new();
while let Some(c) = cursor.peek() {
if c == '<' {
if let Some(len) = markup_tag_len(cursor.rest) {
for _ in 0..len {
word.push(cursor.advance().unwrap());
}
continue;
if c == '<'
&& let Some(len) = markup_tag_len(cursor.rest)
{
for _ in 0..len {
word.push(cursor.advance().unwrap());
}
continue;
}
let interior_dash = c == '-'
&& cursor
Expand Down
Loading
Loading