Skip to main content

zai_rs/model/ocr/
response.rs

1use serde::{Deserialize, Serialize};
2use validator::Validate;
3
4/// OCR recognition response
5#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
6pub struct OcrResponse {
7    /// Task ID
8    pub task_id: String,
9
10    /// Message (e.g., success or error description)
11    pub message: String,
12
13    /// Status identifier
14    pub status: OcrStatus,
15
16    /// Number of recognition results
17    pub words_result_num: i64,
18
19    /// Text recognition results
20    #[serde(skip_serializing_if = "Option::is_none")]
21    pub words_result: Option<Vec<WordsResultItem>>,
22}
23
24/// OCR task status defined by the response contract.
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
26#[serde(rename_all = "lowercase")]
27pub enum OcrStatus {
28    /// Recognition completed successfully.
29    Succeeded,
30    /// Recognition failed.
31    Failed,
32}
33
34/// One recognized text line within an [`OcrResponse`].
35#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
36pub struct WordsResultItem {
37    /// Location coordinates of the text line
38    pub location: Location,
39
40    /// Recognized text content
41    pub words: String,
42
43    /// Confidence information required by the frozen item schema.
44    pub probability: Probability,
45}
46
47/// Bounding-box rectangle of a recognized text line.
48#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
49pub struct Location {
50    /// Left coordinate of the rectangle
51    pub left: i64,
52
53    /// Top coordinate of the rectangle
54    pub top: i64,
55
56    /// Width of the rectangle
57    pub width: i64,
58
59    /// Height of the rectangle
60    pub height: i64,
61}
62
63/// Per-character confidence statistics (only returned when `probability=true`).
64#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
65pub struct Probability {
66    /// Average confidence
67    pub average: f64,
68
69    /// Variance of confidence
70    pub variance: f64,
71
72    /// Minimum confidence
73    pub min: f64,
74}
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79
80    #[test]
81    fn top_level_required_fields_and_status_enum_are_enforced() {
82        let valid = serde_json::json!({
83            "task_id": "ocr-1",
84            "message": "ok",
85            "status": "succeeded",
86            "words_result_num": 0
87        });
88        assert!(serde_json::from_value::<OcrResponse>(valid).is_ok());
89        assert!(serde_json::from_str::<OcrResponse>("{}").is_err());
90        assert!(
91            serde_json::from_value::<OcrResponse>(serde_json::json!({
92                "task_id": "ocr-1",
93                "message": "ok",
94                "status": "SUCCESS",
95                "words_result_num": 0
96            }))
97            .is_err()
98        );
99    }
100
101    #[test]
102    fn result_item_enforces_nested_required_fields() {
103        assert!(serde_json::from_str::<WordsResultItem>(r#"{"words":"hello"}"#).is_err());
104        let item = serde_json::json!({
105            "location": {"left": 1, "top": 2, "width": 3, "height": 4},
106            "words": "hello",
107            "probability": {"average": 0.9, "variance": 0.1, "min": 0.7}
108        });
109        assert!(serde_json::from_value::<WordsResultItem>(item).is_ok());
110    }
111}