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