Skip to content

Commit a99faaf

Browse files
committed
ci: add rust continuous integration
1 parent 0cc5a0f commit a99faaf

File tree

7 files changed

+125
-26
lines changed

7 files changed

+125
-26
lines changed

.github/workflows/rust.yml

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
name: Rust
2+
3+
on:
4+
pull_request:
5+
push:
6+
branches:
7+
- develop
8+
9+
jobs:
10+
test:
11+
name: Test (${{ matrix.name }})
12+
runs-on: ubuntu-latest
13+
strategy:
14+
fail-fast: false
15+
matrix:
16+
include:
17+
- name: no-default-features
18+
args: "--no-default-features"
19+
- name: default-features (serde)
20+
args: ""
21+
- name: fzn
22+
args: "--no-default-features --features fzn"
23+
- name: all-features
24+
args: "--all-features"
25+
steps:
26+
- name: Checkout repository
27+
uses: actions/checkout@v6
28+
- name: Install Rust toolchain
29+
uses: dtolnay/rust-toolchain@nightly
30+
- name: Cache cargo artifacts
31+
uses: Swatinem/rust-cache@v2
32+
- name: Build
33+
run: cargo build ${{ matrix.args }}
34+
- name: Test
35+
run: cargo test ${{ matrix.args }}
36+
37+
clippy:
38+
name: Clippy (${{ matrix.name }})
39+
runs-on: ubuntu-latest
40+
strategy:
41+
fail-fast: false
42+
matrix:
43+
include:
44+
- name: no-default-features
45+
args: "--no-default-features"
46+
- name: default-features (serde)
47+
args: ""
48+
- name: fzn
49+
args: "--no-default-features --features fzn"
50+
- name: all-features
51+
args: "--all-features"
52+
steps:
53+
- name: Checkout repository
54+
uses: actions/checkout@v6
55+
- name: Install Rust toolchain
56+
uses: dtolnay/rust-toolchain@nightly
57+
with:
58+
components: clippy
59+
- name: Cache cargo artifacts
60+
uses: Swatinem/rust-cache@v2
61+
- name: Run clippy
62+
run: cargo clippy --all-targets ${{ matrix.args }} -- -D warnings
63+
64+
fmt:
65+
name: Rustfmt
66+
runs-on: ubuntu-latest
67+
steps:
68+
- name: Checkout repository
69+
uses: actions/checkout@v6
70+
- name: Install Rust toolchain
71+
uses: dtolnay/rust-toolchain@nightly
72+
with:
73+
components: rustfmt
74+
- name: Run rustfmt
75+
run: cargo fmt --all --check

Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ trivial_numeric_casts = "warn"
4040
unit_bindings = "warn"
4141
unnameable_types = "warn"
4242
unreachable_pub = "warn"
43-
unused_crate_dependencies = "deny"
4443
unused_import_braces = "warn"
4544
unused_lifetimes = "warn"
4645
unused_macro_rules = "warn"

src/fzn.rs

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ use annotations::*;
1515
pub use error::FznParseError;
1616
use primitives::*;
1717
use winnow::{
18-
combinator::{alt, delimited, opt, preceded, separated, separated_pair},
1918
Parser, Result, Stateful,
19+
combinator::{alt, delimited, opt, preceded, separated, separated_pair},
2020
};
2121

2222
use crate::{
@@ -35,18 +35,28 @@ enum Declaration<Identifier> {
3535
}
3636

3737
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
38+
/// Represents the current parsing phase.
3839
enum ParsePhase {
40+
/// Accepting all items, possibly already parsed a predicate declaration.
3941
Predicates,
42+
/// Parsed a array/variable declaration item, no longer accepting predicate
43+
/// declarations.
4044
Declarations,
45+
/// Parsed a constraint item, no longer accepting predicate or
46+
/// array/variable declarations.
4147
Constraints,
48+
/// Parsed a solve item, no longer accepting any items.
4249
Solve,
4350
}
4451

4552
#[derive(Debug, PartialEq)]
53+
/// State used during parsing.
4654
struct ParseState<'s, Identifier> {
55+
/// Collection of parsed parameters to be replaced in constraint arguments.
4756
parameters: &'s mut HashMap<String, Literal<Identifier>>,
4857
}
4958

59+
/// Type used for the parser input and state.
5060
type Stream<'source, 'state, Identifier> = Stateful<&'source str, ParseState<'state, Identifier>>;
5161

5262
/// Parses a constraint argument.
@@ -127,8 +137,8 @@ where
127137

128138
/// Parses the domain in a variable declaration.
129139
///
130-
/// Has no direct analogue in the grammar. However, it is essentially the `<basic-var-type>`
131-
/// without the "var" token preceding it:
140+
/// Has no direct analogue in the grammar. However, it is essentially the
141+
/// `<basic-var-type>` without the "var" token preceding it:
132142
///
133143
/// ```bnf
134144
/// <basic-var-type> ::= "var" <basic-par-type>
@@ -197,6 +207,7 @@ where
197207
.parse_next(input)
198208
}
199209

210+
/// Parse a parameter declaration item.
200211
fn parameter_item<Identifier>(
201212
input: &mut Stream<'_, '_, Identifier>,
202213
) -> Result<(String, Literal<Identifier>)>
@@ -537,7 +548,7 @@ where
537548
.parse_next(input)
538549
}
539550

540-
/// Parse a variable model item.
551+
/// Parse a variable declaration item.
541552
fn variable<Identifier>(
542553
input: &mut Stream<'_, '_, Identifier>,
543554
) -> Result<(Identifier, Variable<Identifier>, bool)>
@@ -583,15 +594,15 @@ mod tests {
583594

584595
use rangelist::RangeList;
585596
use ustr::Ustr;
586-
use winnow::{error::ParserError, Parser, Stateful};
597+
use winnow::{Parser, Stateful, error::ParserError};
587598

588599
use crate::{
589-
fzn::{
590-
array_item, constraint, parameter_item, predicate_item, solve_objective, variable,
591-
ParseState, Stream,
592-
},
593600
Annotation, AnnotationArgument, AnnotationCall, AnnotationLiteral, Argument, Array,
594601
Constraint, FlatZinc, FznParseError, Literal, Method, SolveObjective, Type, Variable,
602+
fzn::{
603+
ParseState, Stream, array_item, constraint, parameter_item, predicate_item,
604+
solve_objective, variable,
605+
},
595606
};
596607

597608
#[test]

src/fzn/annotations.rs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,23 @@ use std::{
66
};
77

88
use winnow::{
9-
combinator::{alt, delimited, opt, preceded, repeat, separated},
109
Parser, Result,
10+
combinator::{alt, delimited, opt, preceded, repeat, separated},
1111
};
1212

1313
use crate::{
14-
fzn::{identifier, identifier_raw, literal, token, Stream},
1514
Annotation, AnnotationArgument, AnnotationCall, AnnotationLiteral, FznParseError, Literal,
15+
fzn::{Stream, identifier, identifier_raw, literal, token},
1616
};
1717

1818
/// Semantic flags projected out of special FlatZinc annotations.
1919
#[derive(Default)]
2020
pub(crate) struct AnnotationFlags {
21+
/// Whether the variable is defined by a constraint
2122
pub(crate) defined: bool,
23+
/// Whether the variable was additionally introduced by the compiler.
2224
pub(crate) introduced: bool,
25+
/// Whether the variable is an output variable.
2326
pub(crate) output: bool,
2427
}
2528

@@ -77,8 +80,8 @@ where
7780

7881
/// Parses an annotation with arguments.
7982
///
80-
/// This does not have an analogue in the FZN grammar. It is only used to parse annotation
81-
/// arguments that are nested annotation calls.
83+
/// This does not have an analogue in the FZN grammar. It is only used to parse
84+
/// annotation arguments that are nested annotation calls.
8285
fn annotation_call<Identifier>(
8386
input: &mut Stream<'_, '_, Identifier>,
8487
) -> Result<AnnotationCall<Identifier>>
@@ -120,6 +123,8 @@ where
120123
.parse_next(input)
121124
}
122125

126+
/// Parses the annotations for a constraint, returning optionally the identifier
127+
/// of the defined variable and a list of annotations.
123128
pub(super) fn constraint_annotations<Identifier>(
124129
input: &mut Stream<'_, '_, Identifier>,
125130
) -> Result<(Option<Identifier>, Vec<Annotation<Identifier>>)>
@@ -167,6 +172,7 @@ where
167172
.parse_next(input)
168173
}
169174

175+
/// Parses a general list of annotations.
170176
pub(super) fn general_annotations<Identifier>(
171177
input: &mut Stream<'_, '_, Identifier>,
172178
) -> Result<Vec<Annotation<Identifier>>>
@@ -199,6 +205,8 @@ where
199205
.parse_next(input)
200206
}
201207

208+
/// Parses the annotations for a variable declaration, returning flags for
209+
/// standard annotations and a list of other annotations.
202210
pub(super) fn variable_annotations<Identifier>(
203211
input: &mut Stream<'_, '_, Identifier>,
204212
) -> Result<(AnnotationFlags, Vec<Annotation<Identifier>>)>
@@ -257,8 +265,8 @@ mod tests {
257265
use rangelist::RangeList;
258266

259267
use crate::{
260-
fzn::{general_annotations, tests::check_parser},
261268
Annotation, AnnotationArgument, AnnotationCall, AnnotationLiteral, Literal,
269+
fzn::{general_annotations, tests::check_parser},
262270
};
263271

264272
#[test]

src/fzn/primitives.rs

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@ use std::{
77

88
use rangelist::RangeList;
99
use winnow::{
10+
Parser, Result,
1011
ascii::{digit1, hex_digit1, multispace1, oct_digit1},
1112
combinator::{alt, delimited, opt, separated, separated_pair, trace},
1213
error::{ContextError, FromExternalError},
1314
stream::AsChar,
1415
token::{one_of, take_till, take_until, take_while},
15-
Parser, Result,
1616
};
1717

18-
use crate::{fzn::Stream, FznParseError, Literal};
18+
use crate::{FznParseError, Literal, fzn::Stream};
1919

2020
/// Parse a `/* ... */` block comment.
2121
fn block_comment<I>(input: &mut Stream<'_, '_, I>) -> Result<()>
@@ -37,8 +37,8 @@ pub(super) fn boolean<I: Debug>(input: &mut Stream<'_, '_, I>) -> Result<bool> {
3737
alt(("true".map(|_| true), "false".map(|_| false))).parse_next(input)
3838
}
3939

40-
/// Parses a list of elements seperated by a comma, and delimited by `open_token` and
41-
/// `close_token`.
40+
/// Parses a list of elements seperated by a comma, and delimited by
41+
/// `open_token` and `close_token`.
4242
pub(super) fn delimited_list<'source, 'state, T, I>(
4343
open_token: &'static str,
4444
element_parser: impl Parser<Stream<'source, 'state, I>, T, ContextError>,
@@ -212,9 +212,10 @@ where
212212
Identifier: Clone + Debug + FromStr,
213213
<Identifier as FromStr>::Err: Display,
214214
{
215-
// This can be optimized if it turns out to be a bottleneck. At the moment, to parse a literal,
216-
// it will first attempt to parse a float and, if that fails, parse an integer. We can be more
217-
// clever about that by peeking at the next character to determine what is being parsed.
215+
// This can be optimized if it turns out to be a bottleneck. At the moment, to
216+
// parse a literal, it will first attempt to parse a float and, if that fails,
217+
// parse an integer. We can be more clever about that by peeking at the next
218+
// character to determine what is being parsed.
218219

219220
let parsed_literal = alt((
220221
set(int).map(Literal::IntSet),
@@ -255,7 +256,9 @@ where
255256
///
256257
/// Works with either interval sets or sparse sets.
257258
///
258-
/// The grammar is modified from the documentation. Here we abstract the element type.
259+
/// The grammar is modified from the documentation. Here we abstract the element
260+
/// type.
261+
///
259262
/// ```bnf
260263
/// <set-literal> ::= <set-term> [ "union" <set-term> ] ...
261264
///
@@ -306,8 +309,8 @@ mod tests {
306309
use winnow::{Parser, Stateful};
307310

308311
use crate::{
309-
fzn::{literal, tests::check_parser, ParseState},
310312
Literal,
313+
fzn::{ParseState, literal, tests::check_parser},
311314
};
312315

313316
#[test]

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -671,6 +671,7 @@ impl<Identifier: Display> Display for SolveObjective<Identifier> {
671671
}
672672

673673
impl Type {
674+
/// Return the canonical FlatZinc type name without any domain restriction.
674675
fn base_name(&self) -> &'static str {
675676
match self {
676677
Type::Bool => "bool",

src/serde.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
use std::{fmt, marker::PhantomData};
55

66
use serde::{
7+
Deserialize, Deserializer, Serialize, Serializer,
78
de::{MapAccess, Visitor},
89
ser::SerializeMap,
9-
Deserialize, Deserializer, Serialize, Serializer,
1010
};
1111

1212
use crate::{Annotation, Literal, Method, RangeList, SolveObjective, Type, Variable};
@@ -58,7 +58,8 @@ pub(crate) enum VariableDomain {
5858
/// Integer domain payload serialized as a JSON array of inclusive bounds.
5959
#[serde(deserialize_with = "deserialize_set", serialize_with = "serialize_set")]
6060
Int(RangeList<i64>),
61-
/// Floating-point domain payload serialized as a JSON array of inclusive bounds.
61+
/// Floating-point domain payload serialized as a JSON array of inclusive
62+
/// bounds.
6263
#[serde(deserialize_with = "deserialize_set", serialize_with = "serialize_set")]
6364
Float(RangeList<f64>),
6465
}
@@ -160,6 +161,7 @@ where
160161
deserializer.deserialize_map(KeyValueObjectVisitor(PhantomData))
161162
}
162163

164+
/// Deserialize a raw `(start, end)` range list into a [`RangeList`].
163165
pub(crate) fn deserialize_set<
164166
'de,
165167
D: Deserializer<'de>,

0 commit comments

Comments
 (0)