playwright_core/protocol/download.rs
1// Copyright 2024 Paul Adamson
2// Licensed under the Apache License, Version 2.0
3//
4// Download protocol object
5//
6// Represents a file download triggered by the page.
7// Downloads are dispatched via page.on('download') events.
8
9use crate::channel_owner::ChannelOwner;
10use crate::error::Result;
11use serde_json::json;
12use std::path::PathBuf;
13use std::sync::Arc;
14
15/// Download represents a file download triggered by the page.
16///
17/// Downloads are dispatched via the page.on('download') event.
18/// The download will be automatically deleted when the browser context closes.
19///
20/// NOTE: Unlike other protocol objects, Download is a wrapper around the Artifact
21/// protocol object. The URL and suggested_filename come from the download event params,
22/// while the actual file operations are delegated to the underlying Artifact.
23///
24/// See: <https://playwright.dev/docs/api/class-download>
25#[derive(Clone)]
26pub struct Download {
27 /// Reference to the underlying Artifact protocol object
28 artifact: Arc<dyn ChannelOwner>,
29 /// URL from download event params
30 url: String,
31 /// Suggested filename from download event params
32 suggested_filename: String,
33}
34
35impl Download {
36 /// Creates a new Download from an Artifact and event params
37 ///
38 /// This is NOT created via the object factory, but rather constructed
39 /// directly from the download event params which contain {url, suggestedFilename, artifact}.
40 ///
41 /// # Arguments
42 ///
43 /// * `artifact` - The Artifact protocol object (from event params)
44 /// * `url` - Download URL (from event params)
45 /// * `suggested_filename` - Suggested filename (from event params)
46 pub fn from_artifact(
47 artifact: Arc<dyn ChannelOwner>,
48 url: String,
49 suggested_filename: String,
50 ) -> Self {
51 Self {
52 artifact,
53 url,
54 suggested_filename,
55 }
56 }
57
58 /// Returns the download URL.
59 ///
60 /// See: <https://playwright.dev/docs/api/class-download#download-url>
61 pub fn url(&self) -> &str {
62 &self.url
63 }
64
65 /// Returns the suggested filename for the download.
66 ///
67 /// This is typically the server-suggested filename from the Content-Disposition
68 /// header or the HTML download attribute.
69 ///
70 /// See: <https://playwright.dev/docs/api/class-download#download-suggested-filename>
71 pub fn suggested_filename(&self) -> &str {
72 &self.suggested_filename
73 }
74
75 /// Returns the underlying Artifact's channel for protocol communication
76 fn channel(&self) -> &crate::channel::Channel {
77 self.artifact.channel()
78 }
79
80 /// Returns the path to the downloaded file after it completes.
81 ///
82 /// This method waits for the download to finish if necessary.
83 /// Returns an error if the download fails or is canceled.
84 ///
85 /// See: <https://playwright.dev/docs/api/class-download#download-path>
86 pub async fn path(&self) -> Result<Option<PathBuf>> {
87 #[derive(serde::Deserialize)]
88 struct PathResponse {
89 value: Option<String>,
90 }
91
92 let result: PathResponse = self.channel().send("path", json!({})).await?;
93
94 Ok(result.value.map(PathBuf::from))
95 }
96
97 /// Saves the download to the specified path.
98 ///
99 /// This method can be safely called while the download is still in progress.
100 /// The file will be copied to the specified location after the download completes.
101 ///
102 /// # Example
103 ///
104 /// ```ignore
105 /// # use playwright_core::protocol::Download;
106 /// # async fn example(download: Download) -> Result<(), Box<dyn std::error::Error>> {
107 /// download.save_as("/path/to/save/file.pdf").await?;
108 /// # Ok(())
109 /// # }
110 /// ```
111 ///
112 /// See: <https://playwright.dev/docs/api/class-download#download-save-as>
113 pub async fn save_as(&self, path: impl AsRef<std::path::Path>) -> Result<()> {
114 let path_str = path
115 .as_ref()
116 .to_str()
117 .ok_or_else(|| crate::error::Error::InvalidArgument("Invalid path".to_string()))?;
118
119 self.channel()
120 .send_no_result("saveAs", json!({ "path": path_str }))
121 .await?;
122
123 Ok(())
124 }
125
126 /// Cancels the download.
127 ///
128 /// After calling this method, `failure()` will return an error message.
129 ///
130 /// See: <https://playwright.dev/docs/api/class-download#download-cancel>
131 pub async fn cancel(&self) -> Result<()> {
132 self.channel().send_no_result("cancel", json!({})).await?;
133
134 Ok(())
135 }
136
137 /// Deletes the downloaded file.
138 ///
139 /// The download must be finished before calling this method.
140 ///
141 /// See: <https://playwright.dev/docs/api/class-download#download-delete>
142 pub async fn delete(&self) -> Result<()> {
143 self.channel().send_no_result("delete", json!({})).await?;
144
145 Ok(())
146 }
147
148 /// Returns the download error message if it failed, otherwise None.
149 ///
150 /// See: <https://playwright.dev/docs/api/class-download#download-failure>
151 pub async fn failure(&self) -> Result<Option<String>> {
152 #[derive(serde::Deserialize)]
153 struct FailureResponse {
154 error: Option<String>,
155 }
156
157 let result: FailureResponse = self.channel().send("failure", json!({})).await?;
158
159 Ok(result.error)
160 }
161}
162
163impl std::fmt::Debug for Download {
164 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
165 f.debug_struct("Download")
166 .field("url", &self.url())
167 .field("suggested_filename", &self.suggested_filename())
168 .finish()
169 }
170}