Skip to main content

turbo_tasks_fetch/
error.rs

1use 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}