]> git.proxmox.com Git - rustc.git/blob - src/doc/book/src/ch12-05-working-with-environment-variables.md
New upstream version 1.63.0+dfsg1
[rustc.git] / src / doc / book / src / ch12-05-working-with-environment-variables.md
1 ## Working with Environment Variables
2
3 We’ll improve `minigrep` by adding an extra feature: an option for
4 case-insensitive searching that the user can turn on via an environment
5 variable. We could make this feature a command line option and require that
6 users enter it each time they want it to apply, but by instead making it an
7 environment variable, we allow our users to set the environment variable once
8 and have all their searches be case insensitive in that terminal session.
9
10 ### Writing a Failing Test for the Case-Insensitive `search` Function
11
12 We first add a new `search_case_insensitive` function that will be called when
13 the environment variable has a value. We’ll continue to follow the TDD process,
14 so the first step is again to write a failing test. We’ll add a new test for
15 the new `search_case_insensitive` function and rename our old test from
16 `one_result` to `case_sensitive` to clarify the differences between the two
17 tests, as shown in Listing 12-20.
18
19 <span class="filename">Filename: src/lib.rs</span>
20
21 ```rust,ignore,does_not_compile
22 {{#rustdoc_include ../listings/ch12-an-io-project/listing-12-20/src/lib.rs:here}}
23 ```
24
25 <span class="caption">Listing 12-20: Adding a new failing test for the
26 case-insensitive function we’re about to add</span>
27
28 Note that we’ve edited the old test’s `contents` too. We’ve added a new line
29 with the text `"Duct tape."` using a capital D that shouldn’t match the query
30 `"duct"` when we’re searching in a case-sensitive manner. Changing the old test
31 in this way helps ensure that we don’t accidentally break the case-sensitive
32 search functionality that we’ve already implemented. This test should pass now
33 and should continue to pass as we work on the case-insensitive search.
34
35 The new test for the case-*insensitive* search uses `"rUsT"` as its query. In
36 the `search_case_insensitive` function we’re about to add, the query `"rUsT"`
37 should match the line containing `"Rust:"` with a capital R and match the line
38 `"Trust me."` even though both have different casing from the query. This is
39 our failing test, and it will fail to compile because we haven’t yet defined
40 the `search_case_insensitive` function. Feel free to add a skeleton
41 implementation that always returns an empty vector, similar to the way we did
42 for the `search` function in Listing 12-16 to see the test compile and fail.
43
44 ### Implementing the `search_case_insensitive` Function
45
46 The `search_case_insensitive` function, shown in Listing 12-21, will be almost
47 the same as the `search` function. The only difference is that we’ll lowercase
48 the `query` and each `line` so whatever the case of the input arguments,
49 they’ll be the same case when we check whether the line contains the query.
50
51 <span class="filename">Filename: src/lib.rs</span>
52
53 ```rust,noplayground
54 {{#rustdoc_include ../listings/ch12-an-io-project/listing-12-21/src/lib.rs:here}}
55 ```
56
57 <span class="caption">Listing 12-21: Defining the `search_case_insensitive`
58 function to lowercase the query and the line before comparing them</span>
59
60 First, we lowercase the `query` string and store it in a shadowed variable with
61 the same name. Calling `to_lowercase` on the query is necessary so no
62 matter whether the user’s query is `"rust"`, `"RUST"`, `"Rust"`, or `"rUsT"`,
63 we’ll treat the query as if it were `"rust"` and be insensitive to the case.
64 While `to_lowercase` will handle basic Unicode, it won’t be 100% accurate. If
65 we were writing a real application, we’d want to do a bit more work here, but
66 this section is about environment variables, not Unicode, so we’ll leave it at
67 that here.
68
69 Note that `query` is now a `String` rather than a string slice, because calling
70 `to_lowercase` creates new data rather than referencing existing data. Say the
71 query is `"rUsT"`, as an example: that string slice doesn’t contain a lowercase
72 `u` or `t` for us to use, so we have to allocate a new `String` containing
73 `"rust"`. When we pass `query` as an argument to the `contains` method now, we
74 need to add an ampersand because the signature of `contains` is defined to take
75 a string slice.
76
77 Next, we add a call to `to_lowercase` on each `line` to lowercase all
78 characters. Now that we’ve converted `line` and `query` to lowercase, we’ll
79 find matches no matter what the case of the query is.
80
81 Let’s see if this implementation passes the tests:
82
83 ```console
84 {{#include ../listings/ch12-an-io-project/listing-12-21/output.txt}}
85 ```
86
87 Great! They passed. Now, let’s call the new `search_case_insensitive` function
88 from the `run` function. First, we’ll add a configuration option to the
89 `Config` struct to switch between case-sensitive and case-insensitive search.
90 Adding this field will cause compiler errors because we aren’t initializing
91 this field anywhere yet:
92
93 <span class="filename">Filename: src/lib.rs</span>
94
95 ```rust,ignore,does_not_compile
96 {{#rustdoc_include ../listings/ch12-an-io-project/listing-12-22/src/lib.rs:here}}
97 ```
98
99 We added the `ignore_case` field that holds a Boolean. Next, we need the `run`
100 function to check the `ignore_case` field’s value and use that to decide
101 whether to call the `search` function or the `search_case_insensitive`
102 function, as shown in Listing 12-22. This still won’t compile yet.
103
104 <span class="filename">Filename: src/lib.rs</span>
105
106 ```rust,ignore,does_not_compile
107 {{#rustdoc_include ../listings/ch12-an-io-project/listing-12-22/src/lib.rs:there}}
108 ```
109
110 <span class="caption">Listing 12-22: Calling either `search` or
111 `search_case_insensitive` based on the value in `config.ignore_case`</span>
112
113 Finally, we need to check for the environment variable. The functions for
114 working with environment variables are in the `env` module in the standard
115 library, so we bring that module into scope at the top of *src/lib.rs*. Then
116 we’ll use the `var` function from the `env` module to check to see if any value
117 has been set for an environment variable named `IGNORE_CASE`, as shown in
118 Listing 12-23.
119
120 <span class="filename">Filename: src/lib.rs</span>
121
122 ```rust,noplayground
123 {{#rustdoc_include ../listings/ch12-an-io-project/listing-12-23/src/lib.rs:here}}
124 ```
125
126 <span class="caption">Listing 12-23: Checking for any value in an environment
127 variable named `IGNORE_CASE`</span>
128
129 Here, we create a new variable `ignore_case`. To set its value, we call the
130 `env::var` function and pass it the name of the `IGNORE_CASE` environment
131 variable. The `env::var` function returns a `Result` that will be the
132 successful `Ok` variant that contains the value of the environment variable if
133 the environment variable is set to any value. It will return the `Err` variant
134 if the environment variable is not set.
135
136 We’re using the `is_ok` method on the `Result` to check whether the environment
137 variable is set, which means the program should do a case-insensitive search.
138 If the `IGNORE_CASE` environment variable isn’t set to anything, `is_ok` will
139 return false and the program will perform a case-sensitive search. We don’t
140 care about the *value* of the environment variable, just whether it’s set or
141 unset, so we’re checking `is_ok` rather than using `unwrap`, `expect`, or any
142 of the other methods we’ve seen on `Result`.
143
144 We pass the value in the `ignore_case` variable to the `Config` instance so the
145 `run` function can read that value and decide whether to call
146 `search_case_insensitive` or `search`, as we implemented in Listing 12-22.
147
148 Let’s give it a try! First, we’ll run our program without the environment
149 variable set and with the query `to`, which should match any line that contains
150 the word “to” in all lowercase:
151
152 ```console
153 {{#include ../listings/ch12-an-io-project/listing-12-23/output.txt}}
154 ```
155
156 Looks like that still works! Now, let’s run the program with `IGNORE_CASE`
157 set to `1` but with the same query `to`.
158
159 ```console
160 $ IGNORE_CASE=1 cargo run -- to poem.txt
161 ```
162
163 If you’re using PowerShell, you will need to set the environment variable and
164 run the program as separate commands:
165
166 ```console
167 PS> $Env:IGNORE_CASE=1; cargo run -- to poem.txt
168 ```
169
170 This will make `IGNORE_CASE` persist for the remainder of your shell
171 session. It can be unset with the `Remove-Item` cmdlet:
172
173 ```console
174 PS> Remove-Item Env:IGNORE_CASE
175 ```
176
177 We should get lines that contain “to” that might have uppercase letters:
178
179 <!-- manual-regeneration
180 cd listings/ch12-an-io-project/listing-12-23
181 IGNORE_CASE=1 cargo run -- to poem.txt
182 can't extract because of the environment variable
183 -->
184
185 ```console
186 Are you nobody, too?
187 How dreary to be somebody!
188 To tell your name the livelong day
189 To an admiring bog!
190 ```
191
192 Excellent, we also got lines containing “To”! Our `minigrep` program can now do
193 case-insensitive searching controlled by an environment variable. Now you know
194 how to manage options set using either command line arguments or environment
195 variables.
196
197 Some programs allow arguments *and* environment variables for the same
198 configuration. In those cases, the programs decide that one or the other takes
199 precedence. For another exercise on your own, try controlling case sensitivity
200 through either a command line argument or an environment variable. Decide
201 whether the command line argument or the environment variable should take
202 precedence if the program is run with one set to case sensitive and one set to
203 ignore case.
204
205 The `std::env` module contains many more useful features for dealing with
206 environment variables: check out its documentation to see what is available.