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