1#![warn(missing_docs)]
11
12use std::str::FromStr;
13
14#[derive(Clone, Debug)]
16pub struct MappingFile {
17 pub mappings: Vec<Mapping>,
19}
20
21#[derive(Clone, Debug)]
23pub struct Mapping {
24 pub user: String,
26 pub keys: Vec<Key>,
28}
29
30#[derive(Clone, Debug)]
32pub struct Key {
33 pub handle: String,
35 pub public: String,
37 pub kind: String,
39 pub flags: Vec<String>,
41}
42
43impl FromStr for MappingFile {
44 type Err = Error;
45
46 fn from_str(s: &str) -> Result<Self, Self::Err> {
47 let mappings = s
48 .lines()
49 .map(Mapping::from_str)
50 .collect::<Result<Vec<Mapping>, Error>>()?;
51 Ok(MappingFile { mappings })
52 }
53}
54
55impl std::str::FromStr for Mapping {
56 type Err = Error;
57
58 fn from_str(s: &str) -> Result<Self, Self::Err> {
59 let mut fields = s.split(':');
60 let user = fields.next().ok_or(Error::UserMissing)?;
61 let mut keys = Vec::new();
62 for field in fields {
63 let mut subfields = field.split(',');
64 let public = subfields.next().unwrap().to_owned();
66 let handle = subfields.next().ok_or(Error::HandleMissing)?.to_owned();
67 let kind = subfields.next().ok_or(Error::KindMissing)?.to_owned();
68 let flags = subfields.next().ok_or(Error::FlagsMissing)?.to_owned();
69 let mut flags = flags.split('+');
70 if flags.next() != Some("") {
71 return Err(Error::BadFlags);
72 }
73 let flags = flags.map(|s| s.to_owned()).collect::<Vec<_>>();
74 keys.push(Key {
75 public,
76 handle,
77 kind,
78 flags,
79 })
80 }
81 Ok(Mapping {
82 user: user.to_owned(),
83 keys,
84 })
85 }
86}
87
88impl std::fmt::Display for Mapping {
89 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
90 write!(f, "{}", self.user)?;
91 for key in &self.keys {
92 write!(f, ":{},{},{},", &key.handle, &key.public, &key.kind)?;
93 for flag in &key.flags {
94 write!(f, "+{flag}")?;
95 }
96 }
97 Ok(())
98 }
99}
100
101#[derive(Debug, Clone, Copy)]
103pub enum Error {
104 UserMissing,
106 HandleMissing,
108 KindMissing,
110 FlagsMissing,
112 BadFlags,
114}
115
116impl std::fmt::Display for Error {
117 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
118 use Error::*;
119 let s = match *self {
120 UserMissing => "Entry has no username",
121 HandleMissing => "Missing second half of key data",
122 KindMissing => "Entry has no key type",
123 FlagsMissing => "Entry has no flags",
124 BadFlags => "Entry has ill-formed flags",
125 };
126 f.write_str(s)
127 }
128}
129
130impl std::error::Error for Error {}
131
132#[cfg(test)]
133mod tests {
134 use super::Mapping;
135 type BoxError = Box<dyn std::error::Error>;
136 const TEST_MAPPING: &str = "alice:\
137 owBYtYMabYlexEG10ildyDLNqwkpeIZyc4YwqP6yUnqlQ3DCxNMjPXoGcQOPiNXu2kFuGKs\
138 LsN6am/UCjgUpwvr9G54GyY85i0zt/vHRsU+OayYoalSjsVjBvyRqFai3fZUdGEHVLdpw9Y\
139 Z3MZeJiSWWEumF59CBdFWNLtq0Xi5M1katPXKIqOUSHLePlq1UfaGkh7R5y+Cv8jXtrhtak\
140 ROcMXjrAfo+5Wq0hNe0JiQwxFPufHUJ8IMBTFw4Qv3TnPGcVFTXZgJQU1FguzVlQ6pU7FS6\
141 37Dhdg==,\
142 IiFyv2O8qSG517c2ghvHEbMb6xs5ToPaoOXdgGkkorH2ta/iYWtOhMB7wxaiS3BhOHSxcJU\
143 JJkMLmfUWl8Uivw==,\
144 es256,+presence";
145 #[test]
146 fn parse() -> Result<(), BoxError> {
147 let mapping: Mapping = TEST_MAPPING.parse()?;
148 assert_eq!(mapping.user, "alice", "user mismatch");
149 let key = &mapping.keys[0];
150 assert_eq!(
151 key.handle,
152 "owBYtYMabYlexEG10ildyDLNqwkpeIZyc4YwqP6yUnqlQ3DCxNMjPXoGcQOPiNXu2k\
153 FuGKsLsN6am/UCjgUpwvr9G54GyY85i0zt/vHRsU+OayYoalSjsVjBvyRqFai3fZUdG\
154 EHVLdpw9YZ3MZeJiSWWEumF59CBdFWNLtq0Xi5M1katPXKIqOUSHLePlq1UfaGkh7R5\
155 y+Cv8jXtrhtakROcMXjrAfo+5Wq0hNe0JiQwxFPufHUJ8IMBTFw4Qv3TnPGcVFTXZgJ\
156 QU1FguzVlQ6pU7FS637Dhdg==",
157 "key handle mismatch"
158 );
159 assert_eq!(
160 key.public,
161 "IiFyv2O8qSG517c2ghvHEbMb6xs5ToPaoOXdgGkkorH2ta/iYWtOhMB7wxaiS3BhOHSxcJUJJkMLmfUWl8Uivw==",
162 "public key mismatch"
163 );
164 assert_eq!(key.kind, "es256");
165 assert_eq!(key.flags, &["presence"]);
166 Ok(())
167 }
168 #[test]
170 fn non_destructive() -> Result<(), BoxError> {
171 assert_eq!(TEST_MAPPING.parse::<Mapping>()?.to_string(), TEST_MAPPING);
172 Ok(())
173 }
174}