Skip to main content

flatzinc_serde/
lib.rs

1//! Serialization of the FlatZinc data format
2//!
3//! FlatZinc is the language in which data and solver specific constraint models
4//! are produced by the [MiniZinc](https://www.minizinc.org) compiler. This
5//! crate implements the FlatZinc serialization format as described in the
6//! [Interfacing Solvers to
7//! FlatZinc](https://www.minizinc.org/doc-latest/en/fzn-spec.html#specification-of-flatzinc-json)
8//! section of the MiniZinc reference manual. It supports both the JSON-based
9//! FlatZinc representation, via [serde](https://serde.rs), and the older
10//! textual `.fzn` format. For the JSON format, we suggest using
11//! [`serde_json`](https://crates.io/crates/serde_json) with the specification
12//! in this crate to parse the FlatZinc JSON files produced by the MiniZinc
13//! compiler.
14//!
15//! # Feature Flags
16//!
17//! - `serde` (default): enables JSON serialization and deserialization support
18//!   via the [`serde`](https://serde.rs) crate.
19//! - `fzn`: enables parsing of the original `.fzn` text format via [`winnow`](https://crates.io/crates/winnow).
20//!
21//! # Getting Started
22//!
23//! For the default JSON-based workflow, install `flatzinc-serde` and
24//! `serde_json` for your package:
25//!
26//! ```bash
27//! cargo add flatzinc-serde serde_json
28//! ```
29//!
30//! If you disable the default `serde` feature and only use the older textual
31//! `.fzn` support, `serde_json` is not required.
32//!
33//! Once these dependencies have been installed to your crate, you can
34//! deserialize a FlatZinc JSON file as follows:
35//!
36//! ```
37//! # #[cfg(feature = "serde")] {
38//! # use flatzinc_serde::FlatZinc;
39//! # use std::{fs::File, io::BufReader, path::Path};
40//! # let path = Path::new("./corpus/json/documentation_example.fzn.json");
41//! // let path = Path::new("/lorem/ipsum/model.fzn.json");
42//! let rdr = BufReader::new(File::open(path).unwrap());
43//! let fzn: FlatZinc = serde_json::from_reader(rdr).unwrap();
44//! // ... process FlatZinc ...
45//! # }
46//! ```
47//!
48//! When deserializing FlatZinc JSON, this crate rejects unknown fields on inner
49//! FlatZinc objects such as variables, arrays, constraints, solve items, and
50//! annotation-call objects. Unknown fields on the outer top-level wrapper
51//! object are ignored to preserve some forward compatibility for envelope
52//! metadata.
53//!
54//! The older textual `.fzn` format is also supported when the `fzn` feature is
55//! enabled:
56//!
57//! ```
58//! # #[cfg(feature = "fzn")] {
59//! # use flatzinc_serde::FlatZinc;
60//! # use std::{fs::File, io::BufReader, path::Path};
61//! # let path = Path::new("./corpus/fzn/documentation_example.fzn");
62//! // let path = Path::new("/lorem/ipsum/model.fzn");
63//! let rdr = BufReader::new(File::open(path).unwrap());
64//! let fzn: FlatZinc = FlatZinc::from_fzn(rdr).unwrap();
65//! // ... process FlatZinc ...
66//! # }
67//! ```
68//!
69//! To serialize a FlatZinc JSON value, you can use the usual `serde_json`
70//! APIs:
71//!
72//! ```
73//! # #[cfg(feature = "serde")] {
74//! # use flatzinc_serde::FlatZinc;
75//! let fzn = FlatZinc::<String>::default();
76//! // ... create  solver constraint model ...
77//! let json_str = serde_json::to_string(&fzn).unwrap();
78//! # }
79//! ```
80//! Note that `serde_json::to_writer`, using a buffered file writer, would be
81//! preferred when writing larger FlatZinc files.
82//!
83//! To serialize a FlatZinc value to the older textual `.fzn` format, use its
84//! [`Display`] implementation:
85//!
86//! ```
87//! # use flatzinc_serde::FlatZinc;
88//! let fzn = FlatZinc::<String>::default();
89//! let fzn_text = fzn.to_string();
90//! ```
91//!
92//! # Register your solver with MiniZinc
93//!
94//! If your goal is to deserialize FlatZinc to implement a MiniZinc solver, then
95//! the next step is to register your solver executable with MiniZinc. This can
96//! be done by creating a [MiniZinc Solver
97//! Configuration](https://www.minizinc.org/doc-2.8.2/en/fzn-spec.html#solver-configuration-files)
98//! (`.msc`) file, and adding it to a folder on the `MZN_SOLVER_PATH` or a
99//! standardized path, like `~/.minizinc/solvers/`. A basic solver configuration
100//! for a solver that accepts JSON input would look as follows:
101//!
102//! ```json
103//! {
104//!   "name" : "My Solver",
105//!   "version": "0.0.1",
106//!   "id": "my.organisation.mysolver",
107//!   "inputType": "JSON",
108//!   "executable": "../../../bin/fzn-my-solver",
109//!   "mznlib": "../mysolver"
110//!   "stdFlags": [],
111//!   "extraFlags": []
112//! }
113//! ```
114//!
115//! Once you have placed your configuration file on the correct path, then you
116//! solver will be listed by `minizinc --solvers`. Calling `minizinc --solver
117//! mysolver model.mzn data.dzn`, assuming a valid MiniZinc instance, will
118//! (after compilation) invoke the registered executable with a path of a
119//! FlatZinc JSON file, and potentially any registered standard and extra flags
120//! (e.g., `../../../bin/fzn-my-solver model.fzn.json`).
121
122mod error;
123#[cfg(feature = "fzn")]
124mod fzn;
125pub mod helpers;
126#[cfg(any(feature = "fzn", feature = "serde"))]
127mod intermediate;
128#[cfg(feature = "serde")]
129mod serde_impl;
130
131use std::{
132	cmp::Ordering,
133	collections::HashSet,
134	fmt::{Debug, Display},
135	hash::{Hash, Hasher},
136	sync::{Arc, Weak},
137};
138
139pub use rangelist::RangeList;
140#[cfg(feature = "serde")]
141use serde::{Deserializer, Serialize};
142
143pub use crate::error::{FznParseError, LinkError};
144use crate::helpers::ArcKey;
145
146/// Additional information provided in a standardized format for declarations,
147/// constraints, or solve objectives
148///
149/// In MiniZinc annotations can both be added explicitly in the model, or can be
150/// added during compilation process.
151///
152/// Note that annotations are generally defined either in the MiniZinc standard
153/// library or in a solver's redefinition library. Solvers are encouraged to
154/// rewrite annotations in their redefinitions library when required.
155#[cfg_attr(feature = "serde", derive(Serialize))]
156#[cfg_attr(feature = "serde", serde(untagged))]
157#[derive(Clone, PartialEq, Debug)]
158pub enum Annotation<Identifier = String> {
159	/// Atom annotation (i.e., a single `Identifier`)
160	Atom(Identifier),
161	/// Call annotation
162	Call(AnnotationCall<Identifier>),
163}
164
165/// The argument type associated with [`AnnotationCall`]
166#[cfg_attr(feature = "serde", derive(Serialize))]
167#[cfg_attr(feature = "serde", serde(untagged))]
168#[derive(Clone, Debug)]
169pub enum AnnotationArgument<Identifier = String> {
170	/// Sequence of [`Literal`]s
171	Array(Vec<AnnotationLiteral<Identifier>>),
172	#[cfg_attr(
173		feature = "serde",
174		serde(serialize_with = "serde_impl::serialize_array_weak")
175	)]
176	/// Named array of [`Literal`]s
177	ArrayNamed(Weak<Array<Identifier>>),
178	/// Singular argument
179	Literal(AnnotationLiteral<Identifier>),
180}
181
182/// An object depicting an annotation in the form of a call
183#[cfg_attr(feature = "serde", derive(Serialize))]
184#[cfg_attr(feature = "serde", serde(rename = "annotation_call"))]
185#[derive(Clone, PartialEq, Debug)]
186pub struct AnnotationCall<Identifier = String> {
187	/// Identifier of the constraint predicate
188	pub id: Identifier,
189	/// Arguments of the constraint
190	pub args: Vec<AnnotationArgument<Identifier>>,
191}
192
193/// Literal values as arguments to [`AnnotationCall`]
194#[cfg_attr(feature = "serde", derive(Serialize))]
195#[cfg_attr(feature = "serde", serde(untagged))]
196#[derive(Clone, Debug)]
197pub enum AnnotationLiteral<Identifier = String> {
198	/// Integer value
199	Int(i64),
200	/// Floating point value
201	Float(f64),
202	/// Reference to a decision variable.
203	#[cfg_attr(
204		feature = "serde",
205		serde(serialize_with = "serde_impl::serialize_variable_weak")
206	)]
207	Variable(Weak<Variable<Identifier>>),
208	/// Boolean value
209	Bool(bool),
210	/// Set of integers, represented as a list of integer ranges
211	#[cfg_attr(
212		feature = "serde",
213		serde(serialize_with = "serde_impl::serialize_encapsulate_set")
214	)]
215	IntSet(RangeList<i64>),
216	/// Set of floating point values, represented as a list of floating point
217	/// ranges
218	#[cfg_attr(
219		feature = "serde",
220		serde(serialize_with = "serde_impl::serialize_encapsulate_set")
221	)]
222	FloatSet(RangeList<f64>),
223	/// String value
224	#[cfg_attr(
225		feature = "serde",
226		serde(serialize_with = "serde_impl::serialize_encapsulate_string")
227	)]
228	String(String),
229	/// An annotation object.
230	Annotation(Annotation<Identifier>),
231}
232
233/// The argument type associated with [`Constraint`]
234#[cfg_attr(feature = "serde", derive(Serialize))]
235#[cfg_attr(feature = "serde", serde(untagged))]
236#[derive(Clone, PartialEq, Debug)]
237pub enum Argument<Identifier = String> {
238	/// Sequence of [`Literal`]s
239	Array(Vec<Literal<Identifier>>),
240	/// Sequence of [`Literal`]s
241	#[cfg_attr(
242		feature = "serde",
243		serde(serialize_with = "serde_impl::serialize_array_arc",)
244	)]
245	ArrayNamed(Arc<Array<Identifier>>),
246	/// Literal
247	Literal(Literal<Identifier>),
248}
249
250/// A definition of a named array literal in FlatZinc
251///
252/// FlatZinc Arrays are a simple (one-dimensional) sequence of [`Literal`]s.
253/// These values are stored as the [`Array::contents`] member. Additional
254/// information, in the form of [`Annotation`]s, from the MiniZinc model is
255/// stored in [`Array::ann`] when present. When [`Array::defined`] is set to
256/// `true`, then
257#[cfg_attr(feature = "serde", derive(Serialize))]
258#[cfg_attr(feature = "serde", serde(rename = "array"))]
259#[derive(Clone, PartialEq, Debug)]
260pub struct Array<Identifier = String> {
261	/// The optional public name of the array literal.
262	///
263	/// This is `None` for arrays inlined within constraints.
264	#[cfg_attr(feature = "serde", serde(skip))]
265	pub name: String,
266	/// The values stored within the array literal
267	#[cfg_attr(feature = "serde", serde(rename = "a"))]
268	pub contents: Vec<Literal<Identifier>>,
269	/// List of annotations
270	#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Vec::is_empty"))]
271	pub ann: Vec<Annotation<Identifier>>,
272	/// This field is set to `true` when there is a constraint that has been
273	/// marked as defining this array.
274	#[cfg_attr(feature = "serde", serde(skip_serializing_if = "serde_impl::is_false"))]
275	pub defined: bool,
276	/// This field is set to `true` when the array has been introduced by the
277	/// MiniZinc compiler, rather than being explicitly defined at the top-level
278	/// of the MiniZinc model.
279	#[cfg_attr(feature = "serde", serde(skip_serializing_if = "serde_impl::is_false"))]
280	pub introduced: bool,
281}
282
283/// An object depicting a constraint
284#[cfg_attr(feature = "serde", derive(Serialize))]
285#[cfg_attr(feature = "serde", serde(rename = "constraint"))]
286#[derive(Clone, PartialEq, Debug)]
287pub struct Constraint<Identifier = String> {
288	/// Identifier of the constraint predicate
289	pub id: Identifier,
290	/// Arguments of the constraint
291	pub args: Vec<Argument<Identifier>>,
292	/// Variable that the constraint defines
293	#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
294	pub defines: Option<NamedRef<Identifier>>,
295	/// List of annotations
296	#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Vec::is_empty"))]
297	pub ann: Vec<Annotation<Identifier>>,
298}
299
300/// The structure depicting a FlatZinc instance
301///
302/// FlatZinc is (generally) a format produced by the MiniZinc compiler as a
303/// result of instantiating the parameter variables of a MiniZinc model and
304/// generating a solver-specific equisatisfiable model.
305#[cfg_attr(feature = "serde", derive(Serialize))]
306#[derive(Clone, PartialEq, Debug)]
307pub struct FlatZinc<Identifier = String> {
308	#[cfg_attr(
309		feature = "serde",
310		serde(serialize_with = "serde_impl::serialize_variable_map")
311	)]
312	/// A list of decision variable definitions.
313	pub variables: Vec<Arc<Variable<Identifier>>>,
314	#[cfg_attr(
315		feature = "serde",
316		serde(serialize_with = "serde_impl::serialize_array_map")
317	)]
318	/// A list of named array definitions.
319	pub arrays: Vec<Arc<Array<Identifier>>>,
320	/// A list of (solver-specific) constraints, that must be satisfied in a
321	/// solution.
322	pub constraints: Vec<Constraint<Identifier>>,
323	/// A list of all entities for which the solver must produce output for each
324	/// solution.
325	pub output: Vec<NamedRef<Identifier>>,
326	/// A specification of the goal of solving the FlatZinc instance.
327	pub solve: SolveObjective<Identifier>,
328	/// The version of the FlatZinc serialization specification used
329	pub version: String,
330}
331
332/// Literal values
333#[cfg_attr(feature = "serde", derive(Serialize))]
334#[cfg_attr(feature = "serde", serde(untagged))]
335#[derive(Clone, PartialEq, Debug)]
336pub enum Literal<Identifier = String> {
337	/// Integer value
338	Int(i64),
339	/// Floating point value
340	Float(f64),
341	/// Reference to a decision variable.
342	#[cfg_attr(
343		feature = "serde",
344		serde(serialize_with = "serde_impl::serialize_variable_arc",)
345	)]
346	Variable(Arc<Variable<Identifier>>),
347	/// Boolean value
348	Bool(bool),
349	/// Set of integers, represented as a list of integer ranges
350	#[cfg_attr(
351		feature = "serde",
352		serde(serialize_with = "serde_impl::serialize_encapsulate_set",)
353	)]
354	IntSet(RangeList<i64>),
355	/// Set of floating point values, represented as a list of floating point
356	/// ranges
357	#[cfg_attr(
358		feature = "serde",
359		serde(serialize_with = "serde_impl::serialize_encapsulate_set",)
360	)]
361	FloatSet(RangeList<f64>),
362	/// String value
363	#[cfg_attr(
364		feature = "serde",
365		serde(serialize_with = "serde_impl::serialize_encapsulate_string",)
366	)]
367	String(String),
368}
369
370/// Goal of solving a FlatZinc instance.
371#[derive(Clone, Debug, Default, PartialEq)]
372pub enum Method<Identifier = String> {
373	/// Find any solution.
374	#[default]
375	Satisfy,
376	/// Find the solution with the lowest value for the given objective.
377	Minimize(Literal<Identifier>),
378	/// Find the solution with the highest value for the given objective.
379	Maximize(Literal<Identifier>),
380}
381
382/// Reference to a named top-level declaration (variable or array)
383///
384/// ### Warning
385///
386/// It is possible for an [`Array`] to exist without an `name` attribute, if a
387/// reference to such an [`Array`] is used as a [`NamedRef`], serialization can
388/// panic.
389///
390/// [`NamedRef`] compares, hashes, and orders by the referenced declaration
391/// name. As a consequence, two values that refer to different allocations but
392/// expose the same name are considered equal, and a variable and array with
393/// the same name are also treated as equal for these trait implementations.
394/// Note that this cannot occur in valid FlatZinc models.
395#[derive(Clone, Debug)]
396pub enum NamedRef<Identifier = String> {
397	/// Reference to a variable.
398	Variable(Arc<Variable<Identifier>>),
399	/// Reference to an array.
400	Array(Arc<Array<Identifier>>),
401}
402
403/// A specification of objective of a FlatZinc instance
404#[derive(Clone, PartialEq, Debug)]
405pub struct SolveObjective<Identifier = String> {
406	/// The method expected to be used for solving the instance.
407	pub method: Method<Identifier>,
408	/// A list of annotations from the solve statement in the MiniZinc model
409	///
410	/// Note that this includes the search annotations if they are present in
411	/// the model.
412	pub ann: Vec<Annotation<Identifier>>,
413}
414
415/// Used to signal the type of (decision) [`Variable`]
416#[derive(Clone, PartialEq, Debug)]
417pub enum Type {
418	/// Boolean decision variable
419	Bool,
420	/// Integer decision variable
421	Int(Option<RangeList<i64>>),
422	/// Floating point decision variable
423	Float(Option<RangeList<f64>>),
424	/// Integer set decision variable
425	IntSet(Option<RangeList<i64>>),
426}
427
428/// The definition of a decision variable
429#[derive(Clone, PartialEq, Debug)]
430pub struct Variable<Identifier = String> {
431	/// The public name of the decision variable.
432	pub name: String,
433	/// The type of the decision variable, and set of potential values  from
434	/// which the decision variable must take its value in a solution, i.e. its
435	/// domain.
436	///
437	/// If domain has the value `None`, then all values of the decision
438	/// variable's `Type` are allowed in a solution.
439	pub ty: Type,
440	/// A list of annotations
441	pub ann: Vec<Annotation<Identifier>>,
442	/// This field is set to `true` when there is a constraint that has been
443	/// marked as defining this variable.
444	pub defined: bool,
445	/// This field is set to `true` when the variable has been introduced by the
446	/// MiniZinc compiler, rather than being explicitly defined at the top-level
447	/// of the MiniZinc model.
448	pub introduced: bool,
449}
450
451impl<Identifier: Display> Display for Annotation<Identifier> {
452	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
453		match self {
454			Annotation::Atom(a) => write!(f, "{a}"),
455			Annotation::Call(c) => write!(f, "{c}"),
456		}
457	}
458}
459
460impl<Idenfier: Display> Display for AnnotationArgument<Idenfier> {
461	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
462		match self {
463			AnnotationArgument::Array(arr) => {
464				write!(f, "[")?;
465				let mut first = true;
466				for v in arr {
467					if !first {
468						write!(f, ", ")?
469					}
470					write!(f, "{v}")?;
471					first = false;
472				}
473				write!(f, "]")
474			}
475			AnnotationArgument::ArrayNamed(array) => match array.upgrade() {
476				Some(array) => write!(f, "{}", &array.name),
477				None => write!(f, "[/* dangling array ref */]"),
478			},
479			AnnotationArgument::Literal(lit) => write!(f, "{lit}"),
480		}
481	}
482}
483
484impl<Identifier> From<Argument<Identifier>> for AnnotationArgument<Identifier> {
485	fn from(value: Argument<Identifier>) -> Self {
486		match value {
487			Argument::Array(arr) => {
488				AnnotationArgument::Array(arr.into_iter().map(|l| l.into()).collect())
489			}
490			Argument::ArrayNamed(arr) => AnnotationArgument::ArrayNamed(Arc::downgrade(&arr)),
491			Argument::Literal(l) => AnnotationArgument::Literal(l.into()),
492		}
493	}
494}
495
496impl<Identifier: PartialEq> PartialEq for AnnotationArgument<Identifier> {
497	fn eq(&self, other: &Self) -> bool {
498		match (self, other) {
499			(AnnotationArgument::Array(lhs), AnnotationArgument::Array(rhs)) => lhs == rhs,
500			(AnnotationArgument::ArrayNamed(lhs), AnnotationArgument::ArrayNamed(rhs)) => {
501				match (lhs.upgrade(), rhs.upgrade()) {
502					(Some(lhs), Some(rhs)) => lhs == rhs,
503					(None, None) => true,
504					_ => false,
505				}
506			}
507			(AnnotationArgument::Literal(lhs), AnnotationArgument::Literal(rhs)) => lhs == rhs,
508			_ => false,
509		}
510	}
511}
512
513impl<Identifier: Display> Display for AnnotationCall<Identifier> {
514	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
515		write!(f, "{}(", self.id)?;
516		let mut first = true;
517		for arg in &self.args {
518			if !first {
519				write!(f, ", ")?
520			}
521			write!(f, "{arg}")?;
522			first = false;
523		}
524		write!(f, ")")
525	}
526}
527
528impl<Idenfier: Display> Display for AnnotationLiteral<Idenfier> {
529	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
530		match self {
531			AnnotationLiteral::Int(i) => write!(f, "{i}"),
532			AnnotationLiteral::Float(x) => write!(f, "{x:?}"),
533			AnnotationLiteral::Variable(var) => match var.upgrade() {
534				Some(var) => write!(f, "{}", var.name),
535				None => write!(f, "DANGLING_VARIABLE_REFERENCE"),
536			},
537			AnnotationLiteral::Bool(b) => write!(f, "{b}"),
538			AnnotationLiteral::IntSet(is) => write!(f, "{is}"),
539			AnnotationLiteral::FloatSet(fs) => write!(f, "{fs}"),
540			AnnotationLiteral::String(s) => write!(f, "{s:?}"),
541			AnnotationLiteral::Annotation(ann) => write!(f, "{ann}"),
542		}
543	}
544}
545
546impl<Identifier> From<Literal<Identifier>> for AnnotationLiteral<Identifier> {
547	fn from(value: Literal<Identifier>) -> Self {
548		match value {
549			Literal::Int(i) => AnnotationLiteral::Int(i),
550			Literal::Float(f) => AnnotationLiteral::Float(f),
551			Literal::Bool(b) => AnnotationLiteral::Bool(b),
552			Literal::String(s) => AnnotationLiteral::String(s),
553			Literal::Variable(var) => AnnotationLiteral::Variable(Arc::downgrade(&var)),
554			Literal::IntSet(set) => AnnotationLiteral::IntSet(set),
555			Literal::FloatSet(set) => AnnotationLiteral::FloatSet(set),
556		}
557	}
558}
559
560impl<Identifier: PartialEq> PartialEq for AnnotationLiteral<Identifier> {
561	fn eq(&self, other: &Self) -> bool {
562		match (self, other) {
563			(Self::Int(lhs), Self::Int(rhs)) => lhs == rhs,
564			(Self::Float(lhs), Self::Float(rhs)) => lhs == rhs,
565			(Self::Variable(lhs), Self::Variable(rhs)) => match (lhs.upgrade(), rhs.upgrade()) {
566				(Some(lhs), Some(rhs)) => lhs == rhs,
567				(None, None) => true,
568				_ => false,
569			},
570			(Self::Bool(lhs), Self::Bool(rhs)) => lhs == rhs,
571			(Self::IntSet(lhs), Self::IntSet(rhs)) => lhs == rhs,
572			(Self::FloatSet(lhs), Self::FloatSet(rhs)) => lhs == rhs,
573			(Self::String(lhs), Self::String(rhs)) => lhs == rhs,
574			(Self::Annotation(lhs), Self::Annotation(rhs)) => lhs == rhs,
575			_ => false,
576		}
577	}
578}
579impl<Identifier: Display> Display for Argument<Identifier> {
580	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
581		match self {
582			Argument::Array(arr) => {
583				write!(f, "[")?;
584				let mut first = true;
585				for v in arr {
586					if !first {
587						write!(f, ", ")?
588					}
589					write!(f, "{v}")?;
590					first = false;
591				}
592				write!(f, "]")
593			}
594			Argument::ArrayNamed(arr) => write!(f, "{}", &arr.name),
595			Argument::Literal(lit) => write!(f, "{lit}"),
596		}
597	}
598}
599
600impl<Identifier> Array<Identifier> {
601	/// Clones this array reference into an [`ArcKey`].
602	///
603	/// This is useful when storing arrays in collections such as
604	/// [`HashMap`](std::collections::HashMap), [`HashSet`], and
605	/// [`BTreeMap`](std::collections::BTreeMap), where the key should identify
606	/// the specific parsed array object rather than its contents or `name`.
607	///
608	/// This method clones the [`Arc`] reference count and keeps the original
609	/// array reference usable by the caller.
610	///
611	/// The resulting key uses the allocation / pointer identity of this
612	/// [`Arc`]. Two arrays with the same name and equal contents will
613	/// therefore compare as different keys if they are stored in different
614	/// allocations.
615	///
616	/// During FlatZinc parsing and deserialization, this crate guarantees that
617	/// identical top-level arrays are allocated only once. In those cases,
618	/// `ArcKey` is a good fit for keying collections by the canonical parsed
619	/// array object.
620	pub fn cloned_key(self: &Arc<Self>) -> ArcKey<Self> {
621		ArcKey::new(Arc::clone(self))
622	}
623
624	/// Heuristic to determine the type of the array
625	fn determine_type(&self) -> (&str, bool) {
626		let ty = match self.contents.first().unwrap() {
627			Literal::Int(_) => "int",
628			Literal::Float(_) => "float",
629			Literal::Variable(var) => return (var.ty.base_name(), true),
630			Literal::Bool(_) => "bool",
631			Literal::IntSet(_) => "set of int",
632			Literal::FloatSet(_) => "set of float",
633			Literal::String(_) => "string",
634		};
635		let is_var = self
636			.contents
637			.iter()
638			.any(|lit| matches!(lit, Literal::Variable(_)));
639		(ty, is_var)
640	}
641}
642
643impl<Identifier: Display> Display for Constraint<Identifier> {
644	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
645		write!(f, "{}(", self.id)?;
646		let mut first = true;
647		for arg in &self.args {
648			if !first {
649				write!(f, ", ")?
650			}
651			write!(f, "{arg}")?;
652			first = false;
653		}
654		write!(f, ")")?;
655		if let Some(defines) = &self.defines {
656			write!(f, " ::defines_var({})", defines.name())?
657		}
658		for a in &self.ann {
659			write!(f, " ::{a}")?
660		}
661		Ok(())
662	}
663}
664
665impl<Identifier> FlatZinc<Identifier>
666where
667	Identifier: Clone + Debug,
668{
669	/// Deserialize a FlatZinc JSON value using a custom identifier interner,
670	/// used for constraint and annotation identifiers.
671	///
672	/// Unknown fields on inner FlatZinc objects are rejected. Unknown fields on
673	/// the outer top-level JSON object are ignored.
674	#[cfg(feature = "serde")]
675	pub fn deserialize_with_interner<'de, D, F, E>(
676		deserializer: D,
677		interner: F,
678	) -> Result<Self, D::Error>
679	where
680		D: Deserializer<'de>,
681		F: FnMut(&str) -> Result<Identifier, E>,
682		E: Display,
683	{
684		use serde::de::{self, DeserializeSeed};
685
686		use crate::intermediate::ParserState;
687
688		let (model, interner) = ParserState::new(interner).deserialize(deserializer)?;
689		FlatZinc::from_intermediate(model, interner).map_err(de::Error::custom)
690	}
691
692	/// Parse a `.fzn` source into a [`FlatZinc`] instance.
693	#[cfg(feature = "fzn")]
694	pub fn from_fzn<E>(source: impl std::io::BufRead) -> Result<Self, FznParseError>
695	where
696		for<'a> Identifier: TryFrom<&'a str, Error = E>,
697		E: Display,
698	{
699		fzn::parse(source)
700	}
701
702	/// Parse a `.fzn` source into a [`FlatZinc`] instance using a custom
703	/// identifier interner, used for constraint and annotation identifiers.
704	#[cfg(feature = "fzn")]
705	pub fn from_fzn_with_interner<F, E>(
706		source: impl std::io::BufRead,
707		interner: F,
708	) -> Result<Self, FznParseError>
709	where
710		F: FnMut(&str) -> Result<Identifier, E>,
711		E: Display,
712	{
713		fzn::parse_with_interner(source, interner)
714	}
715}
716
717impl<Identifier> Default for FlatZinc<Identifier> {
718	fn default() -> Self {
719		Self {
720			variables: Vec::new(),
721			arrays: Vec::new(),
722			constraints: Vec::new(),
723			output: Vec::new(),
724			solve: Default::default(),
725			version: "1.0".into(),
726		}
727	}
728}
729
730impl<Identifier: Display> Display for FlatZinc<Identifier> {
731	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
732		let output_map: HashSet<_> = self.output.iter().collect();
733
734		for var in &self.variables {
735			write!(f, "var {}", var.ty)?;
736			write!(f, ": {}", var.name)?;
737			let name_ref: NamedRef<_> = Arc::clone(var).into();
738			if output_map.contains(&name_ref) {
739				write!(f, " ::output_var")?;
740			}
741			if var.defined {
742				write!(f, " ::is_defined_var")?;
743			}
744			if var.introduced {
745				write!(f, " ::var_is_introduced")?;
746			}
747			for ann in &var.ann {
748				write!(f, " ::{ann}")?
749			}
750			writeln!(f, ";")?
751		}
752		for arr in &self.arrays {
753			let (ty, is_var) = arr.determine_type();
754			write!(
755				f,
756				"array[1..{}] of {}{ty}: {}",
757				arr.contents.len(),
758				if is_var { "var " } else { "" },
759				arr.name
760			)?;
761			let name_ref: NamedRef<_> = Arc::clone(arr).into();
762			if output_map.contains(&name_ref) {
763				write!(f, " ::output_array([1..{}])", arr.contents.len())?;
764			}
765			if arr.defined {
766				write!(f, " ::is_defined_var")?;
767			}
768			if arr.introduced {
769				write!(f, " ::var_is_introduced")?;
770			}
771			for ann in &arr.ann {
772				write!(f, " ::{ann}")?
773			}
774			write!(f, " = [")?;
775			let mut first = true;
776			for v in &arr.contents {
777				if !first {
778					write!(f, ", ")?;
779				}
780				write!(f, "{v}")?;
781				first = false;
782			}
783			writeln!(f, "];")?
784		}
785		for c in &self.constraints {
786			writeln!(f, "constraint {c};")?;
787		}
788		writeln!(f, "{};", self.solve)
789	}
790}
791
792impl<Identifier: Display> Display for Literal<Identifier> {
793	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
794		match self {
795			Literal::Int(i) => write!(f, "{i}"),
796			Literal::Float(x) => write!(f, "{x:?}"),
797			Literal::Variable(var) => write!(f, "{}", var.name),
798			Literal::Bool(b) => write!(f, "{b}"),
799			Literal::IntSet(is) => write!(f, "{is}"),
800			Literal::FloatSet(fs) => write!(f, "{fs}"),
801			Literal::String(s) => write!(f, "{s:?}"),
802		}
803	}
804}
805
806impl<Identifier: Display> Display for Method<Identifier> {
807	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
808		match self {
809			Method::Satisfy => write!(f, "satisfy"),
810			Method::Minimize(objective) => write!(f, "minimize {objective}"),
811			Method::Maximize(objective) => write!(f, "maximize {objective}"),
812		}
813	}
814}
815
816impl<Identifier> NamedRef<Identifier> {
817	/// Return the identifier of the referenced output target.
818	pub fn name(&self) -> &str {
819		match self {
820			NamedRef::Variable(var) => &var.name,
821			NamedRef::Array(array) => &array.name,
822		}
823	}
824}
825
826impl<Identifier> Eq for NamedRef<Identifier> {}
827
828impl<Identifier> From<Arc<Array<Identifier>>> for NamedRef<Identifier> {
829	fn from(arc: Arc<Array<Identifier>>) -> Self {
830		NamedRef::Array(arc)
831	}
832}
833
834impl<Identifier> From<Arc<Variable<Identifier>>> for NamedRef<Identifier> {
835	fn from(arc: Arc<Variable<Identifier>>) -> Self {
836		NamedRef::Variable(arc)
837	}
838}
839
840impl<Identifier> Hash for NamedRef<Identifier> {
841	fn hash<H: Hasher>(&self, state: &mut H) {
842		self.name().hash(state);
843	}
844}
845
846impl<Identifier> Ord for NamedRef<Identifier> {
847	fn cmp(&self, other: &Self) -> Ordering {
848		self.name().cmp(other.name())
849	}
850}
851
852impl<Identifier> PartialEq for NamedRef<Identifier> {
853	fn eq(&self, other: &Self) -> bool {
854		self.name() == other.name()
855	}
856}
857
858impl<Identifier> PartialOrd for NamedRef<Identifier> {
859	fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
860		Some(self.cmp(other))
861	}
862}
863
864impl<Identifier> Default for SolveObjective<Identifier> {
865	fn default() -> Self {
866		Self {
867			method: Default::default(),
868			ann: Vec::new(),
869		}
870	}
871}
872
873impl<Identifier: Display> Display for SolveObjective<Identifier> {
874	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
875		write!(f, "solve ")?;
876		for a in &self.ann {
877			write!(f, "::{a} ")?;
878		}
879		write!(f, "{}", self.method)
880	}
881}
882
883impl Type {
884	/// Return the canonical FlatZinc type name without any domain restriction.
885	fn base_name(&self) -> &'static str {
886		match self {
887			Type::Bool => "bool",
888			Type::Int(_) => "int",
889			Type::Float(_) => "float",
890			Type::IntSet(_) => "set of int",
891		}
892	}
893}
894
895impl Display for Type {
896	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
897		match self {
898			Type::Bool => write!(f, "bool"),
899			Type::Int(Some(domain)) => write!(f, "{domain}"),
900			Type::Int(None) => write!(f, "int"),
901			Type::Float(Some(domain)) => write!(f, "{domain}"),
902			Type::Float(None) => write!(f, "float"),
903			Type::IntSet(Some(domain)) => write!(f, "set of {domain}"),
904			Type::IntSet(None) => write!(f, "set of int"),
905		}
906	}
907}
908
909impl<Identifier> Variable<Identifier> {
910	/// Clones this variable reference into an [`ArcKey`].
911	///
912	/// This is useful when storing variables in collections such as
913	/// [`HashMap`](std::collections::HashMap), [`HashSet`], and
914	/// [`BTreeMap`](std::collections::BTreeMap), where the key should identify
915	/// the specific parsed variable object rather than its fields or `name`.
916	///
917	/// This method clones the [`Arc`] reference count and keeps the original
918	/// variable reference usable by the caller.
919	///
920	/// The resulting key uses the allocation / pointer identity of this
921	/// [`Arc`]. Two variables with the same name and equal fields will
922	/// therefore compare as different keys if they are stored in different
923	/// allocations.
924	///
925	/// During FlatZinc parsing and deserialization, this crate guarantees that
926	/// identical top-level variables are allocated only once. In those cases,
927	/// `ArcKey` is a good fit for keying collections by the canonical parsed
928	/// variable object.
929	pub fn cloned_key(self: &Arc<Self>) -> ArcKey<Self> {
930		ArcKey::new(Arc::clone(self))
931	}
932}