1#![warn(missing_docs)]
30
31pub mod error;
32pub mod extra_data;
33pub mod header;
34pub mod link_info;
35pub mod link_target_id_list;
36pub mod string_data;
37
38pub use extra_data::*;
39pub use header::*;
40pub use link_info::*;
41pub use link_target_id_list::*;
42use std::convert::TryFrom;
43use std::path::PathBuf;
44pub use string_data::*;
45
46pub type Result<T> = std::result::Result<T, error::Error>;
48
49#[derive(Clone, Debug)]
51pub struct Lnk {
52 path: Option<PathBuf>,
54
55 pub header: ShellLinkHeader,
57
58 pub string_data: StringData,
60
61 pub link_target_id_list: LinkTargetIdList,
63
64 pub link_info: LinkInfo,
66
67 pub extra_data: ExtraData,
69}
70
71impl Lnk {
72 pub fn new<S: std::io::Read>(reader: &mut S) -> Result<Lnk> {
85 let mut data_buf = Vec::new();
86 reader
87 .read_to_end(&mut data_buf)
88 .map_err(error::HeaderError::Read)?;
89
90 let mut cursor = std::io::Cursor::new(data_buf);
91
92 let header = ShellLinkHeader::try_from(&mut cursor)?;
93 let link_target_id_list = LinkTargetIdList::new(&mut cursor, &header)?;
94 let link_info = LinkInfo::new(&mut cursor, &header)?;
95 let string_data = StringData::new(&mut cursor, &header)?;
96 let extra_data = ExtraData::new(&mut cursor, &header)?;
97
98 Ok(Lnk {
99 path: None,
100 header,
101 string_data,
102 link_target_id_list,
103 link_info,
104 extra_data,
105 })
106 }
107
108 pub fn arguments(&self) -> Option<String> {
110 self.string_data.command_line_arguments.clone()
111 }
112
113 pub fn relative_path(&self) -> Option<PathBuf> {
115 self.string_data.relative_path.clone()
116 }
117
118 pub fn working_dir(&self) -> Option<PathBuf> {
120 self.string_data.working_dir.clone()
121 }
122
123 pub fn description(&self) -> Option<String> {
125 self.string_data.name_string.clone()
126 }
127
128 pub fn creation_time(&self) -> u64 {
130 self.header.creation_time
131 }
132
133 pub fn access_time(&self) -> u64 {
135 self.header.access_time
136 }
137
138 pub fn write_time(&self) -> u64 {
140 self.header.write_time
141 }
142
143 #[cfg(feature = "chrono")]
145 pub fn created_on(&self) -> Option<chrono::DateTime<chrono::Utc>> {
146 self.header.created_on
147 }
148
149 #[cfg(feature = "chrono")]
151 pub fn accessed_on(&self) -> Option<chrono::DateTime<chrono::Utc>> {
152 self.header.accessed_on
153 }
154
155 #[cfg(feature = "chrono")]
157 pub fn modified_on(&self) -> Option<chrono::DateTime<chrono::Utc>> {
158 self.header.modified_on
159 }
160}
161
162impl TryFrom<&std::path::Path> for Lnk {
163 type Error = crate::error::Error;
164
165 fn try_from(p: &std::path::Path) -> std::result::Result<Self, Self::Error> {
166 let mut f = std::fs::File::open(p).map_err(crate::error::Error::from)?;
167 Lnk::new(&mut f).map(|mut lnk| {
168 lnk.path = Some(p.to_path_buf());
169 lnk
170 })
171 }
172}
173
174impl TryFrom<&[u8]> for Lnk {
175 type Error = crate::error::Error;
176
177 fn try_from(mut p: &[u8]) -> std::result::Result<Self, Self::Error> {
178 Lnk::new(&mut p)
179 }
180}
181
182impl TryFrom<Vec<u8>> for Lnk {
183 type Error = crate::error::Error;
184
185 fn try_from(p: Vec<u8>) -> std::result::Result<Self, Self::Error> {
186 Lnk::new(&mut &p[0..])
187 }
188}
189
190impl TryFrom<&Vec<u8>> for Lnk {
191 type Error = crate::error::Error;
192
193 fn try_from(p: &Vec<u8>) -> std::result::Result<Self, Self::Error> {
194 Lnk::new(&mut &p[0..])
195 }
196}
197
198#[cfg(test)]
199mod tests {
200 use crate::Lnk;
201 use std::convert::TryFrom;
202 use std::path::Path;
203
204 #[test]
205 fn firefox() {
206 let path = Path::new("./test_data/firefox.lnk");
207 assert!(Lnk::try_from(path).is_ok());
208 }
209
210 #[test]
211 fn commander() {
212 let path = Path::new("./test_data/commander.lnk");
213 assert!(Lnk::try_from(path).is_ok());
214 }
215
216 #[test]
217 fn notepad() {
218 let path = Path::new("./test_data/notepad.lnk");
219 assert!(Lnk::try_from(path).is_ok());
220 }
221
222 #[test]
223 fn xp_outlook_express() {
224 let path = Path::new("./test_data/outlook_express.lnk");
225 assert!(Lnk::try_from(path).is_ok());
226 }
227}