Skip to content

Commit fd136bb

Browse files
committed
uefi: Modifying Vars to return environment variable values in addition to names
1 parent 3e9bff8 commit fd136bb

File tree

2 files changed

+73
-19
lines changed

2 files changed

+73
-19
lines changed

uefi-test-runner/src/proto/shell.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ use uefi::{boot, cstr16};
88
pub fn test_env(shell: &ScopedProtocol<Shell>) {
99
/* Test retrieving list of environment variable names */
1010
let mut cur_env_vec = shell.vars();
11-
assert_eq!(cur_env_vec.next().unwrap(), cstr16!("path"),);
11+
assert_eq!(cur_env_vec.next().unwrap().0, cstr16!("path"));
1212
// check pre-defined shell variables; see UEFI Shell spec
13-
assert_eq!(cur_env_vec.next().unwrap(), cstr16!("nonesting"),);
13+
assert_eq!(cur_env_vec.next().unwrap().0, cstr16!("nonesting"));
1414
let cur_env_vec = shell.vars();
1515
let default_len = cur_env_vec.count();
1616

@@ -27,15 +27,15 @@ pub fn test_env(shell: &ScopedProtocol<Shell>) {
2727
assert_eq!(cur_env_str, test_val);
2828

2929
let mut found_var = false;
30-
for env_var in cur_env_vec {
30+
for (env_var, _) in cur_env_vec {
3131
if env_var == test_var {
3232
found_var = true;
3333
}
3434
}
3535
assert!(!found_var);
3636
let cur_env_vec = shell.vars();
3737
let mut found_var = false;
38-
for env_var in cur_env_vec {
38+
for (env_var, _) in cur_env_vec {
3939
if env_var == test_var {
4040
found_var = true;
4141
}
@@ -53,7 +53,7 @@ pub fn test_env(shell: &ScopedProtocol<Shell>) {
5353

5454
let cur_env_vec = shell.vars();
5555
let mut found_var = false;
56-
for env_var in cur_env_vec {
56+
for (env_var, _) in cur_env_vec {
5757
if env_var == test_var {
5858
found_var = true;
5959
}

uefi/src/proto/shell/mod.rs

Lines changed: 68 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,30 +17,45 @@ use crate::{CStr16, Char16, Result, StatusExt};
1717
#[unsafe_protocol(ShellProtocol::GUID)]
1818
pub struct Shell(ShellProtocol);
1919

20+
/// Trait for implementing the var function
21+
pub trait ShellVar {
22+
/// Gets the value of the specified environment variable
23+
fn var(&self, name: &CStr16) -> Option<&CStr16>;
24+
}
25+
2026
/// Iterator over the names of environmental variables obtained from the Shell protocol.
2127
#[derive(Debug)]
22-
pub struct Vars<'a> {
28+
pub struct Vars<'a, T: ShellVar> {
2329
/// Char16 containing names of environment variables
24-
inner: *const Char16,
30+
names: *const Char16,
31+
/// Reference to Shell Protocol
32+
protocol: *const T,
2533
/// Placeholder to attach a lifetime to `Vars`
2634
placeholder: PhantomData<&'a CStr16>,
2735
}
2836

29-
impl<'a> Iterator for Vars<'a> {
30-
type Item = &'a CStr16;
37+
impl<'a, T: ShellVar + 'a> Iterator for Vars<'a, T> {
38+
type Item = (&'a CStr16, Option<&'a CStr16>);
3139
// We iterate a list of NUL terminated CStr16s.
3240
// The list is terminated with a double NUL.
3341
fn next(&mut self) -> Option<Self::Item> {
34-
let s = unsafe { CStr16::from_ptr(self.inner) };
42+
let s = unsafe { CStr16::from_ptr(self.names) };
3543
if s.is_empty() {
3644
None
3745
} else {
38-
self.inner = unsafe { self.inner.add(s.num_chars() + 1) };
39-
Some(s)
46+
self.names = unsafe { self.names.add(s.num_chars() + 1) };
47+
Some((s, unsafe { self.protocol.as_ref().unwrap().var(s) }))
4048
}
4149
}
4250
}
4351

52+
impl ShellVar for Shell {
53+
/// Gets the value of the specified environment variable
54+
fn var(&self, name: &CStr16) -> Option<&CStr16> {
55+
self.var(name)
56+
}
57+
}
58+
4459
impl Shell {
4560
/// Gets the value of the specified environment variable
4661
///
@@ -67,10 +82,11 @@ impl Shell {
6782

6883
/// Gets an iterator over the names of all environment variables
6984
#[must_use]
70-
pub fn vars(&self) -> Vars<'_> {
85+
pub fn vars(&self) -> Vars<'_, Self> {
7186
let env_ptr = unsafe { (self.0.get_env)(ptr::null()) };
7287
Vars {
73-
inner: env_ptr.cast::<Char16>(),
88+
names: env_ptr.cast::<Char16>(),
89+
protocol: self,
7490
placeholder: PhantomData,
7591
}
7692
}
@@ -144,9 +160,33 @@ impl Shell {
144160
#[cfg(test)]
145161
mod tests {
146162
use super::*;
163+
use alloc::collections::BTreeMap;
147164
use alloc::vec::Vec;
148165
use uefi::cstr16;
149166

167+
struct ShellMock<'a> {
168+
inner: BTreeMap<&'a CStr16, &'a CStr16>,
169+
}
170+
171+
impl<'a> ShellMock<'a> {
172+
fn new(names: Vec<&'a CStr16>, values: Vec<&'a CStr16>) -> ShellMock<'a> {
173+
let mut inner_map = BTreeMap::new();
174+
for (name, val) in names.iter().zip(values.iter()) {
175+
inner_map.insert(*name, *val);
176+
}
177+
ShellMock { inner: inner_map }
178+
}
179+
}
180+
impl<'a> ShellVar for ShellMock<'a> {
181+
fn var(&self, name: &CStr16) -> Option<&CStr16> {
182+
if let Some(val) = self.inner.get(name) {
183+
Some(*val)
184+
} else {
185+
None
186+
}
187+
}
188+
}
189+
150190
/// Testing Vars struct
151191
#[test]
152192
fn test_vars() {
@@ -155,9 +195,11 @@ mod tests {
155195
vars_mock.push(0);
156196
vars_mock.push(0);
157197
let mut vars = Vars {
158-
inner: vars_mock.as_ptr().cast(),
198+
names: vars_mock.as_ptr().cast(),
199+
protocol: &ShellMock::new(Vec::new(), Vec::new()),
159200
placeholder: PhantomData,
160201
};
202+
161203
assert!(vars.next().is_none());
162204

163205
// One environment variable in Vars
@@ -168,10 +210,14 @@ mod tests {
168210
vars_mock.push(0);
169211
vars_mock.push(0);
170212
let vars = Vars {
171-
inner: vars_mock.as_ptr().cast(),
213+
names: vars_mock.as_ptr().cast(),
214+
protocol: &ShellMock::new(Vec::from([cstr16!("foo")]), Vec::from([cstr16!("value")])),
172215
placeholder: PhantomData,
173216
};
174-
assert_eq!(vars.collect::<Vec<_>>(), Vec::from([cstr16!("foo")]));
217+
assert_eq!(
218+
vars.collect::<Vec<_>>(),
219+
Vec::from([(cstr16!("foo"), Some(cstr16!("value")))])
220+
);
175221

176222
// Multiple environment variables in Vars
177223
let mut vars_mock = Vec::<u16>::new();
@@ -192,12 +238,20 @@ mod tests {
192238
vars_mock.push(0);
193239

194240
let vars = Vars {
195-
inner: vars_mock.as_ptr().cast(),
241+
names: vars_mock.as_ptr().cast(),
242+
protocol: &ShellMock::new(
243+
Vec::from([cstr16!("foo1"), cstr16!("bar"), cstr16!("baz2")]),
244+
Vec::from([cstr16!("value"), cstr16!("one"), cstr16!("two")]),
245+
),
196246
placeholder: PhantomData,
197247
};
198248
assert_eq!(
199249
vars.collect::<Vec<_>>(),
200-
Vec::from([cstr16!("foo1"), cstr16!("bar"), cstr16!("baz2")])
250+
Vec::from([
251+
(cstr16!("foo1"), Some(cstr16!("value"))),
252+
(cstr16!("bar"), Some(cstr16!("one"))),
253+
(cstr16!("baz2"), Some(cstr16!("two")))
254+
])
201255
);
202256
}
203257
}

0 commit comments

Comments
 (0)