alpm_package/
scriptlet.rs1use std::{fs::File, io::Read, path::Path};
6
7use fluent_i18n::t;
8
9use crate::Error;
10
11const REQUIRED_FUNCTION_SIGNATURES: &[&str] = &[
15 "pre_install",
16 "post_install",
17 "pre_upgrade",
18 "post_upgrade",
19 "pre_remove",
20 "post_remove",
21];
22
23pub fn check_scriptlet(path: impl AsRef<Path>) -> Result<(), Error> {
41 let path = path.as_ref();
42 let mut file = File::open(path).map_err(|source| Error::IoPath {
43 path: path.to_path_buf(),
44 context: t!("error-io-open-scriptlet"),
45 source,
46 })?;
47 let mut buf = String::new();
48 file.read_to_string(&mut buf)
49 .map_err(|source| Error::IoPath {
50 path: path.to_path_buf(),
51 context: t!("error-io-read-to-string"),
52 source,
53 })?;
54
55 for line in buf.lines() {
56 for function_name in REQUIRED_FUNCTION_SIGNATURES {
57 if line.starts_with(&format!("{function_name}()"))
58 || line.starts_with(&format!("{function_name}() {{"))
59 || line.starts_with(&format!("function {function_name}()"))
60 || line.starts_with(&format!("function {function_name}() {{"))
61 {
62 return Ok(());
63 }
64 }
65 }
66
67 Err(Error::InstallScriptlet {
68 path: path.to_path_buf(),
69 context: format!(
70 "it must implement at least one of the functions: {}",
71 REQUIRED_FUNCTION_SIGNATURES.join(", ")
72 ),
73 })
74}
75
76#[cfg(test)]
77mod test {
78 use std::io::Write;
79
80 use rstest::rstest;
81 use tempfile::NamedTempFile;
82 use testresult::TestResult;
83
84 use super::*;
85
86 const INSTALL_SCRIPTLET_FULL: &str = r#"pre_install() {
87 true
88}
89
90post_install() {
91 true
92}
93
94pre_upgrade() {
95 true
96}
97
98post_upgrade() {
99 true
100}
101
102pre_remove() {
103 true
104}
105
106post_remove() {
107 true
108}"#;
109 const INSTALL_SCRIPTLET_FULL_FUNCTION_PREFIX: &str = r#"function pre_install() {
110 true
111}
112
113function post_install() {
114 true
115}
116
117function pre_upgrade() {
118 true
119}
120
121function post_upgrade() {
122 true
123}
124
125function pre_remove() {
126 true
127}
128
129function post_remove() {
130 true
131}"#;
132 const INSTALL_SCRIPTLET_EMPTY: &str = "";
133 const INSTALL_SCRIPTLET_INVALID: &str = r#"pre_install
134post_install
135pre_upgrade
136post_upgrade
137pre_remove
138post_remove"#;
139 const INSTALL_SCRIPTLET_INVALID_FUNCTION_PREFIX: &str = r#"function pre_install
140function post_install
141function pre_upgrade
142function post_upgrade
143function pre_remove
144function post_remove"#;
145
146 #[rstest]
147 #[case(INSTALL_SCRIPTLET_FULL)]
148 #[case(INSTALL_SCRIPTLET_FULL_FUNCTION_PREFIX)]
149 fn valid_scriptlet(#[case] scriptlet: &str) -> TestResult {
150 let mut file = NamedTempFile::new()?;
151 write!(file, "{scriptlet}")?;
152
153 check_scriptlet(file.path())?;
154
155 Ok(())
156 }
157
158 #[rstest]
159 #[case(INSTALL_SCRIPTLET_EMPTY)]
160 #[case(INSTALL_SCRIPTLET_INVALID)]
161 #[case(INSTALL_SCRIPTLET_INVALID_FUNCTION_PREFIX)]
162 fn invalid_scriptlet(#[case] scriptlet: &str) -> TestResult {
163 let mut file = NamedTempFile::new()?;
164 write!(file, "{scriptlet}")?;
165
166 assert!(check_scriptlet(file.path()).is_err());
167
168 Ok(())
169 }
170}