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: 3 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,9 @@ jobs:
github_token: ${{ secrets.GITHUB_TOKEN }}

- name: Generate coverage
run: cargo llvm-cov --all-features --no-report
# Use --test-threads=1 to prevent race conditions in stdin-mocking tests
# that manipulate file descriptors (dup/dup2) which aren't thread-safe
run: cargo llvm-cov --all-features --no-report -- --test-threads=1

- name: Combine coverage reports
run: cargo llvm-cov report --lcov --output-path lcov.info
Expand Down
23 changes: 22 additions & 1 deletion .serena/project.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,27 @@ excluded_tools: []
# initial prompt for the project. It will always be given to the LLM upon activating the project
# (contrary to the memories, which are loaded on demand).
initial_prompt: ""

# the name by which the project can be referenced within Serena
project_name: "libmagic-rs"

# list of tools to include that would otherwise be disabled (particularly optional tools that are disabled by default)
included_optional_tools: []

# list of mode names to that are always to be included in the set of active modes
# The full set of modes to be activated is base_modes + default_modes.
# If the setting is undefined, the base_modes from the global configuration (serena_config.yml) apply.
# Otherwise, this setting overrides the global configuration.
# Set this to [] to disable base modes for this project.
# Set this to a list of mode names to always include the respective modes for this project.
base_modes:

# list of mode names that are to be activated by default.
# The full set of modes to be activated is base_modes + default_modes.
# If the setting is undefined, the default_modes from the global configuration (serena_config.yml) apply.
# Otherwise, this overrides the setting from the global configuration (serena_config.yml).
# This setting can, in turn, be overridden by CLI parameters (--mode).
default_modes:

# fixed set of tools to use as the base tool set (if non-empty), replacing Serena's default set of tools.
# This cannot be combined with non-empty excluded_tools or included_optional_tools.
fixed_tools: []
10 changes: 10 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ This document provides comprehensive guidelines for AI assistants working on the
- **Bounds checking** for all buffer access using `.get()` methods
- **Safe resource management** with RAII patterns
- **Graceful error handling** for malformed inputs
- **Safe string operations**: Use `strip_prefix()`/`strip_suffix()` instead of direct slicing (`&str[n..]`) to avoid UTF-8 panics

### 2. Zero-Warnings Policy

Expand All @@ -44,6 +45,7 @@ This document provides comprehensive guidelines for AI assistants working on the
- Use `cargo nextest` for faster, more reliable test execution
- Include property tests with `proptest` for fuzzing
- Benchmark critical path components with `criterion`
- Verify doc examples with `cargo test --doc` - ensure example strings don't accidentally match multiple patterns

## Architecture Patterns

Expand Down Expand Up @@ -91,6 +93,14 @@ evaluator/

- Avoid using emojis and other non-ASCII characters in code, comments, or documentation, except when the code is handling non-plaintext characters (for example: em dash, en dash, or other non-ASCII symbols).

### Case-Insensitive Matching Pattern

When implementing case-insensitive string matching:

- Lowercase inputs at ALL entry points (constructors, setters)
- Store normalized values internally
- Document the case-insensitivity in public API docs

### Error Handling Patterns

```rust
Expand Down
25 changes: 23 additions & 2 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ mod error;
mod parser;

use error::ParseError;
use parser::ast::{Endianness, MagicRule, OffsetSpec, Operator, TypeKind, Value};
use parser::ast::{Endianness, MagicRule, OffsetSpec, Operator, StrengthModifier, TypeKind, Value};
use parser::parse_text_magic_file;
use std::env;
use std::fs;
Expand All @@ -32,6 +32,7 @@ const INDENT_WIDTH: usize = 4;

fn main() {
println!("cargo:rerun-if-changed=src/builtin_rules.magic");
println!("cargo:rerun-if-changed=build.rs");

let manifest_dir = match env::var("CARGO_MANIFEST_DIR") {
Ok(value) => value,
Expand Down Expand Up @@ -109,9 +110,11 @@ fn format_parse_error(error: &ParseError) -> String {
fn generate_builtin_rules(rules: &[MagicRule]) -> String {
let mut output = String::new();

// Allow unused_imports since StrengthModifier may not be used if no rules have strength modifiers
push_line(&mut output, "#[allow(unused_imports)]");
push_line(
&mut output,
"use crate::parser::ast::{MagicRule, OffsetSpec, TypeKind, Operator, Value, Endianness};",
"use crate::parser::ast::{MagicRule, OffsetSpec, TypeKind, Operator, Value, Endianness, StrengthModifier};",
);
push_line(&mut output, "use std::sync::LazyLock;");
push_line(&mut output, "");
Expand Down Expand Up @@ -200,12 +203,30 @@ fn serialize_magic_rule(rule: &MagicRule, indent: usize) -> String {
&rule.level.to_string(),
);

push_field(
&mut output,
indent + INDENT_WIDTH,
"strength_modifier",
&serialize_strength_modifier(&rule.strength_modifier),
);

push_indent(&mut output, indent);
output.push('}');

output
}

fn serialize_strength_modifier(modifier: &Option<StrengthModifier>) -> String {
match modifier {
None => "None".to_string(),
Some(StrengthModifier::Add(val)) => format!("Some(StrengthModifier::Add({val}))"),
Some(StrengthModifier::Subtract(val)) => format!("Some(StrengthModifier::Subtract({val}))"),
Some(StrengthModifier::Multiply(val)) => format!("Some(StrengthModifier::Multiply({val}))"),
Some(StrengthModifier::Divide(val)) => format!("Some(StrengthModifier::Divide({val}))"),
Some(StrengthModifier::Set(val)) => format!("Some(StrengthModifier::Set({val}))"),
}
}

fn serialize_children(children: &[MagicRule], indent: usize) -> String {
if children.is_empty() {
return "Vec::new()".to_string();
Expand Down
Loading
Loading