1use crate::error::Error;
2use curl::easy::Easy;
3use exitfailure::ExitFailure;
4use std::fs::File;
5use std::io::Write;
6use std::path::PathBuf;
7
8#[derive(Clone, Debug, Eq, PartialEq)]
9pub struct Engine {
10 version: String,
11 target: String,
12 build: Build,
13}
14
15#[derive(Clone, Copy, Debug, Eq, PartialEq)]
16pub enum Build {
17 Debug,
18 Release,
19 Profile,
20}
21
22impl Build {
23 pub fn build(&self) -> &str {
24 match self {
25 Self::Debug => "debug_unopt",
26 Self::Release => "release",
27 Self::Profile => "profile",
28 }
29 }
30}
31
32impl Engine {
33 pub fn new(version: String, target: String, build: Build) -> Engine {
34 Engine {
35 version,
36 target,
37 build,
38 }
39 }
40
41 pub fn download_url(&self) -> String {
42 let build = self.build.build();
43 let platform = match self.target.as_str() {
44 "x86_64-unknown-linux-gnu" => format!("linux_x64-host_{}", build),
45 "armv7-linux-androideabi" => format!("linux_x64-android_{}", build),
46 "aarch64-linux-android" => format!("linux_x64-android_{}_arm64", build),
47 "i686-linux-android" => format!("linux_x64-android_{}_x64", build),
48 "x86_64-linux-android" => format!("linux_x64-android_{}_x86", build),
49 "x86_64-apple-darwin" => format!("macosx_x64-host_{}", build),
50 "armv7-apple-ios" => format!("macosx_x64-ios_{}_arm", build),
51 "aarch64-apple-ios" => format!("macosx_x64-ios_{}", build),
52 "x86_64-pc-windows-msvc" => format!("windows_x64-host_{}", build),
53 _ => panic!("unsupported platform"),
54 };
55 format!(
56 "https://github.com/flutter-rs/engine-builds/releases/download/f-{0}/{1}.zip",
57 &self.version, platform
58 )
59 }
60
61 pub fn library_name(&self) -> &'static str {
62 match self.target.as_str() {
63 "x86_64-unknown-linux-gnu" => "libflutter_engine.so",
64 "armv7-linux-androideabi" => "libflutter_engine.so",
65 "aarch64-linux-android" => "libflutter_engine.so",
66 "i686-linux-android" => "libflutter_engine.so",
67 "x86_64-linux-android" => "libflutter_engine.so",
68 "x86_64-apple-darwin" => "libflutter_engine.dylib",
69 "armv7-apple-ios" => "libflutter_engine.dylib",
70 "aarch64-apple-ios" => "libflutter_engine.dylib",
71 "x86_64-pc-windows-msvc" => "flutter_engine.dll",
72 _ => panic!("unsupported platform"),
73 }
74 }
75
76 pub fn engine_dir(&self) -> PathBuf {
77 dirs::cache_dir()
78 .expect("Cannot get cache dir")
79 .join("flutter-engine")
80 .join(&self.version)
81 .join(&self.target)
82 .join(self.build.build())
83 }
84
85 pub fn engine_path(&self) -> PathBuf {
86 self.engine_dir().join(self.library_name())
87 }
88
89 pub fn download(&self, quiet: bool) -> Result<(), ExitFailure> {
90 let url = self.download_url();
91 let path = self.engine_path();
92 let dir = path.parent().unwrap().to_owned();
93
94 if path.exists() {
95 return Ok(());
96 }
97
98 std::fs::create_dir_all(&dir)?;
99
100 println!("Starting download from {}", url);
101 let download_file = dir.join("engine.zip");
102 let mut file = File::create(&download_file)?;
103 let mut last_done = 0.0;
104
105 let mut easy = Easy::new();
106 easy.fail_on_error(true)?;
107 easy.url(&url)?;
108 easy.follow_location(true)?;
109 easy.progress(true)?;
110 easy.progress_function(move |total, done, _, _| {
111 if done > last_done {
112 last_done = done;
113 if !quiet {
114 println!("Downloading flutter engine {} of {}", done, total);
115 }
116 }
117 true
118 })?;
119 easy.write_function(move |data| Ok(file.write(data).unwrap()))?;
120
121 easy.perform()
122 .or_else(|_| Err(Error::EngineNotFound(self.version.clone())))?;
123 println!("Download finished");
124
125 println!("Extracting...");
126 crate::unzip::unzip(&download_file, &dir)?;
127
128 Ok(())
129 }
130
131 pub fn dart(&self) -> Result<PathBuf, Error> {
132 let host_engine_dir = self.engine_dir();
133 ["dart", "dart.exe"]
134 .iter()
135 .map(|bin| host_engine_dir.join(bin))
136 .find(|path| path.exists())
137 .ok_or(Error::DartNotFound)
138 }
139}