1 // Copyright (C) 2023 The Android Open Source Project 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 //! Data structures for representing a crate name and version, which can be 16 //! used as a map key. 17 18 use std::{ 19 borrow::Borrow, 20 cmp::Ordering, 21 hash::{Hash, Hasher}, 22 }; 23 24 use semver::{BuildMetadata, Prerelease, Version}; 25 26 static MIN_VERSION: Version = 27 Version { major: 0, minor: 0, patch: 0, pre: Prerelease::EMPTY, build: BuildMetadata::EMPTY }; 28 29 /// A name and version pair trait. 30 pub trait NamedAndVersioned { 31 /// Returns the name. name(&self) -> &str32 fn name(&self) -> &str; 33 /// Returns the version. version(&self) -> &Version34 fn version(&self) -> &Version; 35 /// Returns a reference that can be used as a map key. key(&self) -> NameAndVersionRef36 fn key(&self) -> NameAndVersionRef; 37 } 38 39 /// An owned namd and version. 40 #[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Hash, Clone)] 41 pub struct NameAndVersion { 42 name: String, 43 version: Version, 44 } 45 46 /// A reference to a name and version. 47 #[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)] 48 pub struct NameAndVersionRef<'a> { 49 name: &'a str, 50 version: &'a Version, 51 } 52 53 impl NameAndVersion { 54 /// Constructor that takes ownership of args. new(name: String, version: Version) -> Self55 pub fn new(name: String, version: Version) -> Self { 56 NameAndVersion { name, version } 57 } 58 /// Constructor that clones a reference. from(nv: &impl NamedAndVersioned) -> Self59 pub fn from(nv: &impl NamedAndVersioned) -> Self { 60 NameAndVersion { name: nv.name().to_string(), version: nv.version().clone() } 61 } 62 /// The lowest possible version, used to find the first key in a map with this name. min_version(name: String) -> Self63 pub fn min_version(name: String) -> Self { 64 NameAndVersion { name, version: MIN_VERSION.clone() } 65 } 66 /// Intended for testing. try_from_str(name: &str, version: &str) -> Result<Self, semver::Error>67 pub fn try_from_str(name: &str, version: &str) -> Result<Self, semver::Error> { 68 Ok(NameAndVersion::new(name.to_string(), Version::parse(version)?)) 69 } 70 } 71 72 impl NamedAndVersioned for NameAndVersion { name(&self) -> &str73 fn name(&self) -> &str { 74 self.name.as_str() 75 } 76 version(&self) -> &Version77 fn version(&self) -> &Version { 78 &self.version 79 } key(&self) -> NameAndVersionRef80 fn key(&self) -> NameAndVersionRef { 81 NameAndVersionRef::new(self.name(), self.version()) 82 } 83 } 84 85 impl<'a> NameAndVersionRef<'a> { 86 /// Construct a reference to a name and version. new(name: &'a str, version: &'a Version) -> Self87 pub fn new(name: &'a str, version: &'a Version) -> Self { 88 NameAndVersionRef { name, version } 89 } 90 } 91 92 impl NamedAndVersioned for NameAndVersionRef<'_> { name(&self) -> &str93 fn name(&self) -> &str { 94 self.name 95 } version(&self) -> &Version96 fn version(&self) -> &Version { 97 self.version 98 } key(&self) -> NameAndVersionRef99 fn key(&self) -> NameAndVersionRef { 100 *self 101 } 102 } 103 104 impl<'a> Borrow<dyn NamedAndVersioned + 'a> for NameAndVersion { borrow(&self) -> &(dyn NamedAndVersioned + 'a)105 fn borrow(&self) -> &(dyn NamedAndVersioned + 'a) { 106 self 107 } 108 } 109 110 impl PartialEq for (dyn NamedAndVersioned + '_) { eq(&self, other: &Self) -> bool111 fn eq(&self, other: &Self) -> bool { 112 self.key().eq(&other.key()) 113 } 114 } 115 116 impl Eq for (dyn NamedAndVersioned + '_) {} 117 118 impl PartialOrd for (dyn NamedAndVersioned + '_) { partial_cmp(&self, other: &Self) -> Option<Ordering>119 fn partial_cmp(&self, other: &Self) -> Option<Ordering> { 120 Some(self.cmp(other)) 121 } 122 } 123 124 impl Ord for (dyn NamedAndVersioned + '_) { cmp(&self, other: &Self) -> Ordering125 fn cmp(&self, other: &Self) -> Ordering { 126 self.key().cmp(&other.key()) 127 } 128 } 129 130 impl Hash for (dyn NamedAndVersioned + '_) { hash<H: Hasher>(&self, state: &mut H)131 fn hash<H: Hasher>(&self, state: &mut H) { 132 self.key().hash(state) 133 } 134 } 135 136 #[cfg(test)] 137 mod tests { 138 use super::*; 139 140 #[test] test_name_version_ref() -> Result<(), semver::Error>141 fn test_name_version_ref() -> Result<(), semver::Error> { 142 let version = Version::parse("2.3.4")?; 143 let nvp = NameAndVersionRef::new("foo", &version); 144 assert_eq!(nvp.name(), "foo"); 145 assert_eq!(nvp.version().to_string(), "2.3.4"); 146 Ok(()) 147 } 148 149 #[test] test_name_and_version() -> Result<(), semver::Error>150 fn test_name_and_version() -> Result<(), semver::Error> { 151 let version = Version::parse("2.3.4")?; 152 let nvp = NameAndVersion::new("foo".to_string(), version); 153 assert_eq!(nvp.name(), "foo"); 154 assert_eq!(nvp.version().to_string(), "2.3.4"); 155 Ok(()) 156 } 157 } 158