fidl_fuchsia_component_abi_ext/
lib.rs1use fuchsia_fs::file::ReadError;
5use fuchsia_fs::node::OpenError;
6use thiserror::Error;
7use version_history::AbiRevision;
8use zx_status::Status;
9use {fidl_fuchsia_component_resolution as fresolution, fidl_fuchsia_io as fio};
10
11#[derive(Error, Debug)]
12pub enum AbiRevisionFileError {
13 #[error("Failed to decode ABI revision value")]
14 Decode,
15 #[error("Failed to open ABI revision file: {0}")]
16 Open(#[from] OpenError),
17 #[error("Failed to read ABI revision file: {0}")]
18 Read(#[from] ReadError),
19}
20
21impl From<AbiRevisionFileError> for fresolution::ResolverError {
22 fn from(err: AbiRevisionFileError) -> fresolution::ResolverError {
23 match err {
24 AbiRevisionFileError::Open(_) => fresolution::ResolverError::AbiRevisionNotFound,
25 AbiRevisionFileError::Read(_) | AbiRevisionFileError::Decode => {
26 fresolution::ResolverError::InvalidAbiRevision
27 }
28 }
29 }
30}
31
32pub async fn read_abi_revision_optional(
34 dir: &fio::DirectoryProxy,
35 path: &str,
36) -> Result<Option<AbiRevision>, AbiRevisionFileError> {
37 match read_abi_revision(dir, path).await {
38 Ok(abi) => Ok(Some(abi)),
39 Err(AbiRevisionFileError::Open(OpenError::OpenError(Status::NOT_FOUND))) => Ok(None),
40 Err(e) => Err(e),
41 }
42}
43
44async fn read_abi_revision(
47 dir: &fio::DirectoryProxy,
48 path: &str,
49) -> Result<AbiRevision, AbiRevisionFileError> {
50 let file = fuchsia_fs::directory::open_file(&dir, path, fio::PERM_READABLE).await?;
51 let bytes: [u8; 8] = fuchsia_fs::file::read(&file)
52 .await?
53 .try_into()
54 .map_err(|_| AbiRevisionFileError::Decode)?;
55 Ok(AbiRevision::from_bytes(bytes))
56}
57
58#[cfg(test)]
59mod tests {
60 use super::*;
61 use fuchsia_fs::directory::open_in_namespace;
62 use vfs::file::vmo::read_only;
63 use vfs::pseudo_directory;
64
65 fn init_fuchsia_abi_dir(filename: &'static str, content: &'static [u8]) -> fio::DirectoryProxy {
66 let dir = pseudo_directory! {
67 "meta" => pseudo_directory! {
68 "fuchsia.abi" => pseudo_directory! {
69 filename => read_only(content),
70 }
71 }
72 };
73 vfs::directory::serve_read_only(dir)
74 }
75
76 const ABI_REV_MAX: &'static [u8] = &u64::MAX.to_le_bytes();
77 const ABI_REV_ZERO: &'static [u8] = &0u64.to_le_bytes();
78
79 #[fuchsia::test]
80 async fn test_read_abi_revision_impl() -> Result<(), AbiRevisionFileError> {
81 let dir = init_fuchsia_abi_dir("abi-revision", b"Invalid ABI revision string");
83 let res = read_abi_revision_optional(&dir, AbiRevision::PATH).await;
84 assert!(matches!(res.unwrap_err(), AbiRevisionFileError::Decode));
85
86 let dir = init_fuchsia_abi_dir("abi-revision", b"");
87 let res = read_abi_revision_optional(&dir, AbiRevision::PATH).await;
88 assert!(matches!(res.unwrap_err(), AbiRevisionFileError::Decode));
89
90 let dir = init_fuchsia_abi_dir("abi-revision", ABI_REV_MAX);
92 let res = read_abi_revision_optional(&dir, AbiRevision::PATH).await.unwrap();
93 assert_eq!(res, Some(u64::MAX.into()));
94
95 let dir = init_fuchsia_abi_dir("abi-revision", ABI_REV_ZERO);
96 let res = read_abi_revision_optional(&dir, AbiRevision::PATH).await.unwrap();
97 assert_eq!(res, Some(0u64.into()));
98
99 Ok(())
100 }
101
102 #[fuchsia::test]
103 async fn test_read_abi_revision_optional_allows_absent_file() -> Result<(), AbiRevisionFileError>
104 {
105 let dir = init_fuchsia_abi_dir("abi-revision-staging", ABI_REV_MAX);
107 let res = read_abi_revision_optional(&dir, AbiRevision::PATH).await.unwrap();
108 assert_eq!(res, None);
109
110 Ok(())
111 }
112
113 #[fuchsia::test]
114 async fn test_read_abi_revision_fails_absent_file() -> Result<(), AbiRevisionFileError> {
115 let dir = init_fuchsia_abi_dir("a-different-file", ABI_REV_MAX);
116 let err = read_abi_revision(&dir, AbiRevision::PATH).await.unwrap_err();
117 assert!(matches!(err, AbiRevisionFileError::Open(OpenError::OpenError(Status::NOT_FOUND))));
118 Ok(())
119 }
120
121 #[fuchsia::test]
123 async fn read_test_pkg_abi_revision() -> Result<(), AbiRevisionFileError> {
124 let dir_proxy = open_in_namespace("/pkg", fio::PERM_READABLE).unwrap();
125 let abi_revision = read_abi_revision(&dir_proxy, AbiRevision::PATH)
126 .await
127 .expect("test package doesn't contain an ABI revision");
128 version_history_data::HISTORY
129 .check_abi_revision_for_runtime(abi_revision)
130 .expect("test package ABI revision should be valid");
131 Ok(())
132 }
133}