44
55use crate :: proto:: unsafe_protocol;
66use crate :: { CStr16 , Char16 , Error , Result , Status , StatusExt } ;
7+
8+ use core:: marker:: PhantomData ;
79use core:: ptr;
810use uefi_raw:: protocol:: shell:: ShellProtocol ;
911
@@ -13,6 +15,45 @@ use uefi_raw::protocol::shell::ShellProtocol;
1315#[ unsafe_protocol( ShellProtocol :: GUID ) ]
1416pub struct Shell ( ShellProtocol ) ;
1517
18+ /// Trait for implementing the var function
19+ pub trait ShellVarProvider {
20+ /// Gets the value of the specified environment variable
21+ fn var ( & self , name : & CStr16 ) -> Option < & CStr16 > ;
22+ }
23+
24+ /// Iterator over the names of environmental variables obtained from the Shell protocol.
25+ #[ derive( Debug ) ]
26+ pub struct Vars < ' a , T : ShellVarProvider > {
27+ /// Char16 containing names of environment variables
28+ names : * const Char16 ,
29+ /// Reference to Shell Protocol
30+ protocol : * const T ,
31+ /// Marker to attach a lifetime to `Vars`
32+ _marker : PhantomData < & ' a CStr16 > ,
33+ }
34+
35+ impl < ' a , T : ShellVarProvider + ' a > Iterator for Vars < ' a , T > {
36+ type Item = ( & ' a CStr16 , Option < & ' a CStr16 > ) ;
37+ // We iterate a list of NUL terminated CStr16s.
38+ // The list is terminated with a double NUL.
39+ fn next ( & mut self ) -> Option < Self :: Item > {
40+ let s = unsafe { CStr16 :: from_ptr ( self . names ) } ;
41+ if s. is_empty ( ) {
42+ None
43+ } else {
44+ self . names = unsafe { self . names . add ( s. num_chars ( ) + 1 ) } ;
45+ Some ( ( s, unsafe { self . protocol . as_ref ( ) . unwrap ( ) . var ( s) } ) )
46+ }
47+ }
48+ }
49+
50+ impl ShellVarProvider for Shell {
51+ /// Gets the value of the specified environment variable
52+ fn var ( & self , name : & CStr16 ) -> Option < & CStr16 > {
53+ self . var ( name)
54+ }
55+ }
56+
1657impl Shell {
1758 /// Returns the current directory on the specified device.
1859 ///
@@ -54,4 +95,159 @@ impl Shell {
5495 let dir_ptr: * const Char16 = directory. map_or ( ptr:: null ( ) , |x| x. as_ptr ( ) ) ;
5596 unsafe { ( self . 0 . set_cur_dir ) ( fs_ptr. cast ( ) , dir_ptr. cast ( ) ) } . to_result ( )
5697 }
98+
99+ /// Gets the value of the specified environment variable
100+ ///
101+ /// # Arguments
102+ ///
103+ /// * `name` - The environment variable name of which to retrieve the
104+ /// value.
105+ ///
106+ /// # Returns
107+ ///
108+ /// * `Some(<env_value>)` - &CStr16 containing the value of the
109+ /// environment variable
110+ /// * `None` - If environment variable does not exist
111+ #[ must_use]
112+ pub fn var ( & self , name : & CStr16 ) -> Option < & CStr16 > {
113+ let name_ptr: * const Char16 = name. as_ptr ( ) ;
114+ let var_val = unsafe { ( self . 0 . get_env ) ( name_ptr. cast ( ) ) } ;
115+ if var_val. is_null ( ) {
116+ None
117+ } else {
118+ unsafe { Some ( CStr16 :: from_ptr ( var_val. cast ( ) ) ) }
119+ }
120+ }
121+
122+ /// Gets an iterator over the names of all environment variables
123+ ///
124+ /// # Returns
125+ ///
126+ /// * `Vars` - Iterator over the names of the environment variables
127+ #[ must_use]
128+ pub fn vars ( & self ) -> Vars < ' _ , Self > {
129+ let env_ptr = unsafe { ( self . 0 . get_env ) ( ptr:: null ( ) ) } ;
130+ Vars {
131+ names : env_ptr. cast :: < Char16 > ( ) ,
132+ protocol : self ,
133+ _marker : PhantomData ,
134+ }
135+ }
136+
137+ /// Sets the environment variable
138+ ///
139+ /// # Arguments
140+ ///
141+ /// * `name` - The environment variable for which to set the value
142+ /// * `value` - The new value of the environment variable
143+ /// * `volatile` - Indicates whether the variable is volatile or
144+ /// not
145+ ///
146+ /// # Returns
147+ ///
148+ /// * `Status::SUCCESS` - The variable was successfully set
149+ pub fn set_var ( & self , name : & CStr16 , value : & CStr16 , volatile : bool ) -> Result {
150+ let name_ptr: * const Char16 = name. as_ptr ( ) ;
151+ let value_ptr: * const Char16 = value. as_ptr ( ) ;
152+ unsafe { ( self . 0 . set_env ) ( name_ptr. cast ( ) , value_ptr. cast ( ) , volatile) } . to_result ( )
153+ }
154+ }
155+
156+ #[ cfg( test) ]
157+ mod tests {
158+ use super :: * ;
159+ use alloc:: collections:: BTreeMap ;
160+ use alloc:: vec:: Vec ;
161+ use uefi:: cstr16;
162+
163+ struct ShellMock < ' a > {
164+ inner : BTreeMap < & ' a CStr16 , & ' a CStr16 > ,
165+ }
166+
167+ impl < ' a > ShellMock < ' a > {
168+ fn new ( pairs : impl IntoIterator < Item = ( & ' a CStr16 , & ' a CStr16 ) > ) -> ShellMock < ' a > {
169+ let mut inner_map = BTreeMap :: new ( ) ;
170+ for ( name, val) in pairs. into_iter ( ) {
171+ inner_map. insert ( name, val) ;
172+ }
173+ ShellMock { inner : inner_map }
174+ }
175+ }
176+ impl < ' a > ShellVarProvider for ShellMock < ' a > {
177+ fn var ( & self , name : & CStr16 ) -> Option < & CStr16 > {
178+ if let Some ( val) = self . inner . get ( name) {
179+ Some ( * val)
180+ } else {
181+ None
182+ }
183+ }
184+ }
185+
186+ /// Testing Vars struct
187+ #[ test]
188+ fn test_vars ( ) {
189+ // Empty Vars
190+ let mut vars_mock = Vec :: < u16 > :: new ( ) ;
191+ vars_mock. extend_from_slice (
192+ b"\0 \0 "
193+ . into_iter ( )
194+ . map ( |& x| x as u16 )
195+ . collect :: < Vec < _ > > ( )
196+ . as_slice ( ) ,
197+ ) ;
198+ let mut vars = Vars {
199+ names : vars_mock. as_ptr ( ) . cast ( ) ,
200+ protocol : & ShellMock :: new ( Vec :: new ( ) ) ,
201+ _marker : PhantomData ,
202+ } ;
203+
204+ assert ! ( vars. next( ) . is_none( ) ) ;
205+
206+ // One environment variable in Vars
207+ let mut vars_mock = Vec :: < u16 > :: new ( ) ;
208+ vars_mock. extend_from_slice (
209+ b"foo\0 \0 "
210+ . into_iter ( )
211+ . map ( |& x| x as u16 )
212+ . collect :: < Vec < _ > > ( )
213+ . as_slice ( ) ,
214+ ) ;
215+ let vars = Vars {
216+ names : vars_mock. as_ptr ( ) . cast ( ) ,
217+ protocol : & ShellMock :: new ( Vec :: from ( [ ( cstr16 ! ( "foo" ) , cstr16 ! ( "value" ) ) ] ) ) ,
218+ _marker : PhantomData ,
219+ } ;
220+ assert_eq ! (
221+ vars. collect:: <Vec <_>>( ) ,
222+ Vec :: from( [ ( cstr16!( "foo" ) , Some ( cstr16!( "value" ) ) ) ] )
223+ ) ;
224+
225+ // Multiple environment variables in Vars
226+ let mut vars_mock = Vec :: < u16 > :: new ( ) ;
227+ vars_mock. extend_from_slice (
228+ b"foo1\0 bar\0 baz2\0 \0 "
229+ . into_iter ( )
230+ . map ( |& x| x as u16 )
231+ . collect :: < Vec < _ > > ( )
232+ . as_slice ( ) ,
233+ ) ;
234+
235+ let vars = Vars {
236+ names : vars_mock. as_ptr ( ) . cast ( ) ,
237+ protocol : & ShellMock :: new ( Vec :: from ( [
238+ ( cstr16 ! ( "foo1" ) , cstr16 ! ( "value" ) ) ,
239+ ( cstr16 ! ( "bar" ) , cstr16 ! ( "one" ) ) ,
240+ ( cstr16 ! ( "baz2" ) , cstr16 ! ( "two" ) ) ,
241+ ] ) ) ,
242+ _marker : PhantomData ,
243+ } ;
244+ assert_eq ! (
245+ vars. collect:: <Vec <_>>( ) ,
246+ Vec :: from( [
247+ ( cstr16!( "foo1" ) , Some ( cstr16!( "value" ) ) ) ,
248+ ( cstr16!( "bar" ) , Some ( cstr16!( "one" ) ) ) ,
249+ ( cstr16!( "baz2" ) , Some ( cstr16!( "two" ) ) )
250+ ] )
251+ ) ;
252+ }
57253}
0 commit comments