citum_engine/api/
style_input.rs1use citum_schema::Style;
9use serde::{Deserialize, Serialize};
10
11#[derive(Debug, Clone, Deserialize, Serialize)]
17#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
18#[serde(tag = "kind", content = "value", rename_all = "lowercase")]
19pub enum StyleInput {
20 Id(String),
23 Uri(String),
26 Path(String),
28 Yaml(String),
30}
31
32impl StyleInput {
33 pub fn resolve_local(&self) -> Result<Style, crate::api::FormatDocumentError> {
45 match self {
46 StyleInput::Path(path) => {
47 let yaml_bytes = std::fs::read(path).map_err(|e| {
48 crate::api::FormatDocumentError::StylePath(format!(
49 "Failed to read style from '{}': {}",
50 path, e
51 ))
52 })?;
53 Style::from_yaml_bytes(&yaml_bytes).map_err(|e| {
54 crate::api::FormatDocumentError::StyleParse(format!(
55 "Failed to parse style from '{}': {}",
56 path, e
57 ))
58 })
59 }
60 StyleInput::Yaml(yaml_str) => {
61 Style::from_yaml_bytes(yaml_str.as_bytes()).map_err(|e| {
62 crate::api::FormatDocumentError::StyleParse(format!(
63 "Failed to parse inline YAML style: {}",
64 e
65 ))
66 })
67 }
68 StyleInput::Id(id) => Err(crate::api::FormatDocumentError::UnresolvedInput(format!(
69 "Style ID '{}' requires resolver chain (not available in engine)",
70 id
71 ))),
72 StyleInput::Uri(uri) => Err(crate::api::FormatDocumentError::UnresolvedInput(format!(
73 "Style URI '{}' requires resolver chain (not available in engine)",
74 uri
75 ))),
76 }
77 }
78}
79
80#[cfg(test)]
81#[allow(
82 clippy::unwrap_used,
83 clippy::expect_used,
84 clippy::panic,
85 reason = "test code uses assertions and panic"
86)]
87mod tests {
88 use super::*;
89 use std::io::Write;
90 use tempfile::NamedTempFile;
91
92 #[test]
93 fn style_input_yaml_resolves_locally() {
94 let yaml_content = r#"---
95info:
96 title: Test Style
97 default-locale: en-us
98"#;
99 let input = StyleInput::Yaml(yaml_content.to_string());
100 let result = input.resolve_local();
101 assert!(result.is_ok());
102 }
103
104 #[test]
105 fn style_input_id_returns_unresolved_error() {
106 let input = StyleInput::Id("apa-7th".to_string());
107 let result = input.resolve_local();
108 match result {
109 Err(crate::api::FormatDocumentError::UnresolvedInput(msg)) => {
110 assert!(msg.contains("Style ID 'apa-7th' requires resolver chain"));
111 }
112 _ => panic!("Expected UnresolvedInput error"),
113 }
114 }
115
116 #[test]
117 fn style_input_uri_returns_unresolved_error() {
118 let input = StyleInput::Uri("https://example.com/style.yaml".to_string());
119 let result = input.resolve_local();
120 match result {
121 Err(crate::api::FormatDocumentError::UnresolvedInput(msg)) => {
122 assert!(msg.contains("https://example.com/style.yaml"));
123 }
124 _ => panic!("Expected UnresolvedInput error"),
125 }
126 }
127
128 #[test]
129 fn style_input_path_reads_and_parses() {
130 let mut tmp = NamedTempFile::new().expect("Failed to create temp file");
131 let yaml_content = r#"---
132info:
133 title: Test Style
134 default-locale: en-us
135"#;
136 tmp.write_all(yaml_content.as_bytes())
137 .expect("Failed to write temp file");
138 tmp.flush().expect("Failed to flush temp file");
139
140 let input = StyleInput::Path(tmp.path().to_string_lossy().to_string());
141 let result = input.resolve_local();
142 assert!(result.is_ok());
143 }
144
145 #[test]
146 fn style_input_path_missing_returns_error() {
147 let input = StyleInput::Path("/nonexistent/path/style.yaml".to_string());
148 let result = input.resolve_local();
149 match result {
150 Err(crate::api::FormatDocumentError::StylePath(msg)) => {
151 assert!(msg.contains("Failed to read style from '/nonexistent/path/style.yaml'"));
152 }
153 _ => panic!("Expected StylePath error"),
154 }
155 }
156
157 #[test]
158 fn style_input_invalid_yaml_returns_parse_error() {
159 let input = StyleInput::Yaml("{ invalid yaml: [".to_string());
160 let result = input.resolve_local();
161 match result {
162 Err(crate::api::FormatDocumentError::StyleParse(msg)) => {
163 assert!(msg.contains("Failed to parse inline YAML style:"));
164 }
165 _ => panic!("Expected StyleParse error"),
166 }
167 }
168}