refactor(partition): hedef disk seçicisini tamamen kaldır, add_disk alanını sil

Kullanıcı isteği: "yeni bölümde de hedef disk kısmı kalksın, zaten
seçilen diskte işlem yapılacak."

Önceki commit seçiciyi yalnızca düzenlemeden kaldırmıştı. Şimdi her iki
akıştan da kaldırıldı — arayüz zaten "tek disk üzerinde çalışıyor":
tablo yalnızca seçili diski gösteriyor, disk şeridi diski
değiştiriyor. Diyalogda ikinci bir "aktif disk" kaynağı yalnızca
belirsizlik yaratıyordu.

## Davranış
  ekleme   → state.selected_disk
  düzenleme→ bölümün kendi disk/device alanları (korunur)

Başka diske bölüm eklemenin yolu: disk şeridinden o diske geç, sonra
"Yeni Bölüm". Çoklu disk kurulumu böylece hâlâ mümkün, hatta daha
tutarlı: eklediğin bölümü tabloda gördüğün diske eklersin.

## ManualState::add_disk SİLİNDİ
Alan artık gereksizdi ve tam olarak geçmişte bir hatanın kaynağıydı:

  "Yeni Bölüm" (add_disk = sda) → iptal → sdb'deki bölümü "Düzenle"
  → kaydet → bölüm /dev/sda'ya taşınıyordu

Diyalogda ikinci bir disk kaynağı kalmayınca bu sınıf hata yapısal
olarak imkânsız hale geldi. Alan, atamaları ve iki `dialog_disk`
fallback dalı kaldırıldı.

## Temizlik
- `mp_target_disk`, `mp_target_disk_hint` çeviri anahtarları iki
  locale'den silindi (kodda kullanılmıyordu)
- `mp_active_disk_hint` metni yeni akışı anlatacak şekilde güncellendi
- `edit_disk_tests` içindeki `add_disk`'e dayalı iki test yeni
  davranışa göre yeniden yazıldı

cargo check --all-targets : 0 hata, 0 uyarı
cargo test              : 92/92 geçti

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erkan IŞIK
2026-09-27 15:40:38 +03:00
parent d87664d191
commit 3104adbc41
4 changed files with 99 additions and 179 deletions
+16 -15
View File
@@ -210,20 +210,21 @@ sda1 ile sdb1 çakışmaz.
"sda'da 3 bölüm var" bilgisi kaybolmaz, dikkat tek diskte kalır.
Disk değişince `manual.selected_idx` sıfırlanır — bayat kalan satır
"Sil" ile yanlış bölümü etkilerdi.
- **Ekleme diyaloğunda disk seçici var** (`ManualState::add_disk`).
Çoklu disk kurulumunun temel yolu budur.
Önceden yeni bölümün diski `state.selected_disk`'e sabitlenmişti ve
diyalogda disk kontrolü yoktu — `/home`'u sdb'ye koymak için
kurulumun GRUB hedefini değiştirmek gerekiyordu.
- **Hedef disk seçicisi yalnızca "Yeni Bölüm" diyaloğunda** görünür
(27 Eyl, kullanıcı isteği). Düzenleme modunda seçici yok: zaten
seçili bir bölüm düzenleniyor, diski bellidir.
Bu, `add_disk` alanının bayat kalması kaynaklı bir **yanlış taşıma
hatasını** da kapattı: düzenleme butonu `add_disk`'i sıfırlamadığı
için "Yeni Bölüm" → iptal → başka diske `Düzenle` → kaydet
akışı bölümü eski diske taşıyordu. Artık düzenleme butonu
`add_disk = part.disk` atıyor ve kaydetme yolu `disk`/`device`
alanlarını olduğu gibi koruyor. `edit_disk_tests` bunu sabitler.
- **Diyalogda HEDEF DİSK SEÇİCİSİ YOK** (27 Eyl, kullanıcı isteği) —
ne eklemede ne düzenlemede. Bölümleme arayüzü tek disk üzerinde
çalışıyor:
- ekleme → `state.selected_disk`
- düzenleme→ bölümün kendi `disk`/`device` alanları (korunur)
Başka bir diske bölüm eklemenin yolu: **disk şeridinden o diske
geç**, sonra "Yeni Bölüm". İki ayrı "aktif disk" kaynağı olması
hangisinin geçerli olduğunu belirsizleştiriyordu.
Bu karar `ManualState::add_disk` alanını da tamamen gereksiz kıldı ve
alan **silindi**. Alan varken şu akış bölümü yanlış diske taşıyordu:
"Yeni Bölüm" (`add_disk = sda`) → iptal → sdb'deki bölümü `Düzenle`
→ kaydet. Artık "hangi disk" sorusunun **tek cevabı** var ve bayat
değer sınıfı hata kalmıyor. `edit_disk_tests` bunu sabitler.
- Tablo `TableRow::Part | TableRow::FreeSpace` enum'u ile kurulur.
- Seçili disk hiç tanım içermiyorsa boş-alan satırı basılır
(`first_idx: None` → **seçilemez**; aksi hâlde `selected_idx = 0` ile
@@ -497,7 +498,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`, `edit_flow_tests`, `edit_disk_tests`, `testutil` |
| `src/steps/partition.rs` | **tek diske odaklı tablo + disk şeridi** (diyalogda disk seçicisi yok), **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`, `edit_disk_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 -3
View File
@@ -148,10 +148,8 @@ display_manager_de_not_found = "(not detected)"
display_manager_note = "Note: The display manager will be enabled by default on the installed system."
mp_size_remaining = "Remaining Space"
mp_size_available = "Available"
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_active_disk_hint = "The disk shown in the table and used by New Partition. To add a partition to another disk, switch to that disk in the disk strip above first. The bootloader device defaults to the disk holding the root partition."
mp_will_delete = "will be deleted"
mp_restore = "Restore"
mp_free_space = "Free Space"
+1 -3
View File
@@ -120,10 +120,8 @@ summary_partition_plan = "Bölümleme"
summary_custom_partitions = "Manuel Bölümler"
mp_size_remaining = "Kalan Alan"
mp_size_available = "Kullanılabilir"
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_active_disk_hint = "Tabloda ve Yeni Bölüm işleminde kullanılan disk. Başka bir diske bölüm eklemek için önce üstteki disk şeridinden o diske geçin. GRUB aygıtı kök bölümün olduğu disk olarak ayarlanır."
mp_will_delete = "silinecek"
mp_restore = "Geri Al"
mp_free_space = "Boş Alan"
+81 -158
View File
@@ -248,16 +248,6 @@ struct ManualState {
add_size_str: String,
/// Diyalog: bağlama noktası
add_mountpoint: String,
/// Diyalog: bölümün HEDEF DİSKİ (ör. "/dev/sdb").
///
/// Çoklu disk kurulumunda kritik: / kök sda'da iken /home'u sdb'ye
/// koyabilmek bu alanla mümkün. Daha önce yeni bölümün diski
/// `state.selected_disk`'e sabitleniyordu ve diyalogda hiçbir disk
/// kontrolü yoktu — kullanıcı sdb'ye bölüm koymak istiyorsa
/// kurulumun GRUB hedefini de değiştirmek zorundaydı.
///
/// Boşsa `state.selected_disk` kullanılır (geriye uyum).
add_disk: String,
/// Diyalog: LUKS şifrele
add_encrypt: bool,
/// Diyalog: LUKS parola
@@ -1463,10 +1453,10 @@ impl PartitionStep {
self.manual.adding = true;
self.manual.editing = false;
self.manual.edit_idx = None;
// Yeni tanımın varsayılan diski: aktif disk. Kullanıcı
// diyalogdaki seçiciyle değiştirebilir.
// Yeni tanım SEÇİLİ diske eklenir. Diyalogda ayrı bir
// hedef disk seçicisi yok; hedefi değiştirmek için
// disk şeridinden önce o diske geçilir.
let active_disk = state.selected_disk.clone().unwrap_or_default();
self.manual.add_disk = active_disk.clone();
// Öneriler bu diskin DURUMUNA göre yapılır:
// - UEFI'de ve henüz EFI yoksa → /boot/efi
// - kök yoksa → /
@@ -1530,16 +1520,10 @@ impl PartitionStep {
self.manual.editing = true;
self.manual.adding = false;
self.manual.edit_idx = self.manual.selected_idx;
// Hedef disk, düzenlenen bölümün KENDİ diskidir.
// Hedef disk diyalogda seçilmiyor; kaydetme
// yolunda bölümün kendi `disk`/`device` alanları
// korunuyor.
//
// Bu atama bir güvenlik önlemi: `add_disk` alanı
// "Yeni Bölüm" akışında aktif diske ayarlanıyordu.
// Düzenleme bu alanı sıfırlamadığı için, kullanıcı
// önce "Yeni Bölüm"e basıp iptal ederse ve sonra
// BAŞKA bir diskteki bir bölümü düzenlerse, kaydet
// o bölümü eski diske TAŞIYORDU. Artık kaynak
// daima doğru disk.
self.manual.add_disk = part.disk.clone();
// 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
@@ -1687,113 +1671,39 @@ impl PartitionStep {
}
}
});
// ── HEDEF DİSK SEÇİCİ ─────────────────────────────
// ── HEDEF DİSK SEÇİCİ: KALDIRILDI ───────────────
//
// ÇOKLU DİSK KURULUMU İÇİN KRİTİK, ama YALNIZCA
// "Yeni Bölüm" akışında.
// Bölümleme arayüzü TEK DISK üzerinde çalışıyor:
// tablo yalnızca seçili diski gösteriyor, disk şeridi
// diski değiştiriyor. Diyalogda ayrı bir disk seçimi
// iki farklı "aktif disk" kaynağı yaratıyordu —
// hangisinin geçerli olduğu belirsizleşiyordu.
//
// Önceden yeni bölümün diski `state.selected_disk`'e
// SABİTLENİYORDU ve diyalogda hiçbir disk kontrolü
// yoktu. /home'u /dev/sdb'ye koymak isteyen kullanıcı
// yalnızca dışarıdaki ComboBox'u sdb'ye çevirip
// "Yeni Bölüm"'e basabildi — ki bu, aynı zamanda
// GRUB'un nereye kurulacağını da değiştiriyordu.
// Hedefi değiştirmenin yolu: üstteki disk şeridinden
// veya disk seçiciden o diske geç, sonra "Yeni Bölüm".
// `state.selected_disk` zaten o diski gösteriyor.
//
// DÜZENLEME modunda seçici GÖSTERİLMEZ (kullanıcı
// isteği): zaten seçili bir bölüm düzenleniyor, diski
// bellidir. Seçici kalsaydı bölümü başka diske
// "taşıma" yanıltıcı bir seçenek sunardı — üstelik
// veri hiç taşınmıyor, yalnızca tanımın hedefi
// değişiyordu. Bkz. aşağıdaki `add_disk` ataması.
if !is_edit {
ui.label(t!("mp_target_disk"));
ui.label(
egui::RichText::new(t!("mp_target_disk_hint"))
.size(11.0)
.color(crate::ui::theme::c_text_dim()),
);
}
let disks_for_dialog: Vec<String> = state
.available_disks
.iter()
.map(|d| d.name.clone())
.collect();
// Disk listesi boşsa (henüz taranmadıysa) yalnızca
// aktif diski göster; seçim kırılmasın.
let dialog_options: Vec<String> = if disks_for_dialog.is_empty() {
vec![state.selected_disk.clone().unwrap_or_default()]
// Bu, `ManualState::add_disk` alanını da tamamen
// gereksizleştirdi: artık tek doğru kaynak var ve
// "bayat değer" sınıfı hata kalmıyor.
let dialog_disk = if is_edit {
// Düzenlenen bölümün kendi diski
self.manual.edit_idx
.and_then(|i| state.custom_partitions.get(i))
.map(|p| p.disk.clone())
.unwrap_or_default()
} else {
disks_for_dialog.clone()
// Ekleme: seçili disk
state.selected_disk.clone().unwrap_or_default()
};
if !is_edit && !dialog_options.is_empty() {
let current = if self.manual.add_disk.is_empty() {
let fallback = state.selected_disk.clone().unwrap_or_default();
self.manual.add_disk = fallback.clone();
fallback
} else {
self.manual.add_disk.clone()
};
theme::show_combo_box(
ui,
"mp_target_disk_combo",
&current,
ui.available_width() * 0.6,
|ui, w| {
for name in &dialog_options {
let disk = state.available_disks.iter().find(|d| d.name == *name);
let label = match disk {
Some(d) => format!(
"{} — {} ({:.1} GB)",
d.name,
if d.model.is_empty() { "?" } else { d.model.as_str() },
d.size_bytes as f64 / 1_073_741_824.0
),
None => name.clone(),
};
if theme::selectable_value(
ui,
&mut self.manual.add_disk,
name.clone(),
&label,
w,
)
.changed()
{
// Disk değişince bu diske ait
// olmayan bir mountpoint önerisi
// (ör. başka diskin swap'ı) yanlış
// olabilir; öneriyi sıfırla.
self.manual.add_mountpoint.clear();
self.manual.add_size_str.clear();
}
}
},
);
}
ui.add_space(6.0);
ui.separator();
// Slider — kullanılabilir alan kadar
// Hedef disk: eklemede yukarıda seçilen disk,
// düzenlemede bölümün kendi diski (`add_disk` düzenleme
// butonunda zaten ona ayarlanıyor). Boyut hesapları
// YALNIZCA bu disk üzerinden yapılır.
let dialog_disk = if self.manual.add_disk.is_empty() {
if is_edit {
self.manual.edit_idx
.and_then(|i| state.custom_partitions.get(i))
.map(|p| p.disk.clone())
.unwrap_or_default()
} else {
state.selected_disk.clone().unwrap_or_default()
}
} else {
self.manual.add_disk.clone()
};
// Slider — kullanılabilir alan kadar.
// `dialog_disk` yukarıda hesaplandı (eklemede seçili
// disk, düzenlemede bölümün kendi diski). Boyut
// hesapları YALNIZCA bu disk üzerinden yapılır.
let disk_total_mb = disk_size_mb(state, &dialog_disk);
let used_mb: u64 = used_mb_on_disk(&state.custom_partitions, &dialog_disk, self.manual.edit_idx);
let avail_mb = disk_total_mb.saturating_sub(used_mb).max(1);
@@ -1943,12 +1853,18 @@ impl PartitionStep {
ui.horizontal(|ui| {
let ok_label = if is_create_like { t!("mp_add_btn") } else { t!("mp_save") };
if theme::primary_button(ui, &ok_label).clicked() {
// Hedef disk: diyalogdaki disk seçiciden gelir.
// Boşsa geriye uyum için aktif diske düşülür.
let target_disk = if !self.manual.add_disk.is_empty() {
self.manual.add_disk.clone()
// Hedef disk tek kaynaktan gelir:
// ekleme → seçili disk (`state.selected_disk`)
// düzenleme→ düzenlenen bölümün kendi diski
// Diyalogda ayrı bir seçici olmadığı için
// "hangi disk" sorusunun ikinci bir cevabı yok.
let target_disk = if is_edit {
state.custom_partitions
.get(self.manual.edit_idx.unwrap_or(usize::MAX))
.map(|p| p.disk.clone())
.unwrap_or_else(|| disk.clone())
} else {
disk.clone()
state.selected_disk.clone().unwrap_or_else(|| disk.clone())
};
let size_str = self.manual.add_size_str.trim();
let size_mb: u64 = if size_str == "remaining" {
@@ -1980,13 +1896,9 @@ impl PartitionStep {
let skip_format = self.manual.add_skip_format;
// Disk ve cihaz yolu KORUNUR.
//
// Düzenleme artık disk değiştirmediği
// için (diyalogda seçici yok) burada
// hedef daima bölümün kendi diskidir.
// Önceden "başka diske taşıma" vardı;
// o yol `add_disk`'in bayat bir
// değerle doldurulması yüzünden
// kazara tetiklenebiliyordu.
// Düzenleme disk değiştirmediği için
// (diyalogda seçici yok) burada hedef
// daima bölümün kendi diskidir.
state.custom_partitions[idx] = CustomPartition {
disk: state.custom_partitions[idx].disk.clone(),
device: state.custom_partitions[idx].device.clone(),
@@ -3420,33 +3332,30 @@ mod edit_disk_tests {
use super::*;
use super::testutil::part;
/// Düzenleme butonu `add_disk`'i düzenlenen bölümün KENDİ diskine
/// ayarlar.
/// Düzenleme, bölümün KENDİ diskini kullanır.
///
/// REGRESYON: düzenleme butonu bu atamayı yapmıyordu. `add_disk`
/// alanı "Yeni Bölüm" akışında aktif diske ayarlanıyor, dolayısıyla
/// şu akış bölümü YANLIŞ diske taşıyordu:
/// Diyalogda hedef disk seçicisi YOK; kaydetme yolu `disk`/`device`
/// alanlarını olduğu gibi kopyalıyor. Bu, geçmişte yaşanan bir hatanın
/// da doğal sonucu: `add_disk` adlı ayrı bir durum alanı varken
/// şu akış bölümü yanlış diske taşıyordu:
///
/// 1. "Yeni Bölüm" → add_disk = /dev/sda
/// 1. "Yeni Bölüm" (add_disk = /dev/sda)
/// 2. İptal
/// 3. /dev/sdb'deki bir bölüme tıkla → "Düzenle"
/// 4. Kaydet → bölüm /dev/sda'ya taşındı
///
/// Artık düzenleme kaydettiği bölümün diski sabitleniyor.
/// Alan tamamen kaldırıldı; artık "hangi disk" sorusunun tek cevabı var.
#[test]
fn edit_uses_the_partitions_own_disk() {
// Seçili disk sdb olsa BİLE düzenlenen bölümün kendi diski kullanılır.
let target = part("/dev/sdb1", 20_000, FsType::Ext4, "/home");
let add_disk_before = "/dev/sda".to_string(); // "Yeni Bölüm"den kalan
// Düzenleme butonunun yaptığı atama:
let add_disk_after = target.disk.clone();
let selected_disk = "/dev/sdb".to_string();
assert_eq!(target.disk, selected_disk);
assert_eq!(
add_disk_after, "/dev/sdb",
"düzenleme bölümün kendi diski kullanmalı, bayat add_disk değil"
target.disk, "/dev/sdb",
"düzenleme bölümün kendi diski kullanmalı"
);
assert_ne!(add_disk_before, add_disk_after,
"bayat değerin kullanılmadığı doğrulanmalı");
}
/// Düzenleme diski ve cihaz yolunu KORUMALI — taşıma yolu kaldırıldığı
@@ -3465,21 +3374,35 @@ mod edit_disk_tests {
assert_eq!(saved_device, "/dev/sdb2", "cihaz yolu değişmemeli");
}
/// Hedef disk seçicisi yalnızca ekleme modunda çizilmeli.
/// Hedef disk seçicisi KALDIRILDI — ne eklemede ne düzenlemede.
#[test]
fn target_disk_selector_only_in_add_mode() {
let show_selector = |is_edit: bool| !is_edit;
assert!(show_selector(false), "eklemede seçici görünmeli");
assert!(!show_selector(true), "düzenlemede seçici GİZLİ olmalı");
fn target_disk_selector_is_gone_entirely() {
// Diyalogda artık hiçbir disk seçici çizilmiyor. Bölümleme
// arayüzü tek disk üzerinde çalışıyor: tablo seçili diski
// gösterir, disk şeridi diski değiştirir, "Yeni Bölüm" o diske
// ekler. İki ayrı "aktif disk" kaynağı kafa karıştırıyordu.
let show_selector = |_is_edit: bool| false;
assert!(!show_selector(false), "eklemede seçici olmamalı");
assert!(!show_selector(true), "düzenlemede seçici olmamalı");
}
/// Yine de ekleme akışında başka diske bölüm koyabilmek mümkün
/// olmalı — çoklu disk kurulumunun temel ihtiyacı bu.
/// Çoklu disk kurulumu YİNE DE mümkün olmalı: hedefi değiştirmek için
/// disk şeridinden o diske geçilir, sonra "Yeni Bölüm" o diske ekler.
///
/// `add_disk` alanı kaldırıldığı için eklemenin diski tek kaynaktan,
/// `state.selected_disk`'ten gelir.
#[test]
fn add_flow_still_allows_another_disk() {
let mut add_disk = "/dev/sda".to_string();
fn new_partition_goes_to_the_selected_disk() {
let mut state = crate::installer::GlobalState::default();
state.selected_disk = Some("/dev/sda".to_string());
// sda seçiliyken ekle → sda
let add_disk = state.selected_disk.clone().unwrap_or_default();
assert_eq!(add_disk, "/dev/sda");
add_disk = "/dev/sdb".to_string(); // seçici değiştirdi
assert_eq!(add_disk, "/dev/sdb", "eklemede disk değiştirilebilmeli");
// Disk şeridinden sdb'ye geçildi
state.selected_disk = Some("/dev/sdb".to_string());
let add_disk_after = state.selected_disk.clone().unwrap_or_default();
assert_eq!(add_disk_after, "/dev/sdb", "ekleme seçili diske gitmeli");
}
}