1use std::fs::File;
4pub use install_location::{normalize_name, InstallLocation, LockedDir};
6use platform_info::PlatformInfoError;
7use std::io;
8use std::path::Path;
9use std::str::FromStr;
10use thiserror::Error;
11pub use wheel::{
12 get_script_launcher, install_wheel, parse_key_value_file, read_record_file, relative_to,
13 Script, MONOTRAIL_SCRIPT_SHEBANG,
14};
15pub use wheel_tags::{Arch, CompatibleTags, Os, WheelFilename};
16use zip::result::ZipError;
17
18mod install_location;
19#[cfg(feature = "python_bindings")]
20mod python_bindings;
21mod wheel;
22mod wheel_tags;
23
24#[derive(Error, Debug)]
25pub enum Error {
26 #[error(transparent)]
27 IO(#[from] io::Error),
28 #[error("Failed to serialize direct_url.json ಠ_ಠ")]
30 DirectUrlSerdeJson(#[source] serde_json::Error),
31 #[error("The wheel is incompatible with the current platform {os} {arch}")]
33 IncompatibleWheel { os: Os, arch: Arch },
34 #[error("The wheel is invalid: {0}")]
36 InvalidWheel(String),
37 #[error("The poetry dependency specification (pyproject.toml or poetry.lock) is broken (try `poetry update`?): {0}")]
39 InvalidPoetry(String),
40 #[error("The wheel filename \"{0}\" is invalid: {1}")]
42 InvalidWheelFileName(String, String),
43 #[error("Failed to read the wheel file {0}")]
44 Zip(String, #[source] ZipError),
45 #[error("Failed to run python subcommand")]
46 PythonSubcommand(#[source] io::Error),
47 #[error("Failed to move data files")]
48 WalkDir(#[from] walkdir::Error),
49 #[error("RECORD file doesn't match wheel contents: {0}")]
50 RecordFile(String),
51 #[error("RECORD file is invalid")]
52 RecordCsv(#[from] csv::Error),
53 #[error("Broken virtualenv: {0}")]
54 BrokenVenv(String),
55 #[error("Failed to detect the operating system version: {0}")]
56 OsVersionDetection(String),
57 #[error("Failed to detect the current platform")]
58 PlatformInfo(#[source] PlatformInfoError),
59 #[error("Invalid version specification, only none or == is supported")]
60 Pep440,
61}
62
63pub fn install_wheel_in_venv(
67 wheel: &Path,
68 venv: &Path,
69 interpreter: &Path,
70 major: u8,
71 minor: u8,
72) -> Result<String, Error> {
73 let venv_base = venv.canonicalize()?;
74 let location = InstallLocation::Venv {
75 venv_base,
76 python_version: (major, minor),
77 };
78 let locked_dir = location.acquire_lock()?;
79
80 let filename = wheel
81 .file_name()
82 .ok_or_else(|| Error::InvalidWheel("Expected a file".to_string()))?
83 .to_string_lossy();
84 let filename = WheelFilename::from_str(&filename)?;
85 let compatible_tags = CompatibleTags::current(location.get_python_version())?;
86 filename.compatibility(&compatible_tags)?;
87
88 install_wheel(
89 &locked_dir,
90 File::open(wheel)?,
91 filename,
92 false,
93 &[],
94 "",
96 interpreter,
97 )
98}