Skip to content

Commit 116701e

Browse files
committed
uefi: Modifying Vars to return environment variable values in addition to names
1 parent 7a5b955 commit 116701e

File tree

2 files changed

+75
-21
lines changed

2 files changed

+75
-21
lines changed

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

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@ use uefi::proto::shell::Shell;
55
use uefi::{boot, cstr16};
66

77
/// Test `var()`, `vars()`, and `set_var()`
8-
pub fn test_env(shell: &ScopedProtocol<Shell>) {
8+
pub fn test_var(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
}
@@ -71,5 +71,5 @@ pub fn test() {
7171
let shell =
7272
boot::open_protocol_exclusive::<Shell>(handle).expect("Failed to open Shell protocol");
7373

74-
test_env(&shell);
74+
test_var(&shell);
7575
}

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
}
@@ -97,9 +113,33 @@ impl Shell {
97113
#[cfg(test)]
98114
mod tests {
99115
use super::*;
116+
use alloc::collections::BTreeMap;
100117
use alloc::vec::Vec;
101118
use uefi::cstr16;
102119

120+
struct ShellMock<'a> {
121+
inner: BTreeMap<&'a CStr16, &'a CStr16>,
122+
}
123+
124+
impl<'a> ShellMock<'a> {
125+
fn new(names: Vec<&'a CStr16>, values: Vec<&'a CStr16>) -> ShellMock<'a> {
126+
let mut inner_map = BTreeMap::new();
127+
for (name, val) in names.iter().zip(values.iter()) {
128+
inner_map.insert(*name, *val);
129+
}
130+
ShellMock { inner: inner_map }
131+
}
132+
}
133+
impl<'a> ShellVar for ShellMock<'a> {
134+
fn var(&self, name: &CStr16) -> Option<&CStr16> {
135+
if let Some(val) = self.inner.get(name) {
136+
Some(*val)
137+
} else {
138+
None
139+
}
140+
}
141+
}
142+
103143
/// Testing Vars struct
104144
#[test]
105145
fn test_vars() {
@@ -108,9 +148,11 @@ mod tests {
108148
vars_mock.push(0);
109149
vars_mock.push(0);
110150
let mut vars = Vars {
111-
inner: vars_mock.as_ptr().cast(),
151+
names: vars_mock.as_ptr().cast(),
152+
protocol: &ShellMock::new(Vec::new(), Vec::new()),
112153
placeholder: PhantomData,
113154
};
155+
114156
assert!(vars.next().is_none());
115157

116158
// One environment variable in Vars
@@ -121,10 +163,14 @@ mod tests {
121163
vars_mock.push(0);
122164
vars_mock.push(0);
123165
let vars = Vars {
124-
inner: vars_mock.as_ptr().cast(),
166+
names: vars_mock.as_ptr().cast(),
167+
protocol: &ShellMock::new(Vec::from([cstr16!("foo")]), Vec::from([cstr16!("value")])),
125168
placeholder: PhantomData,
126169
};
127-
assert_eq!(vars.collect::<Vec<_>>(), Vec::from([cstr16!("foo")]));
170+
assert_eq!(
171+
vars.collect::<Vec<_>>(),
172+
Vec::from([(cstr16!("foo"), Some(cstr16!("value")))])
173+
);
128174

129175
// Multiple environment variables in Vars
130176
let mut vars_mock = Vec::<u16>::new();
@@ -145,12 +191,20 @@ mod tests {
145191
vars_mock.push(0);
146192

147193
let vars = Vars {
148-
inner: vars_mock.as_ptr().cast(),
194+
names: vars_mock.as_ptr().cast(),
195+
protocol: &ShellMock::new(
196+
Vec::from([cstr16!("foo1"), cstr16!("bar"), cstr16!("baz2")]),
197+
Vec::from([cstr16!("value"), cstr16!("one"), cstr16!("two")]),
198+
),
149199
placeholder: PhantomData,
150200
};
151201
assert_eq!(
152202
vars.collect::<Vec<_>>(),
153-
Vec::from([cstr16!("foo1"), cstr16!("bar"), cstr16!("baz2")])
203+
Vec::from([
204+
(cstr16!("foo1"), Some(cstr16!("value"))),
205+
(cstr16!("bar"), Some(cstr16!("one"))),
206+
(cstr16!("baz2"), Some(cstr16!("two")))
207+
])
154208
);
155209
}
156210
}

0 commit comments

Comments
 (0)