Skip to main content

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/// Errors returned by this library.
70///
71/// Conversion failures are not errors. They are recorded outcomes in
72/// the manifest. This type covers the cases where the pipeline itself
73/// cannot proceed.
74#[derive(Debug, ThisError)]
75pub enum Error {
76    /// A filesystem operation failed.
77    #[error("{op} {path}: {source}")]
78    Io {
79        /// The operation that failed, such as `open` or `read`.
80        op: &'static str,
81        /// The path the operation touched.
82        path: PathBuf,
83        /// The underlying error.
84        #[source]
85        source: std::io::Error,
86    },
87
88    /// A rules file failed to parse or validate.
89    #[error("rules file {name}: {message}")]
90    Rules {
91        /// The rules file name.
92        name: String,
93        /// What went wrong.
94        message: String,
95    },
96
97    /// A manifest shard failed to parse or validate.
98    #[error("manifest {path} line {line}: {message}")]
99    Manifest {
100        /// The shard path.
101        path: PathBuf,
102        /// The one-based line number.
103        line: usize,
104        /// What went wrong.
105        message: String,
106    },
107
108    /// A manifest record could not be encoded.
109    #[error("encode manifest record for {path}: {message}")]
110    Encode {
111        /// The shard path.
112        path: PathBuf,
113        /// What went wrong.
114        message: String,
115    },
116
117    /// A division name is not usable as a shard file name.
118    #[error("invalid division name {name:?}: use ASCII letters, digits, hyphen, underscore")]
119    Division {
120        /// The rejected name.
121        name: String,
122    },
123
124    /// The run layout is invalid.
125    #[error("invalid layout: {message}")]
126    Layout {
127        /// What is wrong with the layout.
128        message: String,
129    },
130
131    /// A bundle verb examined its input and refused it, naming every
132    /// offender. Refusal is an outcome, distinct from a verb that
133    /// could not run.
134    #[error("{verb} refused: {}", problems.join("; "))]
135    Refused {
136        /// The verb that refused.
137        verb: &'static str,
138        /// Every offending file, record, or field.
139        problems: Vec<String>,
140    },
141}
142
143impl Error {
144    pub(crate) fn io(op: &'static str, path: &Path, source: std::io::Error) -> Self {
145        Error::Io {
146            op,
147            path: path.to_path_buf(),
148            source,
149        }
150    }
151}
152
153/// The result type used across this library.
154pub type Result<T> = std::result::Result<T, Error>;