Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Çevre Değişkenleriyle (Environment Variables) Çalışmak

minigrep ikili dosyasına ek bir özellik (feature) ekleyerek onu iyileştireceğiz: kullanıcının bir çevre değişkeni aracılığıyla açabileceği büyük/küçük harf duyarsız arama seçeneği. Bu özelliği bir komut satırı seçeneği (command line option) haline getirebilir ve kullanıcıların bunu her uygulamak istediklerinde girmelerini gerektirebilirdik, ancak bunun yerine onu bir çevre değişkeni haline getirerek, kullanıcılarımızın çevre değişkenini bir kez ayarlamasına ve o terminal oturumundaki tüm aramalarının büyük/küçük harfe duyarsız olmasına izin veriyoruz.

Büyük/Küçük Harf Duyarsız Arama Fonksiyonu İçin Başarısız Olan Bir Test Yazmak

İlk olarak minigrep kütüphanesine, çevre değişkeninin bir değeri olduğunda çağrılacak olan buyuk_kucuk_harf_duyarsiz_ara adlı yeni bir fonksiyon ekliyoruz. TDD (test güdümlü geliştirme) sürecini takip etmeye devam edeceğiz, bu nedenle ilk adım yine başarısız olan bir test yazmaktır. Yeni buyuk_kucuk_harf_duyarsiz_ara fonksiyonu için yeni bir test ekleyeceğiz ve iki test arasındaki farkları netleştirmek için Liste 12-20’de gösterildiği gibi eski testimizi tek_sonuc (one_result) yerine buyuk_kucuk_harf_duyarli (case_sensitive) olarak yeniden adlandıracağız.

Filename: src/lib.rs
pub fn ara<'a>(sorgu: &str, icerik: &'a str) -> Vec<&'a str> {
    let mut sonuclar = Vec::new();

    for satir in icerik.lines() {
        if satir.contains(sorgu) {
            sonuclar.push(satir);
        }
    }

    sonuclar
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn buyuk_kucuk_harf_duyarli() {
        let sorgu = "güven";
        let icerik = "\
Güven:
güvenli, hızlı, üretken.
Üçünü de seç.
Güven kolay kazanılmaz.";

        assert_eq!(vec!["güvenli, hızlı, üretken."], ara(sorgu, icerik));
    }

    #[test]
    fn buyuk_kucuk_harf_duyarsiz() {
        let sorgu = "güven";
        let icerik = "\
Güven:
önce sağlamlık gelir.
Planlı çalış, sakin ol.
güven kazanılır.";

        assert_eq!(
            vec!["Güven:", "güven kazanılır."],
            buyuk_kucuk_harf_duyarsiz_ara(sorgu, icerik)
        );
    }
}
Listing 12-20: Eklemek üzere olduğumuz büyük/küçük harf duyarsız fonksiyonu için başarısız olan yeni bir test eklemek

Eski testin icerik kısmını da düzenlediğimize dikkat edin. Büyük/küçük harf duyarlı bir şekilde arama yaptığımızda "güven" sorgusuyla eşleşmemesi gereken, büyük G kullanan "Güven kolay kazanılmaz." metnine sahip yeni bir satır ekledik. Eski testi bu şekilde değiştirmek, daha önce uyguladığımız büyük/küçük harf duyarlı arama işlevselliğini yanlışlıkla bozmadığımızdan emin olmamıza yardımcı olur. Bu test şimdi geçmeli ve biz büyük/küçük harf duyarsız arama üzerinde çalışırken de geçmeye devam etmelidir.

Büyük/küçük harf duyarsız arama için yeni test sorgu olarak "gÜvEn" kullanıyor. Eklemek üzere olduğumuz buyuk_kucuk_harf_duyarsiz_ara fonksiyonunda "gÜvEn" sorgusu, büyük G harfi içeren "Güven:" satırıyla eşleşmeli ve her ikisi de sorgudan farklı büyük/küçük harf kullanımına sahip olsa bile "güven kazanılır." satırıyla da eşleşmelidir. Bu bizim başarısız (failing) testimizdir ve derlenemeyecektir çünkü henüz buyuk_kucuk_harf_duyarsiz_ara fonksiyonunu tanımlamadık. Testin derlendiğini ve başarısız olduğunu görmek için Liste 12-16’da ara fonksiyonu için yaptığımıza benzer şekilde her zaman boş bir vektör döndüren iskelet bir uygulama eklemekten çekinmeyin.

buyuk_kucuk_harf_duyarsiz_ara Fonksiyonunu Uygulamak (Implementing)

Liste 12-21’de gösterilen buyuk_kucuk_harf_duyarsiz_ara fonksiyonu, ara fonksiyonu ile neredeyse aynı olacaktır. Tek fark, girdi argümanlarının durumu ne olursa olsun, satırın sorguyu barındırıp barındırmadığını (contains) kontrol ettiğimizde aynı durumda olmaları için sorguyu ve her bir satirı küçük harfe dönüştürecek olmamızdır.

Filename: src/lib.rs
pub fn ara<'a>(sorgu: &str, icerik: &'a str) -> Vec<&'a str> {
    let mut sonuclar = Vec::new();

    for satir in icerik.lines() {
        if satir.contains(sorgu) {
            sonuclar.push(satir);
        }
    }

    sonuclar
}

pub fn buyuk_kucuk_harf_duyarsiz_ara<'a>(
    sorgu: &str,
    icerik: &'a str,
) -> Vec<&'a str> {
    let sorgu = sorgu.to_lowercase();
    let mut sonuclar = Vec::new();

    for satir in icerik.lines() {
        if satir.to_lowercase().contains(&sorgu) {
            sonuclar.push(satir);
        }
    }

    sonuclar
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn buyuk_kucuk_harf_duyarli() {
        let sorgu = "güven";
        let icerik = "\
Güven:
güvenli, hızlı, üretken.
Üçünü de seç.
Güven kolay kazanılmaz.";

        assert_eq!(vec!["güvenli, hızlı, üretken."], ara(sorgu, icerik));
    }

    #[test]
    fn buyuk_kucuk_harf_duyarsiz() {
        let sorgu = "güven";
        let icerik = "\
Güven:
önce sağlamlık gelir.
Planlı çalış, sakin ol.
güven kazanılır.";

        assert_eq!(
            vec!["Güven:", "güven kazanılır."],
            buyuk_kucuk_harf_duyarsiz_ara(sorgu, icerik)
        );
    }
}
Listing 12-21: Sorguyu ve satırı karşılaştırmadan önce küçük harfe dönüştürecek buyuk_kucuk_harf_duyarsiz_ara fonksiyonunu tanımlamak

Önce, orijinal sorguyu gölgeleyerek, sorgu string’ini (dizgisini) küçük harfe dönüştürüyoruz ve aynı isimli yeni bir değişkende saklıyoruz. Sorgu üzerinde to_lowercase (küçük harfe dönüştür) çağırmak gereklidir, böylece kullanıcının sorgusu "güven", "GÜVEN", "Güven" veya "gÜvEn" olursa olsun sorguyu sanki "güven" imiş gibi ele alırız ve büyük/küçük harfe duyarsız oluruz. to_lowercase temel Unicode’u işleyebilecek olsa da yüzde 100 doğru olmayacaktır. Gerçek bir uygulama yazıyor olsaydık burada biraz daha fazla iş yapmak isterdik, ancak bu bölüm Unicode değil çevre değişkenleri (environment variables) ile ilgili olduğu için şimdilik bunu bu şekilde bırakacağız.

sorgu’nun artık bir string dilimi olmaktan ziyade (rather than) bir String olduğuna dikkat edin, çünkü to_lowercase çağırmak mevcut verilere referans vermek yerine yeni veriler oluşturur. Örnek olarak sorgunun "gÜvEn" olduğunu varsayalım: Bu string dilimi kullanabileceğimiz küçük harfli bir u veya e barındırmaz, bu nedenle "güven" içeren yeni bir String tahsis etmemiz gerekir. Şimdi contains metoduna argüman olarak sorgu’yu aktardığımızda, bir ampersand (ve işareti - &) eklememiz gerekir çünkü contains’in imzası bir string dilimi alacak şekilde tanımlanmıştır.

Sonra, tüm karakterleri küçük harfe dönüştürmek için her satir üzerinde bir to_lowercase çağrısı ekliyoruz. Artık satir ve sorguyu küçük harfe dönüştürdüğümüze göre sorgunun büyük/küçük harf durumu ne olursa olsun eşleşmeleri bulacağız.

Bakalım bu uygulama testleri geçecek mi:

$ cargo test
   Compiling minigrep v0.1.0 (file:///projects/minigrep)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 1.33s
     Running unittests src/lib.rs (target/debug/deps/minigrep-9cd200e5fac0fc94)

running 2 tests
test tests::buyuk_kucuk_harf_duyarsiz ... ok
test tests::buyuk_kucuk_harf_duyarli ... ok

test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

     Running unittests src/main.rs (target/debug/deps/minigrep-9cd200e5fac0fc94)

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests minigrep

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

Harika! Geçtiler. Şimdi calistir fonksiyonundan yeni buyuk_kucuk_harf_duyarsiz_ara fonksiyonunu çağıralım. İlk olarak Yapilandirma struct’ına, büyük/küçük harfe duyarlı ve duyarsız arama arasında geçiş yapmak için bir yapılandırma seçeneği ekleyeceğiz. Bu alanı eklemek derleyici hatalarına neden olacaktır çünkü henüz hiçbir yerde bu alanı ilklendirmiyoruz:

Dosya adı: src/main.rs

use std::env;
use std::error::Error;
use std::fs;
use std::process;

use minigrep::{ara, buyuk_kucuk_harf_duyarsiz_ara};

// --snip--


fn main() {
    let argumanlar: Vec<String> = env::args().collect();

    let yapilandirma =
        Yapilandirma::olustur(&argumanlar).unwrap_or_else(|hata| {
            println!("Argümanları ayrıştırırken problem oluştu: {hata}");
            process::exit(1);
        });

    if let Err(e) = calistir(yapilandirma) {
        println!("Uygulama hatası: {e}");
        process::exit(1);
    }
}

pub struct Yapilandirma {
    pub sorgu: String,
    pub dosya_yolu: String,
    pub buyuk_kucuk_harf_yoksay: bool,
}

impl Yapilandirma {
    fn olustur(argumanlar: &[String]) -> Result<Yapilandirma, &'static str> {
        if argumanlar.len() < 3 {
            return Err("yeterli argüman yok");
        }

        let sorgu = argumanlar[1].clone();
        let dosya_yolu = argumanlar[2].clone();

        Ok(Yapilandirma { sorgu, dosya_yolu })
    }
}

fn calistir(yapilandirma: Yapilandirma) -> Result<(), Box<dyn Error>> {
    let icerik = fs::read_to_string(yapilandirma.dosya_yolu)?;

    let sonuclar = if yapilandirma.buyuk_kucuk_harf_yoksay {
        buyuk_kucuk_harf_duyarsiz_ara(&yapilandirma.sorgu, &icerik)
    } else {
        ara(&yapilandirma.sorgu, &icerik)
    };

    for satir in sonuclar {
        println!("{satir}");
    }

    Ok(())
}

Bir Boolean (mantıksal değer) barındıran buyuk_kucuk_harf_yoksay alanını ekledik. Ardından Liste 12-22’de gösterildiği gibi calistir fonksiyonunun buyuk_kucuk_harf_yoksay alanının değerini kontrol etmesine ve bunu ara fonksiyonunu mu yoksa buyuk_kucuk_harf_duyarsiz_ara fonksiyonunu mu çağıracağına karar vermek için kullanmasına ihtiyacımız var. Bu henüz derlenmeyecektir.

Filename: src/main.rs
use std::env;
use std::error::Error;
use std::fs;
use std::process;

use minigrep::{ara, buyuk_kucuk_harf_duyarsiz_ara};

// --snip--


fn main() {
    let argumanlar: Vec<String> = env::args().collect();

    let yapilandirma =
        Yapilandirma::olustur(&argumanlar).unwrap_or_else(|hata| {
            println!("Argümanları ayrıştırırken problem oluştu: {hata}");
            process::exit(1);
        });

    if let Err(e) = calistir(yapilandirma) {
        println!("Uygulama hatası: {e}");
        process::exit(1);
    }
}

pub struct Yapilandirma {
    pub sorgu: String,
    pub dosya_yolu: String,
    pub buyuk_kucuk_harf_yoksay: bool,
}

impl Yapilandirma {
    fn olustur(argumanlar: &[String]) -> Result<Yapilandirma, &'static str> {
        if argumanlar.len() < 3 {
            return Err("yeterli argüman yok");
        }

        let sorgu = argumanlar[1].clone();
        let dosya_yolu = argumanlar[2].clone();

        Ok(Yapilandirma { sorgu, dosya_yolu })
    }
}

fn calistir(yapilandirma: Yapilandirma) -> Result<(), Box<dyn Error>> {
    let icerik = fs::read_to_string(yapilandirma.dosya_yolu)?;

    let sonuclar = if yapilandirma.buyuk_kucuk_harf_yoksay {
        buyuk_kucuk_harf_duyarsiz_ara(&yapilandirma.sorgu, &icerik)
    } else {
        ara(&yapilandirma.sorgu, &icerik)
    };

    for satir in sonuclar {
        println!("{satir}");
    }

    Ok(())
}
Listing 12-22: yapilandirma.buyuk_kucuk_harf_yoksay içerisindeki değere bağlı olarak ara ya da buyuk_kucuk_harf_duyarsiz_ara fonksiyonunu çağırmak

Son olarak çevre değişkenini kontrol etmemiz gerekiyor. Çevre değişkenleriyle çalışmak için gerekli fonksiyonlar, halihazırda src/main.rs dosyasının en üstünde kapsama dahil edilmiş olan (in scope) standart kütüphanedeki env modülündedir. Liste 12-23’te gösterildiği gibi IGNORE_CASE (BÜYÜK KÜÇÜK HARF YOKSAY) adlı bir çevre değişkeni için herhangi bir değer ayarlanıp ayarlanmadığını görmek üzere env modülündeki var fonksiyonunu kullanacağız.

Filename: src/main.rs
use std::env;
use std::error::Error;
use std::fs;
use std::process;

use minigrep::{ara, buyuk_kucuk_harf_duyarsiz_ara};

fn main() {
    let argumanlar: Vec<String> = env::args().collect();

    let yapilandirma =
        Yapilandirma::olustur(&argumanlar).unwrap_or_else(|hata| {
            println!("Argümanları ayrıştırırken problem oluştu: {hata}");
            process::exit(1);
        });

    if let Err(e) = calistir(yapilandirma) {
        println!("Uygulama hatası: {e}");
        process::exit(1);
    }
}

pub struct Yapilandirma {
    pub sorgu: String,
    pub dosya_yolu: String,
    pub buyuk_kucuk_harf_yoksay: bool,
}

impl Yapilandirma {
    fn olustur(argumanlar: &[String]) -> Result<Yapilandirma, &'static str> {
        if argumanlar.len() < 3 {
            return Err("yeterli argüman yok");
        }

        let sorgu = argumanlar[1].clone();
        let dosya_yolu = argumanlar[2].clone();

        let buyuk_kucuk_harf_yoksay = env::var("IGNORE_CASE").is_ok();

        Ok(Yapilandirma {
            sorgu,
            dosya_yolu,
            buyuk_kucuk_harf_yoksay,
        })
    }
}

fn calistir(yapilandirma: Yapilandirma) -> Result<(), Box<dyn Error>> {
    let icerik = fs::read_to_string(yapilandirma.dosya_yolu)?;

    let sonuclar = if yapilandirma.buyuk_kucuk_harf_yoksay {
        buyuk_kucuk_harf_duyarsiz_ara(&yapilandirma.sorgu, &icerik)
    } else {
        ara(&yapilandirma.sorgu, &icerik)
    };

    for satir in sonuclar {
        println!("{satir}");
    }

    Ok(())
}
Listing 12-23: IGNORE_CASE isimli bir çevre değişkeninde herhangi bir değer olup olmadığını kontrol etmek

Burada buyuk_kucuk_harf_yoksay adında yeni bir değişken oluşturuyoruz. Değerini ayarlamak için env::var fonksiyonunu çağırıyor ve ona IGNORE_CASE çevre değişkeninin adını aktarıyoruz. env::var fonksiyonu, çevre değişkeni herhangi bir değere ayarlanmışsa, çevre değişkeninin değerini içeren başarılı Ok varyantını (seçeneğini) döndürecek bir Result (Sonuç) döndürür. Eğer çevre değişkeni ayarlanmamışsa Err (Hata) varyantını döndürecektir.

Çevre değişkeninin ayarlanıp ayarlanmadığını kontrol etmek için Result üzerindeki is_ok metodunu kullanıyoruz, bu da programın büyük/küçük harf duyarsız arama yapması gerektiği anlamına gelir. Eğer IGNORE_CASE çevre değişkeni herhangi bir değere ayarlanmamışsa, is_ok false (yanlış) değerini döndürecektir ve program büyük/küçük harf duyarlı arama gerçekleştirecektir. Çevre değişkeninin değeri umurumuzda değil, sadece ayarlanmış veya ayarlanmamış olması önemli, bu nedenle unwrap, expect veya Result üzerinde gördüğümüz diğer metotları kullanmak yerine is_ok komutunu kontrol ediyoruz.

buyuk_kucuk_harf_yoksay değişkenindeki değeri Yapilandirma örneğine geçiriyoruz, böylece Liste 12-22’de uyguladığımız (implemented) gibi calistir fonksiyonu o değeri okuyabilir ve buyuk_kucuk_harf_duyarsiz_ara mı yoksa ara mı çağıracağına karar verebilir.

Haydi bir deneyelim! İlk olarak programımızı, çevre değişkeni ayarlanmadan ve tamamı küçük harflerden oluşan ne sorgusuyla çalıştıracağız. Büyük/küçük harfe duyarlı aramada yalnızca gerçekten küçük harfli ne geçen satırın dönmesini bekleriz:

$ cargo run -- ne siir.txt
   Compiling minigrep v0.1.0 (file:///projects/minigrep)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.0s
     Running `target/debug/minigrep ne siir.txt`
Gelme, artık neye yarar?

Görünüşe göre hala çalışıyor! Şimdi programı IGNORE_CASE (BÜYÜK KÜÇÜK HARF YOKSAY) 1’e ayarlanmış olarak fakat aynı ne sorgusuyla çalıştıralım:

$ IGNORE_CASE=1 cargo run -- ne siir.txt

PowerShell kullanıyorsanız, çevre değişkenini ayarlamanız ve programı ayrı komutlar olarak çalıştırmanız gerekir:

PS> $Env:IGNORE_CASE=1; cargo run -- ne siir.txt

Bu IGNORE_CASE’in shell (kabuk) oturumunuzun geri kalanı boyunca kalıcı olmasını sağlayacaktır. Remove-Item cmdlet’i ile kaldırılabilir:

PS> Remove-Item Env:IGNORE_CASE

Büyük/küçük harf duyarsız arama sayesinde Ne ile başlayan satırları da elde etmeliyiz:

Ne hasta bekler sabahı,
Ne taze ölüyü mezar.
Ne de şeytan, bir günahı,
Gelme, artık neye yarar?

Mükemmel, artık hem Ne ile başlayan satırları hem de küçük harfli ne geçen satırı aldık! Bizim minigrep programımız artık bir çevre değişkeni tarafından kontrol edilen büyük/küçük harf duyarsız arama yapabiliyor. Artık komut satırı argümanları (command line arguments) veya çevre değişkenleri kullanarak belirlenen seçenekleri nasıl yöneteceğinizi biliyorsunuz.

Bazı programlar aynı yapılandırma için hem argümanlara hem de çevre değişkenlerine izin verir. Böyle durumlarda, programlar birinin veya diğerinin öncelikli olduğuna karar verir. Kendi başınıza yapacağınız bir başka alıştırma olarak büyük/küçük harf duyarlılığını bir komut satırı argümanı veya bir çevre değişkeni aracılığıyla kontrol etmeyi deneyin. Programın biri büyük/küçük harf duyarlı, diğeri büyük/küçük harf yoksay olarak ayarlanmış şekilde çalıştırılması durumunda komut satırı argümanının mı yoksa çevre değişkeninin mi öncelikli olması gerektiğine karar verin.

std::env modülü çevre değişkenleriyle ilgilenmek için çok daha yararlı özellikler barındırır: Nelerin mevcut olduğunu görmek için belgesine göz atın.