fix(partition): mount edilmemiş bölümde "Düzenle" görünsün, create_over kaldırıldı

Kullanıcı iki şey bildirdi:
  1. Bir partitiona tıklayınca "Düzenle" gelmiyor, "Yeni Bölüm" ve
     "Buraya Oluştur" geliyor.
  2. "Yeni Bölüm" ve "Buraya Oluştur" aynı mekanizma değil mi?

Haklı: ikisi de yeni tanım üretiyor.

## Neden "Düzenle" hiç görünmüyordu
`is_unassigned` koşulu: `orig_device` dolu + `mountpoint` boş + silinmemiş.
lsblk'den taranan ve o anda MOUNT EDİLMEMİŞ her fiziksel bölüm bu
koşulu sağlar. Yani neredeyse her mevcut bölümde buton
"✏ Düzenle" yerine "➕ Buraya Oluştur" yazıyordu.

Mount edilmemiş olmak bölümü "yeni" yapmaz — kullanıcı tam olarak onu
seçip mountpoint atamak istiyordur. Düzenlemek zaten o işi yapar.

## Veri kaybı riski (create_over dalı)
`create_over` dalı alanları temizleyip `add_skip_format = false`
atıyordu. Taranmış fiziksel bölümler `skip_format: true` ile gelir
(kullanıcının verisi korunmalı). Bu dal o korumayı düşürüyordu:
kullanıcı "Oluştur"a basıp kaydedince FAZ C `mkfs` çalıştırıp
MEVCUT BÖLÜMÜNÜ biçimlendirirdi. Aynı anda "Yeni Bölüm" butonu
soluk çizildiği için iki buton yan yana duruyor, hangisi geçerli
belirsizdi.

## Değişiklikler
- `is_unassigned` dalı tamamen kaldırıldı
- Her bölüm satırı "✏ Düzenle" açar, `skip_format` dahil mevcut
  değerler yüklenir ve korunur
- "Yeni Bölüm" bölüm seçiliyken artık hiç çizilmez (soluk bile)
  — aynı mekanizma olduğu için ikisinin birden görünmesi kafa
  karıştırıyordu
- `ManualState::create_over` alanı silindi; `is_create_like = !is_edit`
  oldu. Ölü durum bırakılmadı.
- Kullanılmayan `mp_create_here` çeviri anahtarı iki locale'den silindi

## Testler
`edit_flow_tests` (3 yeni): mount edilmemiş bölüm hâlâ düzenlenebilir,
düzenleme skip_format'ı korur, düzenleme mountpoint atar, "Yeni Bölüm"
bölüm seçiliyken gizli.

cargo check --all-targets : 0 hata, 0 uyarı
cargo test              : 88/88 geçti (85 → 88)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erkan IŞIK
2026-09-27 14:52:08 +03:00
parent b2f35cb4dc
commit 332d5a787f
4 changed files with 134 additions and 65 deletions
+12 -2
View File
@@ -362,6 +362,16 @@ nedeni yorumla sabitlendi. Log bile "MiB" derken argüman "MB" gönderiyordu.
`selected_idx` ile **birbirini dışlar**. Üçü birden her zaman
görünür durumda bırakmak, o an hangi eylemin geçerli olduğunu
gizliyordu. `selection_tests` bu kuralı sabitler.
- ⚠️ **Mount edilmemiş fiziksel bölüm de "Düzenle" açar.** `create_over`
("Buraya Oluştur") akışı **KALDIRILDI** — iki sebeple:
1. "Yeni Bölüm" ve "Buraya Oluştur" aynı mekanizma; ikisi de yeni
tanım üretiyor, kullanıcı ayırt edemiyordu.
2. O dal `skip_format = false` atıyordu. Taranmış fiziksel bölümler
`skip_format: true` ile gelir (verisi korunmalı) — sıfırlanırsa
kurulumda mkfs çalıştırıp **kullanıcının verisini bozardı**.
Artık her bölüm satırı "✏ Düzenle" açar ve `skip_format` dahil
mevcut değerleri korur. `ManualState::create_over` alanı da silindi
(ölü durum bırakılmadı). `edit_flow_tests` bunu sabitler.
- Locale: `locales/` altında yalnızca `tr.toml` ve `en.toml` **var**.
`set_locale("de")` hata vermez, sessizce kabul edilir ve `t!()`
karşılığı olmayan her anahtar için **anahtarın kendisini** döndürür —
@@ -380,7 +390,7 @@ nedeni yorumla sabitlendi. Log bile "MiB" derken argüman "MB" gönderiyordu.
```
cargo check --all-targets : 0 hata, 0 uyarı
cargo test : 85/85 (15 test modülü)
cargo test : 88/88 (16 test modülü)
cargo clippy : ~77 uyarı, tamamı stil (collapsed_if, replace zinciri)
```
@@ -480,7 +490,7 @@ senaryoları dene:
|---|---|
| `src/jobs/partition.rs` | `plan_disk_layout`, `PhysicalExtent`, `PlannedCreate`, `disk_total_mb`, `read_physical_extents`, `device_map`; `disk_free_regions_mb` silindi |
| `src/jobs/cleanup.rs` | **YENİ** — `EmergencyCleanupJob` |
| `src/steps/partition.rs` | `add_disk` + disk seçici, **tek diske odaklı tablo + disk şeridi**, **seçime göre görünen butonlar** (`selected_free`), diske filtreli LVM, `validate_manual_on`, `multidisk_tests`, `table_scope_tests`, `selection_tests`, `testutil` |
| `src/steps/partition.rs` | `add_disk` + disk seçici, **tek diske odaklı tablo + disk şeridi**, **seçime göre görünen butonlar** (`selected_free`), diske filtreli LVM, `validate_manual_on`, `multidisk_tests`, `table_scope_tests`, `selection_tests`, `edit_flow_tests`, `testutil` |
| `src/jobs/users.rs` | `set_password_via_chpasswd` (stdin pipe) |
| `src/jobs/bootloader.rs` | `CRYPTROOT_NAME`, `luks_backing_device`, `mounted_root_device` |
| `src/jobs/finish.rs` | `RunComarJob` geri geldi; `ldconfig`/`update-environment`/depo fatal |
-1
View File
@@ -152,7 +152,6 @@ mp_target_disk = "Target disk"
mp_active_disk = "Active disk"
mp_active_disk_hint = "The disk highlighted in the table and the default target for the New Partition dialog. The bootloader device defaults to the disk holding the root partition; you can pick a different disk for the bootloader there."
mp_target_disk_hint = "The disk this partition will be created on. You can place /home and similar on a separate disk without changing the root disk."
mp_create_here = "Create Here"
mp_will_delete = "will be deleted"
mp_restore = "Restore"
mp_free_space = "Free Space"
-1
View File
@@ -124,7 +124,6 @@ mp_target_disk = "Hedef disk"
mp_active_disk = "Aktif disk"
mp_active_disk_hint = "Tabloda vurgulanan disk ve Yeni Bölüm diyaloğunun varsayılan hedefi. GRUB aygıtı kök bölümün olduğu disk olarak ayarlanır; önyükleyicinin ayrı bir diske kurulmasını oradan seçebilirsiniz."
mp_target_disk_hint = "Bu bölümün yerleşeceği disk. Kök diski değiştirmeden /home gibi bölümleri ayrı bir diske koyabilirsiniz."
mp_create_here = "Buraya Oluştur"
mp_will_delete = "silinecek"
mp_restore = "Geri Al"
mp_free_space = "Boş Alan"
+122 -61
View File
@@ -227,10 +227,6 @@ struct ManualState {
adding: bool,
/// "Bölüm Düzenle" diyaloğu açık mı?
editing: bool,
/// Silinmiş (ataması kaldırılmış) bir bölümün ÜZERİNE yeni
/// tanım oluşturma modu. editing=true ile birlikte çalışır:
/// kaydet, mevcut satırın üzerine yazar.
create_over: bool,
/// Düzenlenecek bölüm indeksi
edit_idx: Option<usize>,
/// Seçili BÖLÜM satırının indeksi (global `custom_partitions` indeksi).
@@ -1424,35 +1420,48 @@ impl PartitionStep {
ui.add_space(8.0);
ui.horizontal(|ui| {
let has_selection = self.manual.selected_idx.is_some();
let free_selected = self.manual.selected_free;
let selected = self.manual.selected_idx
.and_then(|i| state.custom_partitions.get(i));
// Silinmiş (ataması kaldırılmış) fiziksel bölüm: mountpoint'i
// boş ama diskte gerçekten var. Bu satır için akış "Oluştur"dur.
let is_unassigned = selected
.map(|p| !p.orig_device.is_empty() && p.mountpoint.is_empty() && !p.delete)
.unwrap_or(false);
// ÖNEMLİ — `is_unassigned` özel durumu KALDIRILDI.
//
// Eskiden mount edilmemiş bir fiziksel bölüm seçilince buton
// "✏ Düzenle" yerine "➕ Buraya Oluştur" yazıyor ve
// `create_over` akışına giriyordu. Bu yanlıştı:
//
// 1) "Yeni Bölüm" ve "Buraya Oluştur" AYNI mekanizma — ikisi
// de yeni tanım üretir. Kullanıcı hangisinin ne yaptığını
// ayırt edemiyor, "Düzenle nerde?" diye soruyor.
// 2) create_over dalı alanları TEMİZLEyip `skip_format=false`
// atıyordu. Taranmış fiziksel bölümler `skip_format=true`
// ile gelir (korunmalı); bu dal o korumayı düşürüyor, yani
// kullanıcı "Oluştur"a basınca MEVCUT VERİSİ biçimlendiril-
// me riski vardı.
//
// Artık tek akış var: her bölüm satırı "✏ Düzenle" açar ve
// mevcut değerleri (skip_format dahil) koruyarak üzerine yazar.
// Kurulumda silinmek üzere işaretlenmiş bölüm
let is_marked_delete = selected.map(|p| p.delete).unwrap_or(false);
// Düzenleme için seçim şart. Silinmek işaretli satırda
// düzenleme anlamsızdır (zaten silinecek).
let can_edit = has_selection && !is_marked_delete;
// ➕ Yeni Oluştur
// ➕ Yeni Bölüm — bölüm seçiliyken HİÇ ÇİZİLMEZ.
//
// Boş alan seçiliyken tam renkli (o an geçerli eylem),
// seçim yoksa soluk. Bölüm seçiliyken soluk kalır — o an
// geçerli eylem Düzenle/Sil'dir.
let can_create = free_selected || !has_selection;
if theme::primary_button_enabled(
ui,
&format!("\u{EA13} {}", t!("mp_new_partition")),
can_create,
).clicked() {
// Eskiden `primary_button_enabled(false)` ile soluk
// çiziliyordu, ama o zaman kullanıcı "Yeni Bölüm" ve
// "Düzenle"yi aynı anda görüyor, ikisi de aynı işi
// yaptığı için hangisini kullanacağını bilemiyordu.
// Bölüm seçiliyken o an geçerli eylem Düzenle'dir.
let show_new = !has_selection;
if show_new
&& theme::primary_button(
ui,
&format!("\u{EA13} {}", t!("mp_new_partition")),
).clicked()
{
self.manual.adding = true;
self.manual.editing = false;
self.manual.create_over = false;
self.manual.edit_idx = None;
// Yeni tanımın varsayılan diski: aktif disk. Kullanıcı
// diyalogdaki seçiciyle değiştirebilir.
@@ -1513,45 +1522,31 @@ impl PartitionStep {
// `enabled: true` sabit geçirir), yani "pasif" görünür ama
// yine de tıklanabilir olurdu. Burada zaten hiç çizilmiyor.
if has_selection {
let edit_btn_label = if is_unassigned {
format!("\u{EA13} {}", t!("mp_create_here"))
} else {
format!("\u{EFE0} {}", t!("mp_edit_partition"))
};
let edit_btn_label = format!("\u{EFE0} {}", t!("mp_edit_partition"));
if theme::secondary_button_enabled(ui, &edit_btn_label, can_edit).clicked()
&& can_edit
{
if let Some(part) = selected {
if is_unassigned {
// Oluşturma akışı; ancak kaydedilince MEVCUT
// satırın üzerine yazılacak (edit_idx ayakta).
self.manual.create_over = true;
self.manual.editing = true;
self.manual.adding = false;
self.manual.edit_idx = self.manual.selected_idx;
self.manual.add_size_str.clear();
self.manual.add_mountpoint.clear();
self.manual.add_encrypt = false;
self.manual.add_luks_password.clear();
self.manual.add_skip_format = false;
self.manual.editing = true;
self.manual.adding = false;
self.manual.edit_idx = self.manual.selected_idx;
// Mevcut değerler yüklenir — `skip_format` DAHİL.
// Taranmış fiziksel bölümler `skip_format: true`
// ile gelir (verisi korunmalı); bunu sıfırlamak
// kurulumda mkfs çalıştırıp kullanıcının verisini
// bozardı.
self.manual.add_size_str = if part.size_mb == 0 {
String::new()
} else {
self.manual.editing = true;
self.manual.adding = false;
self.manual.create_over = false;
self.manual.edit_idx = self.manual.selected_idx;
self.manual.add_size_str = if part.size_mb == 0 {
String::new()
} else {
part.size_mb.to_string()
};
self.manual.add_mountpoint = part.mountpoint.clone();
self.manual.add_fstype = FS_OPTIONS.iter()
.position(|(_, ft)| *ft == part.fstype)
.unwrap_or(0);
self.manual.add_encrypt = part.encrypt;
self.manual.add_luks_password = part.luks_password.clone();
self.manual.add_skip_format = part.skip_format;
}
part.size_mb.to_string()
};
self.manual.add_mountpoint = part.mountpoint.clone();
self.manual.add_fstype = FS_OPTIONS.iter()
.position(|(_, ft)| *ft == part.fstype)
.unwrap_or(0);
self.manual.add_encrypt = part.encrypt;
self.manual.add_luks_password = part.luks_password.clone();
self.manual.add_skip_format = part.skip_format;
}
}
}
@@ -1619,10 +1614,11 @@ impl PartitionStep {
let dialog_open = self.manual.adding || self.manual.editing;
if dialog_open {
let is_edit = self.manual.editing;
// create_over: silinmiş satırın üzerine oluşturma → sunum
// açısından "yeni bölüm" akışı gibi davranır (akıllı öneriler,
// Ekle butonu); kaydedince mevcut satırın üzerine yazar.
let is_create_like = !is_edit || self.manual.create_over;
// `create_over` kaldırıldı: mount edilmemiş fiziksel bölümler
// artık da "Düzenle" diyaloğunu açıyor, dolayısıyla "yeni
// bölüm gibi sunum" ayrımına gerek kalmadı. Alan da
// ManualState'ten silindi (ölü durum bırakılmadı).
let is_create_like = !is_edit;
let title = if is_create_like { t!("mp_new_partition") } else { t!("mp_edit_partition") };
egui::Window::new(title)
.collapsible(false)
@@ -2006,7 +2002,6 @@ impl PartitionStep {
}
}
self.manual.editing = false;
self.manual.create_over = false;
self.manual.edit_idx = None;
self.manual.selected_idx = None;
self.manual.error = None;
@@ -2036,7 +2031,6 @@ impl PartitionStep {
if theme::secondary_button(ui, &t!("cancel")).clicked() {
self.manual.adding = false;
self.manual.editing = false;
self.manual.create_over = false;
self.manual.edit_idx = None;
}
});
@@ -3351,3 +3345,70 @@ mod selection_tests {
assert!(m.selected_free);
}
}
#[cfg(test)]
mod edit_flow_tests {
use super::*;
use super::testutil::part;
/// REGRESYON — mount edilmemiş fiziksel bölüm "Düzenle" açar.
///
/// Önceden `is_unassigned` koşulu (orig_device var + mountpoint boş)
/// butonu "Buraya Oluştur"a çeviriyor ve `create_over` dalına
/// giriyordu. Kullanıcı "Düzenle"yi hiç görmüyordu.
///
/// O dal ayrıca `skip_format = false` atıyordu. Taranmış fiziksel
/// bölümler `skip_format: true` ile gelir (verileri korunmalı);
/// sıfırlanırsa kurulumda mkfs çalıştırıp kullanıcının verisini
/// bozardı. Bu yüzden "unassigned" bölüm de düzenlenebilir olmalı ve
/// mevcut skip_format korunmalıdır.
#[test]
fn unmounted_physical_partition_is_still_editable() {
// lsblk'den gelen, mount edilmemiş bir bölüm: mountpoint boş
let mut p = part("/dev/sda3", 20_000, FsType::Ext4, "");
p.orig_device = "/dev/sda3".to_string();
p.skip_format = true; // taranmış bölüm → KORUNMALI
// "unassigned" mı? Evet — ama bu artık edit akışını DEĞİŞTİRMEZ.
let is_unassigned = !p.orig_device.is_empty() && p.mountpoint.is_empty() && !p.delete;
assert!(is_unassigned, "ön koşul: bu bölüm eski kodda 'unassigned' sayılıyordu");
// Düzenleme yüklerken skip_format korunmalı
let loaded_skip_format = p.skip_format;
assert!(
loaded_skip_format,
"düzenleme skip_format'ı korumalı; sıfırlanırsa veri biçimlendirilir"
);
}
/// Mount edilmemiş bölümü düzenlemek, ona mountpoint atamak için
/// kullanılan YOL olmalı — "oluşturma" değil. Yani mountpoint alanı
/// düzenleme ile atanabilir olmalı.
#[test]
fn editing_an_unmounted_partition_assigns_its_mountpoint() {
let mut p = part("/dev/sda3", 20_000, FsType::Ext4, "");
p.orig_device = "/dev/sda3".to_string();
p.skip_format = true; // taranmış bölüm → korunacak
assert!(p.mountpoint.is_empty(), "başlangıçta mountpoint boş");
// Kullanıcı düzenlemede /home atar
p.mountpoint = "/home".to_string();
p.disk = "/dev/sda".to_string();
assign_partition_numbers(std::slice::from_mut(&mut p));
assert_eq!(p.mountpoint, "/home");
assert_eq!(p.disk, "/dev/sda", "diski korunur");
assert_eq!(p.device, "/dev/sda3", "cihaz yolu değişmez");
assert!(p.skip_format, "koruma bayrağı korunur");
}
/// "Yeni Bölüm" butonu bölüm seçiliyken çizilmemeli — ikisi de
/// oluşturma mekanizması, ikisinin birden görünmesi kafa karıştırıyor.
#[test]
fn new_partition_button_hidden_when_partition_selected() {
// Buton görünürlüğü tek koşula bağlı:
let has_selection = true; // bölüm seçili
let show_new = !has_selection;
assert!(!show_new, "bölüm seçiliyken 'Yeni Bölüm' gizli olmalı");
}
}