fuchsia_pkg/
meta_package.rs
1use crate::errors::MetaPackageError;
6use crate::path::PackagePath;
7use fuchsia_url::{PackageName, PackageVariant};
8use serde::{Deserialize, Serialize};
9use std::convert::TryInto as _;
10use std::io;
11
12#[derive(Debug, Eq, PartialEq, Clone)]
16pub struct MetaPackage {
17 name: PackageName,
18 variant: PackageVariant,
19}
20
21impl MetaPackage {
22 pub const PATH: &'static str = "meta/package";
23
24 pub fn from_name_and_variant_zero(name: PackageName) -> Self {
26 Self { name, variant: PackageVariant::zero() }
27 }
28
29 pub fn name(&self) -> &PackageName {
31 &self.name
32 }
33
34 pub fn variant(&self) -> &PackageVariant {
36 &self.variant
37 }
38
39 pub fn into_path(self) -> PackagePath {
41 PackagePath::from_name_and_variant(self.name, self.variant)
42 }
43
44 pub fn deserialize(reader: impl io::BufRead) -> Result<Self, MetaPackageError> {
61 MetaPackage::from_v0(serde_json::from_reader(reader)?)
62 }
63
64 pub fn serialize(&self, writer: impl io::Write) -> Result<(), MetaPackageError> {
76 serde_json::to_writer(
77 writer,
78 &MetaPackageV0Serialize { name: self.name.as_ref(), variant: self.variant.as_ref() },
79 )?;
80 Ok(())
81 }
82
83 fn from_v0(v0: MetaPackageV0Deserialize) -> Result<MetaPackage, MetaPackageError> {
84 Ok(MetaPackage {
85 name: v0.name.try_into().map_err(MetaPackageError::PackageName)?,
86 variant: v0.variant.try_into().map_err(MetaPackageError::PackageVariant)?,
87 })
88 }
89}
90
91#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
92struct MetaPackageV0Deserialize {
93 name: String,
94 #[serde(rename = "version")]
95 variant: String,
96}
97
98#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
99struct MetaPackageV0Serialize<'a> {
100 name: &'a str,
101 #[serde(rename = "version")]
102 variant: &'a str,
103}
104
105#[cfg(test)]
106mod tests {
107 use super::*;
108 use crate::test::*;
109 use lazy_static::lazy_static;
110 use proptest::prelude::*;
111 use regex::Regex;
112
113 #[test]
114 fn test_accessors() {
115 let meta_package = MetaPackage::from_name_and_variant_zero("foo".parse().unwrap());
116 assert_eq!(meta_package.name(), &"foo".parse::<PackageName>().unwrap());
117 assert_eq!(meta_package.variant(), &"0".parse::<PackageVariant>().unwrap());
118 }
119
120 #[test]
121 fn test_serialize() {
122 let meta_package = MetaPackage::from_name_and_variant_zero("package-name".parse().unwrap());
123 let mut v: Vec<u8> = Vec::new();
124
125 meta_package.serialize(&mut v).unwrap();
126
127 let expected = r#"{"name":"package-name","version":"0"}"#;
128 assert_eq!(v.as_slice(), expected.as_bytes());
129 }
130
131 #[test]
132 fn test_deserialize() {
133 let json_bytes = r#"{"name":"package-name","version":"0"}"#.as_bytes();
134 assert_eq!(
135 MetaPackage::deserialize(json_bytes).unwrap(),
136 MetaPackage { name: "package-name".parse().unwrap(), variant: "0".parse().unwrap() }
137 );
138 }
139
140 proptest! {
141 #![proptest_config(ProptestConfig{
142 failure_persistence: None,
143 ..Default::default()
144 })]
145
146 #[test]
147 fn test_serialize_deserialize_is_identity(
148 meta_package in random_meta_package(),
149 ) {
150 let mut v: Vec<u8> = Vec::new();
151 meta_package.serialize(&mut v).unwrap();
152 let meta_package_round_trip = MetaPackage::deserialize(v.as_slice()).unwrap();
153 assert_eq!(meta_package, meta_package_round_trip);
154 }
155
156 #[test]
157 fn test_serialized_contains_no_whitespace(
158 meta_package in random_meta_package(),
159 ) {
160 lazy_static! {
161 static ref RE: Regex = Regex::new(r"(\p{White_Space})").unwrap();
162 }
163 let mut v: Vec<u8> = Vec::new();
164 meta_package.serialize(&mut v).unwrap();
165 assert!(!RE.is_match(std::str::from_utf8(v.as_slice()).unwrap()));
166 }
167 }
168}