Rust
JSON config reader
Build a CLI that reads a config file with serde.
Final task: config reader
The last task builds a typical flow from real applications: read the configuration from JSON text, override it with command-line arguments, and report errors with clear messages. The real-project counterpart is serde_json + clap + anyhow:
#[derive(Deserialize)]
struct Ayar { ad: String, port: u16, hata_ayikla: bool }
let mut ayar: Ayar = serde_json::from_str(&metin).context("ayar okunamadı")?;
if let Some(p) = cli.port { ayar.port = p; }
In the exercise you'll write the reader and the override logic with the standard library.
Reading and overriding
The reader converts each key by its type and returns errors with context; the override step applies only the given arguments:
fn oku(json: &str) -> Result<Ayar, String> {
// parse key by key, for port:
// deger.parse().map_err(|_| format!("port geçersiz: {deger}"))?
}
fn ez(ayar: &mut Ayar, args: &[&str]) -> Result<(), String> {
if let Some(i) = args.iter().position(|a| *a == "--port") {
let s = args.get(i + 1).ok_or("--port değeri eksik")?;
ayar.port = s.parse().map_err(|_| format!("port geçersiz: {s}"))?;
}
Ok(())
}
Exercise Config reader
Write the oku and ez functions for Ayar { ad: String, port: u16, hata_ayikla: bool }. Read the text {"ad":"blog","port":3000,"hata_ayikla":false}, override with ["--port", "8080"] and print the values ad port hata_ayikla. Then read a text containing "port":"abc" and print the error as hata: ....
Show example solution
struct Ayar {
ad: String,
port: u16,
hata_ayikla: bool,
}
fn oku(json: &str) -> Result<Ayar, String> {
let mut ad = None;
let mut port = None;
let mut hata_ayikla = None;
for cift in json.trim().trim_start_matches('{').trim_end_matches('}').split(',') {
let (a, d) = cift.split_once(':').ok_or("geçersiz çift")?;
let deger = d.trim().trim_matches('"');
match a.trim().trim_matches('"') {
"ad" => ad = Some(deger.to_string()),
"port" => port = Some(deger.parse().map_err(|_| format!("port geçersiz: {deger}"))?),
"hata_ayikla" => hata_ayikla = Some(deger == "true"),
k => return Err(format!("bilinmeyen anahtar: {k}")),
}
}
Ok(Ayar {
ad: ad.ok_or("ad eksik")?,
port: port.ok_or("port eksik")?,
hata_ayikla: hata_ayikla.unwrap_or(false),
})
}
fn ez(ayar: &mut Ayar, args: &[&str]) -> Result<(), String> {
if let Some(i) = args.iter().position(|a| *a == "--port") {
let s = args.get(i + 1).ok_or("--port değeri eksik")?;
ayar.port = s.parse().map_err(|_| format!("port geçersiz: {s}"))?;
}
Ok(())
}
fn main() {
let mut a = oku(r#"{"ad":"blog","port":3000,"hata_ayikla":false}"#).unwrap();
ez(&mut a, &["--port", "8080"]).unwrap();
println!("{} {} {}", a.ad, a.port, a.hata_ayikla);
match oku(r#"{"ad":"blog","port":"abc"}"#) {
Ok(_) => println!("tamam"),
Err(e) => println!("hata: {}", e),
}
}
Test yourself
-
Why is it a common rule for the command-line value to override the file?
- It's faster
- It isn't required
- Temporary changes don't require editing the file
- The file can't be read
Show answer
Temporary changes don't require editing the file — The usual priority: argument > environment variable > file > default.
-
Change the setting
fn ez(ayar: ____, args: &[&str]) -> Result<(), String>
Show answer
fn ez(ayar: &mut Ayar, args: &[&str]) -> Result<(), String>
-
Input and result
- port 3000 + --port 8080
- port 8080
- port 3000 (no argument)
- port 3000
- "port":"abc"
- port geçersiz: abc
- --port (no value)
- --port value missing
Next lesson: Intermediate section exam