1use crate::field::document_format::DocumentFormat;
12use crate::field::observe::{Representation, Selector};
13
14#[derive(Debug, Clone, Copy)]
16pub struct SelectorCapability {
17 pub selector: &'static str,
19 pub representations: &'static [&'static str],
21}
22
23#[derive(Debug, Clone)]
25pub struct Capabilities {
26 pub format: DocumentFormat,
28 pub compiled: bool,
30 pub selectors: Vec<SelectorCapability>,
32 pub profiles: Vec<String>,
34 pub native_selectors: Vec<&'static str>,
36}
37
38impl Capabilities {
39 pub fn to_json(&self) -> String {
41 let selectors = self
42 .selectors
43 .iter()
44 .map(|s| {
45 let reps = s
46 .representations
47 .iter()
48 .map(|r| format!("\"{r}\""))
49 .collect::<Vec<_>>()
50 .join(",");
51 format!(
52 "{{\"selector\":\"{}\",\"representations\":[{reps}]}}",
53 s.selector
54 )
55 })
56 .collect::<Vec<_>>()
57 .join(",");
58 let profiles = self
59 .profiles
60 .iter()
61 .map(|p| format!("\"{}\"", crate::field::provenance::json_escape(p)))
62 .collect::<Vec<_>>()
63 .join(",");
64 let native = self
65 .native_selectors
66 .iter()
67 .map(|s| format!("\"{s}\""))
68 .collect::<Vec<_>>()
69 .join(",");
70 format!(
71 concat!(
72 "{{",
73 "\"root\":\"document\",",
74 "\"format\":\"{}\",",
75 "\"adapter\":\"{}\",",
76 "\"compiled\":{},",
77 "\"selectors\":[{}],",
78 "\"profiles\":[{}],",
79 "\"native_selectors\":[{}]",
80 "}}"
81 ),
82 self.format.name(),
83 self.format.adapter(),
84 self.compiled,
85 selectors,
86 profiles,
87 native
88 )
89 }
90}
91
92const DOCX_NATIVE: &[&str] = &[
93 "docx-story",
94 "docx-paragraph",
95 "docx-table",
96 "docx-cell",
97 "docx-find",
98 "package-part",
99 "relationship",
100 "member",
101];
102const EPUB_NATIVE: &[&str] = &[
103 "epub-package",
104 "epub-manifest-item",
105 "epub-spine-item",
106 "epub-nav",
107 "epub-nav-node",
108 "epub-resource",
109 "epub-block",
110 "epub-cell",
111 "epub-link",
112 "epub-find",
113 "member",
114];
115const ODT_NATIVE: &[&str] = &[
116 "odt-part",
117 "odt-paragraph",
118 "odt-heading",
119 "odt-table",
120 "odt-cell",
121 "odt-list",
122 "odt-find",
123 "package-part",
124 "member",
125];
126const PDF_NATIVE: &[&str] = &[
127 "document",
128 "object",
129 "stream",
130 "revision",
131 "revisions",
132 "page",
133 "byte-range",
134 "text-match",
135];
136const ODS_NATIVE: &[&str] = &[
137 "ods-sheet",
138 "ods-cell",
139 "ods-find",
140 "ods-styles",
141 "ods-named-expressions",
142 "ods-comments",
143 "package-part",
144 "member",
145];
146const XLSX_NATIVE: &[&str] = &[
147 "xlsx-sheet",
148 "xlsx-cell",
149 "xlsx-find",
150 "xlsx-styles",
151 "xlsx-defined-names",
152 "xlsx-external-rels",
153 "xlsx-comments",
154 "xlsx-hyperlinks",
155 "xlsx-tables",
156 "xlsx-drawing",
157 "package-part",
158 "relationship",
159 "member",
160];
161const PPTX_NATIVE: &[&str] = &[
162 "pptx-slide",
163 "pptx-shape",
164 "pptx-notes",
165 "pptx-layouts",
166 "pptx-masters",
167 "pptx-theme",
168 "pptx-media",
169 "pptx-tables",
170 "pptx-find",
171 "package-part",
172 "relationship",
173 "member",
174];
175const ODP_NATIVE: &[&str] = &[
176 "odp-slide",
177 "odp-shape",
178 "odp-notes",
179 "odp-masters",
180 "odp-media",
181 "odp-tables",
182 "odp-find",
183 "package-part",
184 "member",
185];
186const JSON_NATIVE: &[&str] = &["json-pointer", "json-node", "json-find"];
187const JSON5_NATIVE: &[&str] = &[
188 "json5-pointer",
189 "json5-node",
190 "json5-find",
191 "json5-comments",
192];
193const YAML_NATIVE: &[&str] = &[
194 "yaml-path",
195 "yaml-node",
196 "yaml-documents",
197 "yaml-anchor",
198 "yaml-find",
199];
200const CSV_NATIVE: &[&str] = &["csv-row", "csv-cell", "csv-header", "csv-range", "csv-find"];
201const MARKDOWN_NATIVE: &[&str] = &["md-heading", "md-block", "md-code", "md-link", "md-find"];
202const XML_NATIVE: &[&str] = &[
203 "xml-path",
204 "xml-element",
205 "xml-attr",
206 "xml-namespaces",
207 "xml-find",
208];
209const HTML_NATIVE: &[&str] = &[
210 "html-path",
211 "html-element",
212 "html-attr",
213 "html-scripts",
214 "html-find",
215];
216const TOML_NATIVE: &[&str] = &["toml-path", "toml-table", "toml-find"];
217const JSONL_NATIVE: &[&str] = &["jsonl-line", "jsonl-pointer", "jsonl-find"];
218const EML_NATIVE: &[&str] = &[
219 "eml-header",
220 "eml-part",
221 "eml-attachments",
222 "eml-body",
223 "eml-find",
224];
225const PARQUET_NATIVE: &[&str] = &[
226 "parquet-schema",
227 "parquet-column",
228 "parquet-row-group",
229 "parquet-cell",
230];
231const ARROW_NATIVE: &[&str] = &["arrow-schema", "arrow-column", "arrow-batch", "arrow-cell"];
232const CBOR_NATIVE: &[&str] = &["cbor-pointer", "cbor-node", "cbor-find"];
233const MSGPACK_NATIVE: &[&str] = &["msgpack-pointer", "msgpack-node", "msgpack-find"];
234const CONFIG_NATIVE: &[&str] = &[
235 "config-line",
236 "config-entry",
237 "config-section",
238 "config-find",
239];
240const FEED_NATIVE: &[&str] = &[
241 "feed-channel",
242 "feed-field",
243 "feed-entry",
244 "feed-entry-field",
245 "feed-find",
246];
247const GEOJSON_NATIVE: &[&str] = &[
248 "geojson-type",
249 "geojson-feature",
250 "geojson-geometry",
251 "geojson-coordinates",
252 "geojson-property",
253 "geojson-find",
254];
255const GIS_NATIVE: &[&str] = &[
256 "gis-root",
257 "gis-field",
258 "gis-record",
259 "gis-record-field",
260 "gis-point",
261 "gis-find",
262];
263const NOTEBOOK_NATIVE: &[&str] = &[
264 "notebook-nbformat",
265 "notebook-cell",
266 "notebook-cell-type",
267 "notebook-cell-source",
268 "notebook-cell-output",
269 "notebook-find",
270];
271const FIXEDWIDTH_NATIVE: &[&str] = &[
272 "fixedwidth-row",
273 "fixedwidth-cell",
274 "fixedwidth-header",
275 "fixedwidth-columns",
276 "fixedwidth-range",
277 "fixedwidth-find",
278];
279const RST_NATIVE: &[&str] = &[
280 "rst-heading",
281 "rst-block",
282 "rst-directive",
283 "rst-inline",
284 "rst-find",
285];
286const ASCIIDOC_NATIVE: &[&str] = &[
287 "adoc-heading",
288 "adoc-block",
289 "adoc-attribute",
290 "adoc-inline",
291 "adoc-find",
292];
293const MDX_NATIVE: &[&str] = &[
294 "mdx-heading",
295 "mdx-block",
296 "mdx-esm",
297 "mdx-jsx",
298 "mdx-expression",
299 "mdx-find",
300];
301
302const MHTML_NATIVE: &[&str] = &[
303 "mhtml-root",
304 "mhtml-resource",
305 "mhtml-location",
306 "mhtml-find",
307];
308
309const LOGSTREAM_NATIVE: &[&str] = &["logstream-line", "logstream-field", "logstream-find"];
310
311const PKGMETA_NATIVE: &[&str] = &[
312 "pkgmeta-section",
313 "pkgmeta-entry",
314 "pkgmeta-key",
315 "pkgmeta-find",
316];
317
318const APISPEC_NATIVE: &[&str] = &[
319 "apispec-dialect",
320 "apispec-version",
321 "apispec-object",
322 "apispec-ref",
323 "apispec-find",
324];
325
326const BIB_NATIVE: &[&str] = &[
327 "bib-dialect",
328 "bib-entry",
329 "bib-field",
330 "bib-key",
331 "bib-find",
332];
333
334const RTF_NATIVE: &[&str] = &[
335 "rtf-header",
336 "rtf-group",
337 "rtf-control",
338 "rtf-hex",
339 "rtf-unicode",
340 "rtf-text",
341 "rtf-dest",
342 "rtf-find",
343];
344
345const CONTAINER_NATIVE: &[&str] = &["container-dialect", "container-entry", "container-find"];
346
347const HDF_NATIVE: &[&str] = &[
348 "hdf-dialect",
349 "hdf-object",
350 "hdf-dimension",
351 "hdf-attribute",
352 "hdf-find",
353];
354
355const CFBF_NATIVE: &[&str] = &["cfbf-dialect", "cfbf-entry", "cfbf-stream", "cfbf-find"];
356
357const DOC_NATIVE: &[&str] = &[
358 "doc-fib",
359 "doc-piece",
360 "doc-section",
361 "doc-text",
362 "doc-find",
363];
364
365const XLS_NATIVE: &[&str] = &[
366 "xls-biff",
367 "xls-sheet",
368 "xls-cell",
369 "xls-record",
370 "xls-find",
371];
372
373const PPT_NATIVE: &[&str] = &[
374 "ppt-document",
375 "ppt-slide",
376 "ppt-run",
377 "ppt-record",
378 "ppt-list",
379 "ppt-find",
380];
381
382const MSG_NATIVE: &[&str] = &[
383 "msg-summary",
384 "msg-property",
385 "msg-recipient",
386 "msg-attachment",
387 "msg-find",
388];
389
390const COMMON_METADATA: &[&str] = &["metadata"];
391const COMMON_TEXT: &[&str] = &["text"];
392const COMMON_TEXT_META: &[&str] = &["text", "metadata"];
393const COMMON_RESOURCE: &[&str] = &["metadata"];
394const COMMON_RESOURCE_BYTES: &[&str] = &["metadata", "exact", "decoded"];
395const SEARCH: &[&str] = &["text"];
396
397fn caps(selector: &'static str, representations: &'static [&'static str]) -> SelectorCapability {
398 SelectorCapability {
399 selector,
400 representations,
401 }
402}
403
404pub fn capabilities_for_format(format: DocumentFormat) -> Capabilities {
406 let compiled = format.compiled();
407 if !compiled {
408 return Capabilities {
409 format,
410 compiled,
411 selectors: Vec::new(),
412 profiles: Vec::new(),
413 native_selectors: Vec::new(),
414 };
415 }
416 let (selectors, profiles, native_selectors): (Vec<SelectorCapability>, Vec<String>, Vec<&str>) =
417 match format {
418 DocumentFormat::Pdf => (
419 vec![
420 caps("metadata", COMMON_METADATA),
421 caps("text", COMMON_TEXT),
422 caps("search-match", SEARCH),
423 ],
424 Vec::new(),
425 PDF_NATIVE.to_vec(),
426 ),
427 DocumentFormat::Docx => (
428 vec![
429 caps("metadata", COMMON_METADATA),
430 caps("text", COMMON_TEXT),
431 caps("heading", COMMON_TEXT_META),
432 caps("block", COMMON_TEXT_META),
433 caps("table", COMMON_TEXT_META),
434 caps("cell", COMMON_TEXT_META),
435 caps("resource", COMMON_RESOURCE),
436 caps("link", COMMON_METADATA),
437 caps("search-match", SEARCH),
438 ],
439 docx_profiles(),
440 DOCX_NATIVE.to_vec(),
441 ),
442 DocumentFormat::Epub => (
443 vec![
444 caps("metadata", COMMON_METADATA),
445 caps("text", COMMON_TEXT),
446 caps("heading", COMMON_TEXT_META),
447 caps("block", COMMON_TEXT_META),
448 caps("table", COMMON_TEXT_META),
449 caps("cell", COMMON_TEXT_META),
450 caps("resource", COMMON_RESOURCE_BYTES),
451 caps("link", COMMON_METADATA),
452 caps("search-match", SEARCH),
453 ],
454 epub_profiles(),
455 EPUB_NATIVE.to_vec(),
456 ),
457 DocumentFormat::Odt => (
458 vec![
459 caps("metadata", COMMON_METADATA),
460 caps("text", COMMON_TEXT),
461 caps("heading", COMMON_TEXT_META),
462 caps("block", COMMON_TEXT_META),
463 caps("table", COMMON_TEXT_META),
464 caps("cell", COMMON_TEXT_META),
465 caps("resource", COMMON_RESOURCE),
466 caps("link", COMMON_METADATA),
467 caps("search-match", SEARCH),
468 ],
469 odt_profiles(),
470 ODT_NATIVE.to_vec(),
471 ),
472 DocumentFormat::Ods => (
473 vec![
474 caps("metadata", COMMON_METADATA),
475 caps("text", COMMON_TEXT),
476 caps("table", COMMON_TEXT_META),
477 caps("cell", COMMON_TEXT_META),
478 caps("search-match", SEARCH),
479 ],
480 ods_profiles(),
481 ODS_NATIVE.to_vec(),
482 ),
483 DocumentFormat::Xlsx => (
484 vec![
485 caps("metadata", COMMON_METADATA),
486 caps("text", COMMON_TEXT),
487 caps("table", COMMON_TEXT_META),
488 caps("cell", COMMON_TEXT_META),
489 caps("search-match", SEARCH),
490 ],
491 xlsx_profiles(),
492 XLSX_NATIVE.to_vec(),
493 ),
494 DocumentFormat::Pptx => (
495 vec![
496 caps("metadata", COMMON_METADATA),
497 caps("text", COMMON_TEXT),
498 caps("table", COMMON_TEXT_META),
499 caps("cell", COMMON_TEXT_META),
500 caps("search-match", SEARCH),
501 ],
502 pptx_profiles(),
503 PPTX_NATIVE.to_vec(),
504 ),
505 DocumentFormat::Odp => (
506 vec![
507 caps("metadata", COMMON_METADATA),
508 caps("text", COMMON_TEXT),
509 caps("table", COMMON_TEXT_META),
510 caps("cell", COMMON_TEXT_META),
511 caps("search-match", SEARCH),
512 ],
513 odp_profiles(),
514 ODP_NATIVE.to_vec(),
515 ),
516 DocumentFormat::Json => (
517 vec![
518 caps("metadata", COMMON_METADATA),
519 caps("text", COMMON_TEXT),
520 caps("search-match", SEARCH),
521 ],
522 Vec::new(),
523 JSON_NATIVE.to_vec(),
524 ),
525 DocumentFormat::Json5 => (
526 vec![
527 caps("metadata", COMMON_METADATA),
528 caps("text", COMMON_TEXT),
529 caps("search-match", SEARCH),
530 ],
531 Vec::new(),
532 JSON5_NATIVE.to_vec(),
533 ),
534 DocumentFormat::Yaml => (
535 vec![
536 caps("metadata", COMMON_METADATA),
537 caps("text", COMMON_TEXT),
538 caps("search-match", SEARCH),
539 ],
540 Vec::new(),
541 YAML_NATIVE.to_vec(),
542 ),
543 DocumentFormat::Csv => (
544 vec![
545 caps("metadata", COMMON_METADATA),
546 caps("text", COMMON_TEXT),
547 caps("table", COMMON_TEXT_META),
548 caps("cell", COMMON_TEXT_META),
549 caps("search-match", SEARCH),
550 ],
551 Vec::new(),
552 CSV_NATIVE.to_vec(),
553 ),
554 DocumentFormat::Markdown => (
555 vec![
556 caps("metadata", COMMON_METADATA),
557 caps("text", COMMON_TEXT),
558 caps("heading", COMMON_TEXT_META),
559 caps("block", COMMON_TEXT_META),
560 caps("search-match", SEARCH),
561 ],
562 Vec::new(),
563 MARKDOWN_NATIVE.to_vec(),
564 ),
565 DocumentFormat::Xml => (
566 vec![
567 caps("metadata", COMMON_METADATA),
568 caps("text", COMMON_TEXT),
569 caps("search-match", SEARCH),
570 ],
571 Vec::new(),
572 XML_NATIVE.to_vec(),
573 ),
574 DocumentFormat::Html => (
575 vec![
576 caps("metadata", COMMON_METADATA),
577 caps("text", COMMON_TEXT),
578 caps("heading", COMMON_TEXT_META),
579 caps("link", COMMON_METADATA),
580 caps("search-match", SEARCH),
581 ],
582 Vec::new(),
583 HTML_NATIVE.to_vec(),
584 ),
585 DocumentFormat::Toml => (
586 vec![
587 caps("metadata", COMMON_METADATA),
588 caps("text", COMMON_TEXT),
589 caps("search-match", SEARCH),
590 ],
591 Vec::new(),
592 TOML_NATIVE.to_vec(),
593 ),
594 DocumentFormat::Jsonl => (
595 vec![
596 caps("metadata", COMMON_METADATA),
597 caps("text", COMMON_TEXT),
598 caps("search-match", SEARCH),
599 ],
600 Vec::new(),
601 JSONL_NATIVE.to_vec(),
602 ),
603 DocumentFormat::Eml => (
604 vec![
605 caps("metadata", COMMON_METADATA),
606 caps("text", COMMON_TEXT),
607 caps("resource", COMMON_RESOURCE_BYTES),
608 caps("search-match", SEARCH),
609 ],
610 Vec::new(),
611 EML_NATIVE.to_vec(),
612 ),
613 DocumentFormat::Parquet => (
614 vec![
615 caps("metadata", COMMON_METADATA),
616 caps("text", COMMON_TEXT),
617 caps("table", COMMON_TEXT_META),
618 caps("cell", COMMON_TEXT_META),
619 caps("search-match", SEARCH),
620 ],
621 Vec::new(),
622 PARQUET_NATIVE.to_vec(),
623 ),
624 DocumentFormat::ArrowIpc => (
625 vec![
626 caps("metadata", COMMON_METADATA),
627 caps("text", COMMON_TEXT),
628 caps("table", COMMON_TEXT_META),
629 caps("cell", COMMON_TEXT_META),
630 caps("search-match", SEARCH),
631 ],
632 Vec::new(),
633 ARROW_NATIVE.to_vec(),
634 ),
635 DocumentFormat::Cbor => (
636 vec![
637 caps("metadata", COMMON_METADATA),
638 caps("text", COMMON_TEXT),
639 caps("search-match", SEARCH),
640 ],
641 Vec::new(),
642 CBOR_NATIVE.to_vec(),
643 ),
644 DocumentFormat::Msgpack => (
645 vec![
646 caps("metadata", COMMON_METADATA),
647 caps("text", COMMON_TEXT),
648 caps("search-match", SEARCH),
649 ],
650 Vec::new(),
651 MSGPACK_NATIVE.to_vec(),
652 ),
653 DocumentFormat::Config => (
654 vec![
655 caps("metadata", COMMON_METADATA),
656 caps("text", COMMON_TEXT),
657 caps("search-match", SEARCH),
658 ],
659 Vec::new(),
660 CONFIG_NATIVE.to_vec(),
661 ),
662 DocumentFormat::Feed => (
663 vec![
664 caps("metadata", COMMON_METADATA),
665 caps("text", COMMON_TEXT),
666 caps("search-match", SEARCH),
667 ],
668 Vec::new(),
669 FEED_NATIVE.to_vec(),
670 ),
671 DocumentFormat::Geojson => (
672 vec![
673 caps("metadata", COMMON_METADATA),
674 caps("text", COMMON_TEXT),
675 caps("search-match", SEARCH),
676 ],
677 Vec::new(),
678 GEOJSON_NATIVE.to_vec(),
679 ),
680 DocumentFormat::Gis => (
681 vec![
682 caps("metadata", COMMON_METADATA),
683 caps("text", COMMON_TEXT),
684 caps("search-match", SEARCH),
685 ],
686 Vec::new(),
687 GIS_NATIVE.to_vec(),
688 ),
689 DocumentFormat::Notebook => (
690 vec![
691 caps("metadata", COMMON_METADATA),
692 caps("text", COMMON_TEXT),
693 caps("search-match", SEARCH),
694 ],
695 Vec::new(),
696 NOTEBOOK_NATIVE.to_vec(),
697 ),
698 DocumentFormat::FixedWidth => (
699 vec![
700 caps("metadata", COMMON_METADATA),
701 caps("text", COMMON_TEXT),
702 caps("table", COMMON_TEXT_META),
703 caps("cell", COMMON_TEXT_META),
704 caps("search-match", SEARCH),
705 ],
706 Vec::new(),
707 FIXEDWIDTH_NATIVE.to_vec(),
708 ),
709 DocumentFormat::Rst => (
710 vec![
711 caps("metadata", COMMON_METADATA),
712 caps("text", COMMON_TEXT),
713 caps("heading", COMMON_TEXT_META),
714 caps("block", COMMON_TEXT_META),
715 caps("search-match", SEARCH),
716 ],
717 Vec::new(),
718 RST_NATIVE.to_vec(),
719 ),
720 DocumentFormat::Asciidoc => (
721 vec![
722 caps("metadata", COMMON_METADATA),
723 caps("text", COMMON_TEXT),
724 caps("heading", COMMON_TEXT_META),
725 caps("block", COMMON_TEXT_META),
726 caps("search-match", SEARCH),
727 ],
728 Vec::new(),
729 ASCIIDOC_NATIVE.to_vec(),
730 ),
731 DocumentFormat::Mdx => (
732 vec![
733 caps("metadata", COMMON_METADATA),
734 caps("text", COMMON_TEXT),
735 caps("heading", COMMON_TEXT_META),
736 caps("block", COMMON_TEXT_META),
737 caps("search-match", SEARCH),
738 ],
739 Vec::new(),
740 MDX_NATIVE.to_vec(),
741 ),
742 DocumentFormat::Mhtml => (
743 vec![
744 caps("metadata", COMMON_METADATA),
745 caps("text", COMMON_TEXT),
746 caps("resource", COMMON_RESOURCE_BYTES),
747 caps("search-match", SEARCH),
748 ],
749 Vec::new(),
750 MHTML_NATIVE.to_vec(),
751 ),
752 DocumentFormat::Logstream => (
753 vec![
754 caps("metadata", COMMON_METADATA),
755 caps("text", COMMON_TEXT),
756 caps("search-match", SEARCH),
757 ],
758 Vec::new(),
759 LOGSTREAM_NATIVE.to_vec(),
760 ),
761 DocumentFormat::Pkgmeta => (
762 vec![
763 caps("metadata", COMMON_METADATA),
764 caps("text", COMMON_TEXT),
765 caps("search-match", SEARCH),
766 ],
767 Vec::new(),
768 PKGMETA_NATIVE.to_vec(),
769 ),
770 DocumentFormat::Apispec => (
771 vec![
772 caps("metadata", COMMON_METADATA),
773 caps("text", COMMON_TEXT),
774 caps("search-match", SEARCH),
775 ],
776 Vec::new(),
777 APISPEC_NATIVE.to_vec(),
778 ),
779 DocumentFormat::Bib => (
780 vec![
781 caps("metadata", COMMON_METADATA),
782 caps("text", COMMON_TEXT),
783 caps("search-match", SEARCH),
784 ],
785 Vec::new(),
786 BIB_NATIVE.to_vec(),
787 ),
788 DocumentFormat::Rtf => (
789 vec![
790 caps("metadata", COMMON_METADATA),
791 caps("text", COMMON_TEXT),
792 caps("search-match", SEARCH),
793 ],
794 Vec::new(),
795 RTF_NATIVE.to_vec(),
796 ),
797 DocumentFormat::Container => (
798 vec![
799 caps("metadata", COMMON_METADATA),
800 caps("text", COMMON_TEXT),
801 caps("search-match", SEARCH),
802 ],
803 Vec::new(),
804 CONTAINER_NATIVE.to_vec(),
805 ),
806 DocumentFormat::Hdf => (
807 vec![
808 caps("metadata", COMMON_METADATA),
809 caps("text", COMMON_TEXT),
810 caps("search-match", SEARCH),
811 ],
812 Vec::new(),
813 HDF_NATIVE.to_vec(),
814 ),
815 DocumentFormat::Cfbf => (
816 vec![
817 caps("metadata", COMMON_METADATA),
818 caps("text", COMMON_TEXT),
819 caps("search-match", SEARCH),
820 ],
821 Vec::new(),
822 CFBF_NATIVE.to_vec(),
823 ),
824 DocumentFormat::Doc => (
825 vec![
826 caps("metadata", COMMON_METADATA),
827 caps("text", COMMON_TEXT),
828 caps("search-match", SEARCH),
829 ],
830 Vec::new(),
831 DOC_NATIVE.to_vec(),
832 ),
833 DocumentFormat::Xls => (
834 vec![
835 caps("metadata", COMMON_METADATA),
836 caps("text", COMMON_TEXT),
837 caps("search-match", SEARCH),
838 ],
839 Vec::new(),
840 XLS_NATIVE.to_vec(),
841 ),
842 DocumentFormat::Ppt => (
843 vec![
844 caps("metadata", COMMON_METADATA),
845 caps("text", COMMON_TEXT),
846 caps("search-match", SEARCH),
847 ],
848 Vec::new(),
849 PPT_NATIVE.to_vec(),
850 ),
851 DocumentFormat::Msg => (
852 vec![
853 caps("metadata", COMMON_METADATA),
854 caps("text", COMMON_TEXT),
855 caps("search-match", SEARCH),
856 ],
857 Vec::new(),
858 MSG_NATIVE.to_vec(),
859 ),
860 DocumentFormat::Opaque => (Vec::new(), Vec::new(), Vec::new()),
861 };
862 Capabilities {
863 format,
864 compiled,
865 selectors,
866 profiles,
867 native_selectors,
868 }
869}
870
871#[cfg(feature = "docx")]
872fn docx_profiles() -> Vec<String> {
873 vec![crate::adapter::docx::DocxExtractProfile::DEFAULT.fingerprint()]
874}
875#[cfg(not(feature = "docx"))]
876fn docx_profiles() -> Vec<String> {
877 Vec::new()
878}
879
880#[cfg(feature = "epub")]
881fn epub_profiles() -> Vec<String> {
882 vec![crate::adapter::epub::EpubExtractProfile::DEFAULT.fingerprint()]
883}
884#[cfg(not(feature = "epub"))]
885fn epub_profiles() -> Vec<String> {
886 Vec::new()
887}
888
889#[cfg(feature = "odt")]
890fn odt_profiles() -> Vec<String> {
891 vec![crate::adapter::odt::OdtExtractProfile::DEFAULT.fingerprint()]
892}
893#[cfg(not(feature = "odt"))]
894fn odt_profiles() -> Vec<String> {
895 Vec::new()
896}
897
898#[cfg(feature = "ods")]
899fn ods_profiles() -> Vec<String> {
900 vec![crate::adapter::ods::OdsExtractProfile::DEFAULT.fingerprint()]
901}
902#[cfg(not(feature = "ods"))]
903fn ods_profiles() -> Vec<String> {
904 Vec::new()
905}
906
907#[cfg(feature = "xlsx")]
908fn xlsx_profiles() -> Vec<String> {
909 vec![crate::adapter::xlsx::XlsxExtractProfile::DEFAULT.fingerprint()]
910}
911#[cfg(not(feature = "xlsx"))]
912fn xlsx_profiles() -> Vec<String> {
913 Vec::new()
914}
915
916#[cfg(feature = "pptx")]
917fn pptx_profiles() -> Vec<String> {
918 vec![crate::adapter::pptx::PptxExtractProfile::DEFAULT.fingerprint()]
919}
920#[cfg(not(feature = "pptx"))]
921fn pptx_profiles() -> Vec<String> {
922 Vec::new()
923}
924
925#[cfg(feature = "odp")]
926fn odp_profiles() -> Vec<String> {
927 vec![crate::adapter::odp::OdpExtractProfile::DEFAULT.fingerprint()]
928}
929#[cfg(not(feature = "odp"))]
930fn odp_profiles() -> Vec<String> {
931 Vec::new()
932}
933
934pub fn common_selector_name(selector: &Selector) -> Option<&'static str> {
936 match selector {
937 Selector::Metadata => Some("metadata"),
938 Selector::Text => Some("text"),
939 Selector::Heading(_) => Some("heading"),
940 Selector::Block(_) => Some("block"),
941 Selector::Table(_) => Some("table"),
942 Selector::Cell { .. } => Some("cell"),
943 Selector::Resource(_) => Some("resource"),
944 Selector::Link(_) => Some("link"),
945 Selector::SearchMatch(_) => Some("search-match"),
946 _ => None,
947 }
948}
949
950pub fn common_representations(
953 format: DocumentFormat,
954 selector: &Selector,
955) -> Option<&'static [&'static str]> {
956 if !format.compiled() {
957 return None;
958 }
959 let name = common_selector_name(selector)?;
960 let caps = capabilities_for_format(format);
961 caps.selectors
962 .iter()
963 .find(|c| c.selector == name)
964 .map(|c| c.representations)
965}
966
967pub fn common_supported(
969 format: DocumentFormat,
970 selector: &Selector,
971 representation: Representation,
972) -> bool {
973 common_representations(format, selector)
974 .is_some_and(|reps| reps.contains(&representation.name()))
975}
976
977#[cfg(test)]
978mod tests {
979 use super::*;
980
981 #[test]
982 fn pdf_supports_text_and_search_but_not_tables() {
983 assert!(common_supported(
984 DocumentFormat::Pdf,
985 &Selector::Text,
986 Representation::Text
987 ));
988 assert!(common_supported(
989 DocumentFormat::Pdf,
990 &Selector::SearchMatch("x".into()),
991 Representation::Text
992 ));
993 assert!(!common_supported(
994 DocumentFormat::Pdf,
995 &Selector::Table(0),
996 Representation::Text
997 ));
998 assert!(!common_supported(
999 DocumentFormat::Pdf,
1000 &Selector::Text,
1001 Representation::Metadata
1002 ));
1003 }
1004
1005 #[test]
1006 fn opaque_and_unknown_selectors_are_unsupported() {
1007 assert!(!common_supported(
1008 DocumentFormat::Opaque,
1009 &Selector::Text,
1010 Representation::Text
1011 ));
1012 assert!(!common_supported(
1013 DocumentFormat::Pdf,
1014 &Selector::Document,
1015 Representation::Metadata
1016 ));
1017 }
1018}