turbo_tasks_fetch/
error.rs1use anyhow::Result;
2use async_trait::async_trait;
3use turbo_rcstr::{RcStr, rcstr};
4use turbo_tasks::{ResolvedVc, Vc};
5use turbo_tasks_fs::FileSystemPath;
6use turbopack_core::issue::{Issue, IssueSeverity, IssueStage, StyledString};
7
8#[cfg(target_family = "wasm")]
10use crate::wasm_reqwest as reqwest;
11
12#[derive(Debug)]
13#[turbo_tasks::value(shared)]
14pub enum FetchErrorKind {
15 Connect,
16 Timeout,
17 Status(u16),
18 Other,
19}
20
21#[turbo_tasks::value(shared)]
22pub struct FetchError {
23 pub url: ResolvedVc<RcStr>,
24 pub kind: ResolvedVc<FetchErrorKind>,
25 pub detail: ResolvedVc<StyledString>,
26}
27
28impl FetchError {
29 pub(crate) fn from_reqwest_error(error: &reqwest::Error, url: &str) -> FetchError {
30 let kind = if error.is_connect() {
31 FetchErrorKind::Connect
32 } else if error.is_timeout() {
33 FetchErrorKind::Timeout
34 } else if let Some(status) = error.status() {
35 FetchErrorKind::Status(status.as_u16())
36 } else {
37 FetchErrorKind::Other
38 };
39
40 FetchError {
41 detail: StyledString::Text(error.to_string().into()).resolved_cell(),
42 url: ResolvedVc::cell(url.into()),
43 kind: kind.resolved_cell(),
44 }
45 }
46}
47
48#[turbo_tasks::value_impl]
49impl FetchError {
50 #[turbo_tasks::function]
51 pub fn to_issue(
52 &self,
53 severity: IssueSeverity,
54 issue_context: FileSystemPath,
55 ) -> Vc<FetchIssue> {
56 FetchIssue {
57 issue_context,
58 severity,
59 url: self.url,
60 kind: self.kind,
61 detail: self.detail,
62 }
63 .cell()
64 }
65}
66
67#[turbo_tasks::value(shared)]
68pub struct FetchIssue {
69 pub issue_context: FileSystemPath,
70 pub severity: IssueSeverity,
71 pub url: ResolvedVc<RcStr>,
72 pub kind: ResolvedVc<FetchErrorKind>,
73 pub detail: ResolvedVc<StyledString>,
74}
75
76#[async_trait]
77#[turbo_tasks::value_impl]
78impl Issue for FetchIssue {
79 async fn file_path(&self) -> Result<FileSystemPath> {
80 Ok(self.issue_context.clone())
81 }
82
83 fn severity(&self) -> IssueSeverity {
84 self.severity
85 }
86
87 async fn title(&self) -> Result<StyledString> {
88 Ok(StyledString::Text(rcstr!(
89 "Error while requesting resource"
90 )))
91 }
92
93 fn stage(&self) -> IssueStage {
94 IssueStage::Load
95 }
96
97 async fn description(&self) -> Result<Option<StyledString>> {
98 let url = &*self.url.await?;
99 let kind = &*self.kind.await?;
100
101 Ok(Some(match kind {
102 FetchErrorKind::Connect => StyledString::Line(vec![
103 StyledString::Text(rcstr!(
104 "There was an issue establishing a connection while requesting "
105 )),
106 StyledString::Code(url.clone()),
107 ]),
108 FetchErrorKind::Status(status) => StyledString::Line(vec![
109 StyledString::Text(rcstr!("Received response with status ")),
110 StyledString::Code(RcStr::from(status.to_string())),
111 StyledString::Text(rcstr!(" when requesting ")),
112 StyledString::Code(url.clone()),
113 ]),
114 FetchErrorKind::Timeout => StyledString::Line(vec![
115 StyledString::Text(rcstr!("Connection timed out when requesting ")),
116 StyledString::Code(url.clone()),
117 ]),
118 FetchErrorKind::Other => StyledString::Line(vec![
119 StyledString::Text(rcstr!("There was an issue requesting ")),
120 StyledString::Code(url.clone()),
121 ]),
122 }))
123 }
124
125 async fn detail(&self) -> Result<Option<StyledString>> {
126 Ok(Some((*self.detail.await?).clone()))
127 }
128}