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