turbo_tasks_fetch/
error.rs

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