11#![ doc = include_str ! ( "../README.md" ) ]
22
3+ mod error;
34mod formatting;
45
6+ pub use error:: OgImageError ;
7+
58use crate :: formatting:: { format_bytes, format_number} ;
6- use anyhow:: { Context , anyhow} ;
79use bytes:: Bytes ;
810use crates_io_env_vars:: var;
911use minijinja:: { Environment , context} ;
@@ -121,10 +123,10 @@ impl OgImageGenerator {
121123 /// use crates_io_og_image::OgImageGenerator;
122124 ///
123125 /// let generator = OgImageGenerator::from_environment()?;
124- /// # Ok::<(), anyhow::Error >(())
126+ /// # Ok::<(), crates_io_og_image::OgImageError >(())
125127 /// ```
126- pub fn from_environment ( ) -> anyhow :: Result < Self > {
127- if let Some ( path) = var ( "TYPST_PATH" ) ? {
128+ pub fn from_environment ( ) -> Result < Self , OgImageError > {
129+ if let Some ( path) = var ( "TYPST_PATH" ) . map_err ( OgImageError :: EnvVarError ) ? {
128130 Ok ( Self :: new ( PathBuf :: from ( path) ) )
129131 } else {
130132 Ok ( Self :: default ( ) )
@@ -140,7 +142,7 @@ impl OgImageGenerator {
140142 & self ,
141143 data : & ' a OgImageData < ' _ > ,
142144 assets_dir : & std:: path:: Path ,
143- ) -> anyhow :: Result < HashMap < & ' a str , String > > {
145+ ) -> Result < HashMap < & ' a str , String > , OgImageError > {
144146 let mut avatar_map = HashMap :: new ( ) ;
145147
146148 let client = reqwest:: Client :: new ( ) ;
@@ -155,18 +157,27 @@ impl OgImageGenerator {
155157 Bytes :: from_static ( include_bytes ! ( "../assets/test-avatar.png" ) )
156158 } else {
157159 // Download the avatar from the URL
158- let response = client. get ( * avatar) . send ( ) . await ;
159- let response = response
160- . with_context ( || format ! ( "Failed to download avatar from URL: {avatar}" ) ) ?;
161-
162- response. bytes ( ) . await . with_context ( || {
163- format ! ( "Failed to read avatar bytes from URL: {avatar}" )
160+ let response = client. get ( * avatar) . send ( ) . await . map_err ( |err| {
161+ OgImageError :: AvatarDownloadError {
162+ url : ( * avatar) . to_string ( ) ,
163+ source : err,
164+ }
165+ } ) ?;
166+
167+ let bytes = response. bytes ( ) . await ;
168+ bytes. map_err ( |err| OgImageError :: AvatarDownloadError {
169+ url : ( * avatar) . to_string ( ) ,
170+ source : err,
164171 } ) ?
165172 } ;
166173
167174 // Write the bytes to the avatar file
168- let result = fs:: write ( & avatar_path, bytes) . await ;
169- result. with_context ( || format ! ( "Failed to write avatar to {avatar_path:?}" ) ) ?;
175+ fs:: write ( & avatar_path, bytes) . await . map_err ( |err| {
176+ OgImageError :: AvatarWriteError {
177+ path : avatar_path. clone ( ) ,
178+ source : err,
179+ }
180+ } ) ?;
170181
171182 // Store the mapping from the avatar source to the numbered filename
172183 avatar_map. insert ( * avatar, filename) ;
@@ -184,7 +195,7 @@ impl OgImageGenerator {
184195 & self ,
185196 data : & OgImageData < ' _ > ,
186197 avatar_map : & HashMap < & str , String > ,
187- ) -> anyhow :: Result < String > {
198+ ) -> Result < String , OgImageError > {
188199 let template = TEMPLATE_ENV . get_template ( "og-image.typ" ) ?;
189200 let rendered = template. render ( context ! { data, avatar_map } ) ?;
190201 Ok ( rendered)
@@ -199,10 +210,10 @@ impl OgImageGenerator {
199210 /// # Examples
200211 ///
201212 /// ```no_run
202- /// use crates_io_og_image::{OgImageGenerator, OgImageData, OgImageAuthorData};
213+ /// use crates_io_og_image::{OgImageGenerator, OgImageData, OgImageAuthorData, OgImageError };
203214 ///
204215 /// # #[tokio::main]
205- /// # async fn main() -> anyhow:: Result<()> {
216+ /// # async fn main() -> Result<(), OgImageError > {
206217 /// let generator = OgImageGenerator::default();
207218 /// let data = OgImageData {
208219 /// name: "my-crate",
@@ -220,9 +231,9 @@ impl OgImageGenerator {
220231 /// # Ok(())
221232 /// # }
222233 /// ```
223- pub async fn generate ( & self , data : OgImageData < ' _ > ) -> anyhow :: Result < NamedTempFile > {
234+ pub async fn generate ( & self , data : OgImageData < ' _ > ) -> Result < NamedTempFile , OgImageError > {
224235 // Create a temporary folder
225- let temp_dir = tempfile:: tempdir ( ) ?;
236+ let temp_dir = tempfile:: tempdir ( ) . map_err ( OgImageError :: TempDirError ) ?;
226237
227238 // Create assets directory and copy logo and icons
228239 let assets_dir = temp_dir. path ( ) . join ( "assets" ) ;
@@ -253,7 +264,7 @@ impl OgImageGenerator {
253264 fs:: write ( & typ_file_path, rendered) . await ?;
254265
255266 // Create a named temp file for the output PNG
256- let output_file = NamedTempFile :: new ( ) ?;
267+ let output_file = NamedTempFile :: new ( ) . map_err ( OgImageError :: TempFileError ) ?;
257268
258269 // Run typst compile command
259270 let output = Command :: new ( & self . typst_binary_path )
@@ -263,11 +274,17 @@ impl OgImageGenerator {
263274 . arg ( & typ_file_path)
264275 . arg ( output_file. path ( ) )
265276 . output ( )
266- . await ?;
277+ . await
278+ . map_err ( OgImageError :: TypstNotFound ) ?;
267279
268280 if !output. status . success ( ) {
269- let stderr = String :: from_utf8_lossy ( & output. stderr ) ;
270- return Err ( anyhow ! ( "typst compile failed: {stderr}" ) ) ;
281+ let stderr = String :: from_utf8_lossy ( & output. stderr ) . to_string ( ) ;
282+ let stdout = String :: from_utf8_lossy ( & output. stdout ) . to_string ( ) ;
283+ return Err ( OgImageError :: TypstCompilationError {
284+ stderr,
285+ stdout,
286+ exit_code : output. status . code ( ) ,
287+ } ) ;
271288 }
272289
273290 Ok ( output_file)
0 commit comments