text_mirror/lib.rs
1//! Converts binary and rich files into plain text and builds a replica
2//! text-mirror tree beside a provenance manifest.
3//!
4//! The manifest schema `text-mirror/manifest@1` is the public
5//! interface. The [`manifest`] module holds its serde types. See
6//! `docs/DESIGN.md` for the architecture.
7//!
8//! In-process converters cover text, Office and OpenDocument
9//! documents, and spreadsheets. Text-layer PDF and the media adapters
10//! run behind the sandbox [`runner`] in a jailed worker. A format no
11//! converter claims, or one whose adapter engine is not pinned yet, is
12//! inventoried by [`detect`] and recorded as `unsupported`, so the
13//! manifest always reports the true denominator.
14
15#![forbid(unsafe_code)]
16
17use std::path::{Path, PathBuf};
18
19use thiserror::Error as ThisError;
20
21pub mod bundle;
22pub mod convert;
23pub mod detect;
24pub mod hash;
25pub mod manifest;
26pub mod mirror;
27pub mod pipeline;
28pub mod report;
29#[cfg(unix)]
30pub mod runner;
31pub mod segments;
32pub mod walk;
33
34/// The typed adapter request and response bodies, re-exported so
35/// transcript rendering and tests reach them on every platform. On a
36/// unix target these are the same types the [`runner`] protocol uses.
37#[cfg(unix)]
38pub use runner::protocol::bodies as runner_bodies;
39
40/// Standalone copies of the transcript-shaped bodies for platforms
41/// without a jail backend, so transcript rendering still type-checks.
42#[cfg(not(unix))]
43pub mod runner_bodies {
44 use serde::{Deserialize, Serialize};
45
46 /// One transcribed speech segment.
47 #[derive(Debug, Clone, Serialize, Deserialize)]
48 pub struct AsrSegment {
49 /// Segment start in seconds.
50 pub start_seconds: f64,
51 /// Segment end in seconds.
52 pub end_seconds: f64,
53 /// One-based speaker index.
54 pub speaker: u32,
55 /// The transcribed text.
56 pub text: String,
57 }
58
59 /// One deduplicated on-screen text state.
60 #[derive(Debug, Clone, Serialize, Deserialize)]
61 pub struct ScreenState {
62 /// When this state first appeared, in seconds.
63 pub first_seen_seconds: f64,
64 /// The on-screen text.
65 pub text: String,
66 }
67}
68
69/// The BLAKE3 of the provider jail profile template on this platform,
70/// or `None` where no provider profile is measured.
71pub(crate) fn runner_template_blake3() -> Option<String> {
72 #[cfg(unix)]
73 {
74 runner::provider_profile_template_blake3()
75 }
76 #[cfg(not(unix))]
77 {
78 None
79 }
80}
81
82/// Errors returned by this library.
83///
84/// Conversion failures are not errors. They are recorded outcomes in
85/// the manifest. This type covers the cases where the pipeline itself
86/// cannot proceed.
87#[derive(Debug, ThisError)]
88pub enum Error {
89 /// A filesystem operation failed.
90 #[error("{op} {path}: {source}")]
91 Io {
92 /// The operation that failed, such as `open` or `read`.
93 op: &'static str,
94 /// The path the operation touched.
95 path: PathBuf,
96 /// The underlying error.
97 #[source]
98 source: std::io::Error,
99 },
100
101 /// A rules file failed to parse or validate.
102 #[error("rules file {name}: {message}")]
103 Rules {
104 /// The rules file name.
105 name: String,
106 /// What went wrong.
107 message: String,
108 },
109
110 /// A manifest shard failed to parse or validate.
111 #[error("manifest {path} line {line}: {message}")]
112 Manifest {
113 /// The shard path.
114 path: PathBuf,
115 /// The one-based line number.
116 line: usize,
117 /// What went wrong.
118 message: String,
119 },
120
121 /// A manifest record could not be encoded.
122 #[error("encode manifest record for {path}: {message}")]
123 Encode {
124 /// The shard path.
125 path: PathBuf,
126 /// What went wrong.
127 message: String,
128 },
129
130 /// A division name is not usable as a shard file name.
131 #[error("invalid division name {name:?}: use ASCII letters, digits, hyphen, underscore")]
132 Division {
133 /// The rejected name.
134 name: String,
135 },
136
137 /// The run layout is invalid.
138 #[error("invalid layout: {message}")]
139 Layout {
140 /// What is wrong with the layout.
141 message: String,
142 },
143
144 /// A bundle verb examined its input and refused it, naming every
145 /// offender. Refusal is an outcome, distinct from a verb that
146 /// could not run.
147 #[error("{verb} refused: {}", problems.join("; "))]
148 Refused {
149 /// The verb that refused.
150 verb: &'static str,
151 /// Every offending file, record, or field.
152 problems: Vec<String>,
153 },
154}
155
156impl Error {
157 pub(crate) fn io(op: &'static str, path: &Path, source: std::io::Error) -> Self {
158 Error::Io {
159 op,
160 path: path.to_path_buf(),
161 source,
162 }
163 }
164}
165
166/// The result type used across this library.
167pub type Result<T> = std::result::Result<T, Error>;