@@ -46,6 +46,69 @@ as_qf_constraint.qf_constraint_specifier <- function(x, table, ...) {
4646
4747
4848
49+ # Constraint lists =============================================================
50+
51+ new_qf_constraint_list <- function (x ) {
52+ stopifnot(is.list(x ))
53+ stopifnot(purrr :: every(x , is_qf_constraint ))
54+ structure(x , class = c(" qf_constraint_list" , " list" ))
55+ }
56+
57+ is_qf_constraint_list <- function (x ) {
58+ inherits(x , " qf_constraint_list" )
59+ }
60+
61+ as_qf_constraint_list <- function (x ) {
62+ UseMethod(" as_qf_constraint_list" )
63+ }
64+
65+ # ' @noRd
66+ # ' @export
67+ as_qf_constraint_list.qf_constraint_list <- function (x ) {
68+ x
69+ }
70+ # ' @noRd
71+ # ' @export
72+ as_qf_constraint_list.list <- function (x ) {
73+ purrr :: walk(x , \(cstr ) {
74+ if (! is_qf_constraint(cstr ))
75+ stop(" <qf_constraint_list> cannot include a " , typeof(x ))
76+ })
77+ new_qf_constraint_list(x )
78+ }
79+ # ' @noRd
80+ # ' @export
81+ as_qf_constraint_list.qf_constraint <- function (x ) {
82+ new_qf_constraint_list(list (x ))
83+ }
84+
85+ # ' @noRd
86+ # ' @export
87+ `&.qf_constraint_list` <- function (e1 , e2 ) {
88+ new_qf_constraint_list(c(
89+ as_qf_constraint_list(e1 ),
90+ as_qf_constraint_list(e2 ))
91+ )
92+ }
93+ # ' @noRd
94+ # ' @export
95+ `&.qf_constraint` <- `&.qf_constraint_list`
96+
97+
98+
99+ # Methods ======================================================================
100+
101+ # ' @noRd
102+ # ' @export
103+ print.qf_constraint <- function (x , ... ) {
104+ column_names <- paste(x $ cols , collapse = " , " )
105+ cat0(pretty_class(x ), " [" , column_names , " ]" )
106+ cat(" \n " )
107+ invisible (x )
108+ }
109+
110+
111+
49112# Constraint specification =====================================================
50113
51114# ' Create a constraint specifier
150213
151214
152215
153- # Generics =========== ==========================================================
216+ # Constraint checking ==========================================================
154217
155218# ' Check that a constraint is satisfied
156219# '
@@ -166,15 +229,15 @@ check_constraint <- function(constraint, table) {
166229 purrr :: map(excepted_rows , table = table ) | >
167230 purrr :: reduce(`|` , .init = rep(FALSE , nrow(table )))
168231 handled_rows <- satisfied_rows | excepted_rows
169- list (
232+ structure( list (
170233 satisfied = all(satisfied_rows ),
171234 handled = all(handled_rows ),
172235 rows = list (
173236 satisfied = satisfied_rows ,
174237 excepted = excepted_rows ,
175238 handled = handled_rows
176239 )
177- )
240+ ), class = " qf_report_check " , constraint = constraint )
178241}
179242
180243# ' Check that a constraint is satisfied, ignoring exceptions
@@ -190,6 +253,65 @@ check_constraint_strict <- function(constraint, table) {
190253 UseMethod(" check_constraint_strict" )
191254}
192255
256+ # ' @noRd
257+ # ' @export
258+ print.qf_report_check <- function (x , max_width = getOption(" width" ), ... ) {
259+ print(attr(x , " constraint" ))
260+ if (x $ satisfied ) {
261+ cat(" All rows satisfied" )
262+ } else if (x $ handled ) {
263+ cat(" All rows satisfied or excepted" )
264+ } else {
265+ message <- " Violating rows: "
266+ indices <- format_indices(
267+ which(! x $ rows $ handled ),
268+ max_width - nchar(message )
269+ )
270+ cat0(message , indices )
271+ }
272+ cat(" \n " )
273+ invisible (x )
274+ }
275+
276+ # ' @noRd
277+ # ' @export
278+ print.qf_report_check_table <- function (x , ... ) {
279+ report_check_header(x )
280+ for (cstr in x ) print(cstr )
281+ invisible (x )
282+ }
283+
284+ # ' @noRd
285+ # ' @export
286+ print.qf_report_check_dataset <- function (x , ... ) {
287+ report_check_header(unlist(x , recursive = FALSE ))
288+ indent <- " "
289+ for (i in 1 : length(x )) {
290+ if (any(vapply(x [[i ]], \(el ) ! el $ handled , logical (1 )))) {
291+ cat0(" => In table '" , names(x )[[i ]], " ':\n " )
292+ for (j in 1 : length(x [[i ]])) {
293+ if (! x [[i ]][[j ]]$ handled ) {
294+ report_check_constraint <- utils :: capture.output({
295+ cat0(" [[" , j , " ]] " )
296+ print(x [[i ]][[j ]], max_width = getOption(" width" ) - nchar(indent ))
297+ })
298+ cat(paste0(indent , report_check_constraint ), sep = " \n " )
299+ }
300+ }
301+ }
302+ }
303+ }
304+
305+ report_check_header <- function (x ) {
306+ satisfied <- purrr :: map_lgl(x , " satisfied" )
307+ handled <- purrr :: map_lgl(x , " handled" )
308+ n_satisfied <- sum(satisfied )
309+ n_excepted <- sum(handled ) - sum(satisfied )
310+ n_violated <- length(x ) - sum(handled )
311+ cat(" Constraint check report:" , n_satisfied , " satisfied" , " /" , n_excepted ,
312+ " excepted" , " /" , n_violated , " violated:" , " \n " )
313+ }
314+
193315
194316
195317# Exception handling ===========================================================
@@ -216,15 +338,6 @@ exceptions <- function(x) {
216338`exceptions<-` <- function (x , value ) {
217339 if (! is_qf_constraint(x ))
218340 stop(" 'x' must be a <qf_constraint>, not " , typeof(x ))
219- exception_list <-
220- if (is_qf_exception(value ))
221- list (value )
222- else if (is.list(value ))
223- purrr :: walk(value , \(expt ) if (! is_qf_exception(expt )) stop(" 'value' must
224- contain only <qf_exception> objects, not " , typeof(expt )))
225- else stop(" 'value' must be a <qf_exception> or list of <qf_exception> objects,
226- not " , typeof(value ))
227-
228- attr(x , " exceptions" ) <- exception_list
341+ attr(x , " exceptions" ) <- as_qf_exception_list(value )
229342 x
230343}
0 commit comments