From eb3822c07a214e4e52cb548ecd40c53e2975d1f3 Mon Sep 17 00:00:00 2001 From: Victor Fuentes Date: Wed, 31 Aug 2022 23:56:44 -0400 Subject: [PATCH] Add nix profile support Also broke a bunch of legacy/channel stuff I'll fix later --- Cargo.toml | 1 - flake.lock | 6 +- flake.nix | 4 + nsc-helper/src/main.rs | 49 ++++- src/lib.rs | 2 + src/parse/cache.rs | 235 +++++++++++++++++++++++- src/parse/packages.rs | 41 ++++- src/ui/installworker.rs | 272 +++++++++++++++++++--------- src/ui/pkgpage.rs | 134 +++++++++----- src/ui/preferencespage.rs | 45 +++-- src/ui/updatepage.rs | 27 ++- src/ui/updateworker.rs | 123 ++++++++++--- src/ui/window.rs | 371 ++++++++++++++++++++++++++++---------- src/ui/windowloading.rs | 62 +++++-- 14 files changed, 1075 insertions(+), 297 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 88c17ed..50f413b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,6 @@ reqwest = { version = "0.11", features = ["blocking"] } sha256 = "1.0" image = "0.24" spdx = "0.9" -# { git = "https://github.com/EmbarkStudios/spdx", version = "0.8" } edit-distance = "2.1" ijson = "0.1" strum = "0.24" diff --git a/flake.lock b/flake.lock index 36c7d0d..81a3bab 100644 --- a/flake.lock +++ b/flake.lock @@ -51,11 +51,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1661361016, - "narHash": "sha256-Bjf6ZDnDc6glTwIIItvwfcaeJ5zWFM6GYfPajSArdUY=", + "lastModified": 1661720780, + "narHash": "sha256-AJNGyaB2eKZAYaPNjBZOzap87yL+F9ZLaFzzMkvega0=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "b784c5ae63dd288375af1b4d37b8a27dd8061887", + "rev": "a63021a330d8d33d862a8e29924b42d73037dd37", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 53f5063..838f6f7 100644 --- a/flake.nix +++ b/flake.nix @@ -48,6 +48,7 @@ buildInputs = with pkgs; [ cargo clippy + desktop-file-utils rust-analyzer rustc rustfmt @@ -58,10 +59,13 @@ gtk4 gtksourceview5 libadwaita-git + meson + ninja openssl pandoc pango pkgconfig + polkit wrapGAppsHook4 nixos-appstream-data ]; diff --git a/nsc-helper/src/main.rs b/nsc-helper/src/main.rs index 78bb6e4..31ff1ae 100644 --- a/nsc-helper/src/main.rs +++ b/nsc-helper/src/main.rs @@ -25,7 +25,16 @@ enum SubCommands { rebuild: bool, /// Run `nixos-rebuild` with the given arguments arguments: Vec, - } + }, + Flake { + /// Whether to rebuild the system after updating flake + #[clap(short, long)] + rebuild: bool, + /// Path to the flake file + flakepath: String, + /// Run `nixos-rebuild` with the given arguments + arguments: Vec, + }, } fn main() { @@ -88,6 +97,24 @@ fn main() { } }, } + }, + SubCommands::Flake { rebuild: dorebuild, flakepath, arguments } => { + match dorebuild { + true => match rebuild(arguments) { + Ok(_) => (), + Err(err) => { + eprintln!("{}", err); + std::process::exit(1); + } + }, + false => match flake(&flakepath) { + Ok(_) => (), + Err(err) => { + eprintln!("{}", err); + std::process::exit(1); + } + }, + } } } } @@ -125,10 +152,28 @@ fn channel() -> Result<(), Box> { if x.success() { Ok(()) } else { - eprintln!("nixos-rebuild failed with exit code {}", x.code().unwrap()); + eprintln!("nix-channel failed with exit code {}", x.code().unwrap()); Err(Box::new(io::Error::new( io::ErrorKind::Other, "nix-channel failed", ))) } +} + +fn flake(path: &str) -> Result<(), Box> { + let mut cmd = Command::new("nix") + .arg("flake") + .arg("upgrade") + .arg(path) + .spawn()?; + let x = cmd.wait()?; + if x.success() { + Ok(()) + } else { + eprintln!("nix flake failed with exit code {}", x.code().unwrap()); + Err(Box::new(io::Error::new( + io::ErrorKind::Other, + "nix flake failed", + ))) + } } \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 511d423..770541e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,5 @@ +use std::{path::Path, fs}; + pub mod ui; pub mod parse; pub mod config; diff --git a/src/parse/cache.rs b/src/parse/cache.rs index 48c21fb..69423a2 100644 --- a/src/parse/cache.rs +++ b/src/parse/cache.rs @@ -1,5 +1,6 @@ use ijson::IString; use serde::{Deserialize, Serialize}; +use serde_json::Value; use std::{ collections::HashMap, env, @@ -21,13 +22,14 @@ struct NewPackage { } pub fn checkcache() -> Result<(), Box> { - setuppkgscache()?; - setupupdatecache()?; - setupnewestver()?; + // setuplegacypkgscache().unwrap(); + // setupupdatecache().unwrap(); + // setupnewestver().unwrap(); + setupflakepkgscache()?; Ok(()) } -pub fn uptodate() -> Result, Box> { +pub fn uptodatelegacy() -> Result, Box> { let cachedir = format!("{}/.cache/nix-software-center", env::var("HOME")?); let oldversion = fs::read_to_string(format!("{}/sysver.txt", cachedir))? .trim() @@ -39,11 +41,32 @@ pub fn uptodate() -> Result, Box> { println!("System is up to date"); Ok(None) } else { - println!("OLD {:?} != NEW {:?}", oldversion, newversion); Ok(Some((oldversion, newversion))) } } +pub fn uptodateflake() -> Result, Box> { + let cachedir = format!("{}/.cache/nix-software-center", env::var("HOME")?); + let oldversion = fs::read_to_string(format!("{}/flakever.txt", cachedir))? + .trim() + .to_string(); + let newversion = fs::read_to_string(format!("{}/newver.txt", cachedir))? + .trim() + .to_string(); + if oldversion == newversion { + println!("System is up to date"); + Ok(None) + } else { + println!("OLD {:?} != NEW {:?}", oldversion, newversion); + if let (Some(oldv), Some(newv)) = (oldversion.get(..8), newversion.get(..8)) { + println!("OLD {:?} != NEW {:?}", oldv, newv); + Ok(Some((oldv.to_string(), newv.to_string()))) + } else { + Ok(Some((oldversion, newversion))) + } + } +} + pub fn channelver() -> Result, Box> { let cachedir = format!("{}/.cache/nix-software-center", env::var("HOME")?); let oldversion = fs::read_to_string(format!("{}/chnver.txt", cachedir))? @@ -61,7 +84,24 @@ pub fn channelver() -> Result, Box> { } } -fn setuppkgscache() -> Result<(), Box> { +pub fn flakever() -> Result, Box> { + let cachedir = format!("{}/.cache/nix-software-center", env::var("HOME")?); + let oldversion = fs::read_to_string(format!("{}/flakever.txt", cachedir))? + .trim() + .to_string(); + let newversion = fs::read_to_string(format!("{}/newver.txt", cachedir))? + .trim() + .to_string(); + if oldversion == newversion { + println!("Flake hashes match"); + Ok(None) + } else { + println!("flakever {:?} != newver {:?}", oldversion, newversion); + Ok(Some((oldversion, newversion))) + } +} + +fn setuplegacypkgscache() -> Result<(), Box> { let vout = Command::new("nix-instantiate") .arg("-I") .arg("nixpkgs=/nix/var/nix/profiles/per-user/root/channels/nixos") @@ -124,8 +164,187 @@ fn setuppkgscache() -> Result<(), Box> { Ok(()) } +fn setupflakepkgscache() -> Result<(), Box> { + println!("SETUP FLAKES CACHE"); + let cachedir = format!("{}/.cache/nix-software-center", env::var("HOME")?); + + // First remove legacy files + if Path::new(format!("{}/chnver.txt", &cachedir).as_str()).exists() { + fs::remove_file(format!("{}/chnver.txt", &cachedir).as_str())?; + } + + let vout = Command::new("nixos-version") + .arg("--json") + .output()?; + + let versiondata: Value = serde_json::from_str(&String::from_utf8_lossy(&vout.stdout))?; + let rev = versiondata.get("nixpkgsRevision").unwrap().as_str().unwrap(); + let dlver = versiondata.get("nixosVersion").unwrap().as_str().unwrap(); + + let mut relver = dlver.split('.').collect::>()[0..2].join("."); + println!("RELVER {}", relver); + if relver == "22.11" { + relver = "unstable".to_string(); + } + + fs::create_dir_all(&cachedir).expect("Failed to create cache directory"); + let url = format!( + "https://channels.nixos.org/nixos-{}/packages.json.br", + relver + ); + + println!("VERSION {}", relver); + // let response = reqwest::blocking::get(url)?; + // if let Some(latest) = response.url().to_string().split('/').last() { + let cachedir = format!("{}/.cache/nix-software-center", env::var("HOME")?); + if !Path::new(&cachedir).exists() { + fs::create_dir_all(&cachedir).expect("Failed to create cache directory"); + } + + fn writesyspkgs(outfile: &str) -> Result<(), Box> { + let output = Command::new("nix") + .arg("search") + .arg("--inputs-from") + .arg("/home/victor/nix") + .arg("nixpkgs") + .arg("--json") + .output()?; + let mut file = fs::File::create(outfile)?; + file.write_all(&output.stdout)?; + Ok(()) + } + + fn writeprofilepkgs(outfile: &str) -> Result<(), Box> { + let output = Command::new("nix") + .arg("search") + .arg("nixpkgs") + .arg("--json") + .output()?; + let mut file = fs::File::create(outfile)?; + file.write_all(&output.stdout)?; + Ok(()) + } + + if !Path::new(&format!("{}/flakever.txt", &cachedir)).exists() { + let mut sysver = fs::File::create(format!("{}/flakever.txt", &cachedir))?; + sysver.write_all(rev.as_bytes())?; + writesyspkgs(&format!("{}/syspackages.json", &cachedir))?; + } else { + let oldver = fs::read_to_string(&Path::new(format!("{}/flakever.txt", &cachedir).as_str()))?; + let sysver = rev; + if oldver != sysver { + println!("OLD FLAKEVER: {}, != NEW: {}", oldver, sysver); + let mut sysver = fs::File::create(format!("{}/flakever.txt", &cachedir))?; + sysver.write_all(rev.as_bytes())?; + writesyspkgs(&format!("{}/syspackages.json", &cachedir))?; + } + } + + if !Path::new(&format!("{}/syspackages.json", &cachedir)).exists() { + writesyspkgs(&format!("{}/syspackages.json", &cachedir))?; + } + + + + // Check nix profile nixpkgs version + let client = reqwest::blocking::Client::builder() + .user_agent("request") + .build()?; + let response = client.get("https://api.github.com/repos/NixOS/nixpkgs/commits/nixpkgs-unstable").send()?; + if response.status().is_success() { + let profilerevjson = response.text()?; + let profilerevdata: Value = serde_json::from_str(&profilerevjson)?; + let profilerev = profilerevdata.get("sha").unwrap().as_str().unwrap(); + println!("PROFILE REV {}", profilerev); + + if !Path::new(&format!("{}/profilever.txt", &cachedir)).exists() { + let mut sysver = fs::File::create(format!("{}/profilever.txt", &cachedir))?; + sysver.write_all(profilerev.as_bytes())?; + writeprofilepkgs(&format!("{}/profilepackages.json", &cachedir))?; + } else { + let oldver = fs::read_to_string(&Path::new(format!("{}/profilever.txt", &cachedir).as_str()))?; + let sysver = profilerev; + if oldver != sysver { + println!("OLD PROFILEVER: {}, != NEW: {}", oldver, sysver); + let mut sysver = fs::File::create(format!("{}/profilever.txt", &cachedir))?; + sysver.write_all(profilerev.as_bytes())?; + writeprofilepkgs(&format!("{}/profilepackages.json", &cachedir))?; + } else { + println!("PROFILEVER UP TO DATE"); + } + } + } + if !Path::new(&format!("{}/profilepackages.json", &cachedir)).exists() { + writeprofilepkgs(&format!("{}/profilepackages.json", &cachedir))?; + } + + + // Check newest nixpkgs version + let revurl = format!("https://channels.nixos.org/nixos-{}/git-revision", relver); + let response = reqwest::blocking::get(revurl)?; + let mut dl = false; + if response.status().is_success() { + let newrev = response.text()?; + println!("NEW REV: {}", newrev); + if Path::new(&format!("{}/newver.txt", &cachedir)).exists() { + let oldrev = fs::read_to_string(&format!("{}/newver.txt", &cachedir))?; + if oldrev != newrev { + dl = true; + } + } else { + dl = true; + } + let mut sysver = fs::File::create(format!("{}/newver.txt", &cachedir))?; + sysver.write_all(newrev.as_bytes())?; + // if !Path::new(&format!("{}/newver.txt", &cachedir)).exists() { + // } + } + + + + // if Path::new(format!("{}/chnver.txt", &cachedir).as_str()).exists() + // && fs::read_to_string(&Path::new(format!("{}/chnver.txt", &cachedir).as_str()))? == dlver + // && Path::new(format!("{}/packages.json", &cachedir).as_str()).exists() + // { + // return Ok(()); + // } else { + // let oldver = fs::read_to_string(&Path::new(format!("{}/chnver.txt", &cachedir).as_str()))?; + // let sysver = &dlver; + // // Change to debug msg + // println!("OLD: {}, != NEW: {}", oldver, sysver); + // } + // if Path::new(format!("{}/chnver.txt", &cachedir).as_str()).exists() { + // fs::remove_file(format!("{}/chnver.txt", &cachedir).as_str())?; + // } + // let mut sysver = fs::File::create(format!("{}/chnver.txt", &cachedir))?; + // sysver.write_all(dlver.as_bytes())?; + + + + let outfile = format!("{}/packages.json", &cachedir); + if dl { + dlfile(&url, &outfile)?; + } + Ok(()) +} + +// nix-instantiate --eval -E '(builtins.getFlake "/home/victor/nix").inputs.nixpkgs.outPath' +// nix-env -f /nix/store/sjmq1gphj1arbzf4aqqnygd9pf4hkfkf-source -qa --json > packages.json fn setupupdatecache() -> Result<(), Box> { - let dlver = fs::read_to_string("/run/current-system/nixos-version")?; + let vout = Command::new("nix-instantiate") + .arg("-I") + .arg("nixpkgs=/nix/var/nix/profiles/per-user/root/channels/nixos") + .arg("") + .arg("-A") + .arg("version") + .arg("--eval") + .arg("--json") + .output()?; + +let dlver = String::from_utf8_lossy(&vout.stdout) + .to_string() + .replace('"', ""); + // let dlver = fs::read_to_string("/run/current-system/nixos-version")?; let mut relver = dlver.split('.').collect::>().join(".")[0..5].to_string(); @@ -171,7 +390,7 @@ fn setupupdatecache() -> Result<(), Box> { dlfile(&url, &outfile)?; let file = File::open(&outfile)?; let reader = BufReader::new(file); - let pkgbase: NewPackageBase = simd_json::serde::from_reader(reader).unwrap(); + let pkgbase: NewPackageBase = simd_json::serde::from_reader(reader)?; let mut outbase = HashMap::new(); for (pkg, ver) in pkgbase.packages { outbase.insert(pkg.clone(), ver.version.clone()); diff --git a/src/parse/packages.rs b/src/parse/packages.rs index c1f918e..3c538dc 100644 --- a/src/parse/packages.rs +++ b/src/parse/packages.rs @@ -156,23 +156,29 @@ pub struct AppScreenshotImage { pub url: String, } +#[derive(Debug, Serialize, Deserialize)] +struct FlakeJson { + pname: IString, + version: IString, +} + pub async fn readpkgs() -> Result, Box> { let cachedir = format!("{}/.cache/nix-software-center/", env::var("HOME")?); let cachefile = format!("{}/packages.json", cachedir); - let file = File::open(cachefile).unwrap(); + let file = File::open(cachefile)?; let reader = BufReader::new(file); - let pkgbase: PackageBase = simd_json::serde::from_reader(reader).unwrap(); + let pkgbase: PackageBase = simd_json::serde::from_reader(reader)?; let mut pkgs = pkgbase.packages; println!("APPDATADIR {}", APPINFO); - let appdata = File::open(&format!("{}/xmls/nixos_x86_64_linux.yml.gz", APPINFO)).unwrap(); + let appdata = File::open(&format!("{}/xmls/nixos_x86_64_linux.yml.gz", APPINFO))?; let appreader = BufReader::new(appdata); let mut d = GzDecoder::new(appreader); let mut s = String::new(); - d.read_to_string(&mut s).unwrap(); + d.read_to_string(&mut s)?; let mut files = s.split("\n---\n").collect::>(); files.remove(0); for f in files { - let appstream: AppData = serde_yaml::from_str(f).unwrap(); + let appstream: AppData = serde_yaml::from_str(f)?; if let Some(p) = pkgs.get_mut(&appstream.package.to_string()) { p.appdata = Some(appstream); } @@ -180,11 +186,32 @@ pub async fn readpkgs() -> Result, Box Result, Box> { +pub fn readlegacysyspkgs() -> Result, Box> { let cachedir = format!("{}/.cache/nix-software-center/", env::var("HOME")?); let cachefile = format!("{}/syspackages.json", cachedir); let file = File::open(cachefile)?; let reader = BufReader::new(file); - let newpkgs: HashMap = simd_json::serde::from_reader(reader).unwrap(); + let newpkgs: HashMap = simd_json::serde::from_reader(reader)?; Ok(newpkgs) +} + +pub fn readflakesyspkgs() -> Result, Box> { + println!("READFLAKESYSPKGS"); + let cachedir = format!("{}/.cache/nix-software-center/", env::var("HOME")?); + let cachefile = format!("{}/syspackages.json", cachedir); + let file = File::open(cachefile)?; + let reader = BufReader::new(file); + let newpkgs: HashMap = simd_json::serde::from_reader(reader)?; + let newpkgs = newpkgs.into_iter().filter_map(|(k, v)| if let Some(pkg) = k.strip_prefix("legacyPackages.x86_64-linux.") { Some((pkg.to_string(), v.version.to_string())) } else { None }).collect::>(); + Ok(newpkgs) +} + +pub fn readprofilepkgs() -> Result, Box> { + let cachedir = format!("{}/.cache/nix-software-center/", env::var("HOME")?); + let cachefile = format!("{}/profilepackages.json", cachedir); + let file = File::open(cachefile)?; + let reader = BufReader::new(file); + let profilepkgs: HashMap = simd_json::serde::from_reader(reader)?; + let profilepkgs = profilepkgs.into_iter().filter_map(|(k, v)| if let Some(pkg) = k.strip_prefix("legacyPackages.x86_64-linux.") { Some((pkg.to_string(), v.version.to_string())) } else { None }).collect::>(); + Ok(profilepkgs) } \ No newline at end of file diff --git a/src/ui/installworker.rs b/src/ui/installworker.rs index 6398454..c77b6fe 100644 --- a/src/ui/installworker.rs +++ b/src/ui/installworker.rs @@ -1,6 +1,7 @@ use crate::parse::config::NscConfig; use super::pkgpage::{InstallType, PkgAction, PkgMsg, WorkPkg}; +use super::window::{UserPkgs, SystemPkgs}; use relm4::*; use std::error::Error; use std::path::Path; @@ -17,6 +18,8 @@ pub struct InstallAsyncHandler { systemconfig: String, flakeargs: Option, pid: Option, + syspkgs: SystemPkgs, + userpkgs: UserPkgs, } #[derive(Debug)] @@ -27,18 +30,26 @@ pub enum InstallAsyncHandlerMsg { SetPid(Option), } +#[derive(Debug)] +pub struct InstallAsyncHandlerInit { + pub syspkgs: SystemPkgs, + pub userpkgs: UserPkgs, +} + impl Worker for InstallAsyncHandler { - type InitParams = (); + type InitParams = InstallAsyncHandlerInit; type Input = InstallAsyncHandlerMsg; type Output = PkgMsg; - fn init(_params: Self::InitParams, _sender: relm4::ComponentSender) -> Self { + fn init(params: Self::InitParams, _sender: relm4::ComponentSender) -> Self { Self { process: None, work: None, systemconfig: String::new(), flakeargs: None, pid: None, + syspkgs: params.syspkgs, + userpkgs: params.userpkgs, tracker: 0, } } @@ -61,91 +72,188 @@ impl Worker for InstallAsyncHandler { match work.action { PkgAction::Install => { println!("Installing user package: {}", work.pkg); - self.process = Some(relm4::spawn(async move { - let mut p = tokio::process::Command::new("nix-env") - .arg("-iA") - .arg(format!("nixos.{}", work.pkg)) - .kill_on_drop(true) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .spawn() - .expect("Failed to run nix-env"); - - let stderr = p.stderr.take().unwrap(); - let reader = tokio::io::BufReader::new(stderr); - - let mut lines = reader.lines(); - while let Ok(Some(line)) = lines.next_line().await { - println!("CAUGHT LINE: {}", line); - } - - match p.wait().await { - Ok(o) => { - if o.success() { - println!( - "Removed user package: {} success", - work.pkg - ); - // println!("{}", String::from_utf8_lossy(&pstdout)); - sender.output(PkgMsg::FinishedProcess(work)) - } else { - println!( - "Removed user package: {} failed", - work.pkg - ); - // println!("{}", String::from_utf8_lossy(&p.stderr)); - sender.output(PkgMsg::FailedProcess(work)); + println!("{:?}", self.userpkgs); + match self.userpkgs { + UserPkgs::Env => { + self.process = Some(relm4::spawn(async move { + let mut p = tokio::process::Command::new("nix-env") + .arg("-iA") + .arg(format!("nixos.{}", work.pkg)) + .kill_on_drop(true) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .expect("Failed to run nix-env"); + + let stderr = p.stderr.take().unwrap(); + let reader = tokio::io::BufReader::new(stderr); + + let mut lines = reader.lines(); + while let Ok(Some(line)) = lines.next_line().await { + println!("CAUGHT LINE: {}", line); } - } - Err(e) => { - println!("Error removing user package: {}", e); - sender.output(PkgMsg::FailedProcess(work)); - } + + match p.wait().await { + Ok(o) => { + if o.success() { + println!( + "Removed user package: {} success", + work.pkg + ); + // println!("{}", String::from_utf8_lossy(&pstdout)); + sender.output(PkgMsg::FinishedProcess(work)) + } else { + println!( + "Removed user package: {} failed", + work.pkg + ); + // println!("{}", String::from_utf8_lossy(&p.stderr)); + sender.output(PkgMsg::FailedProcess(work)); + } + } + Err(e) => { + println!("Error removing user package: {}", e); + sender.output(PkgMsg::FailedProcess(work)); + } + } + })); } - })); + UserPkgs::Profile => { + self.process = Some(relm4::spawn(async move { + let mut p = tokio::process::Command::new("nix") + .arg("profile") + .arg("install") + .arg(format!("nixpkgs#{}", work.pkg)) + .kill_on_drop(true) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .expect("Failed to run nix profile"); + + let stderr = p.stderr.take().unwrap(); + let reader = tokio::io::BufReader::new(stderr); + + let mut lines = reader.lines(); + while let Ok(Some(line)) = lines.next_line().await { + println!("CAUGHT LINE: {}", line); + } + + match p.wait().await { + Ok(o) => { + if o.success() { + println!( + "Removed user package: {} success", + work.pkg + ); + // println!("{}", String::from_utf8_lossy(&pstdout)); + sender.output(PkgMsg::FinishedProcess(work)) + } else { + println!( + "Removed user package: {} failed", + work.pkg + ); + // println!("{}", String::from_utf8_lossy(&p.stderr)); + sender.output(PkgMsg::FailedProcess(work)); + } + } + Err(e) => { + println!("Error removing user package: {}", e); + sender.output(PkgMsg::FailedProcess(work)); + } + } + })); + } + } } PkgAction::Remove => { println!("Removing user package: {}", work.pkg); - self.process = Some(relm4::spawn(async move { - let mut p = tokio::process::Command::new("nix-env") - .arg("-e") - .arg(&work.pname) - .kill_on_drop(true) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .spawn() - .expect("Failed to run nix-env"); - let stderr = p.stderr.take().unwrap(); - let reader = tokio::io::BufReader::new(stderr); - - let mut lines = reader.lines(); - while let Ok(Some(line)) = lines.next_line().await { - println!("CAUGHT LINE: {}", line); - } - match p.wait().await { - Ok(o) => { - if o.success() { - println!( - "Removed user package: {} success", - work.pkg - ); - // println!("{}", String::from_utf8_lossy(&pstdout)); - sender.output(PkgMsg::FinishedProcess(work)) - } else { - println!( - "Removed user package: {} failed", - work.pkg - ); - // println!("{}", String::from_utf8_lossy(&p.stderr)); - sender.output(PkgMsg::FailedProcess(work)); + match self.userpkgs { + UserPkgs::Env => { + self.process = Some(relm4::spawn(async move { + let mut p = tokio::process::Command::new("nix-env") + .arg("-e") + .arg(&work.pname) + .kill_on_drop(true) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .expect("Failed to run nix-env"); + let stderr = p.stderr.take().unwrap(); + let reader = tokio::io::BufReader::new(stderr); + + let mut lines = reader.lines(); + while let Ok(Some(line)) = lines.next_line().await { + println!("CAUGHT LINE: {}", line); } - } - Err(e) => { - println!("Error removing user package: {}", e); - sender.output(PkgMsg::FailedProcess(work)); - } + match p.wait().await { + Ok(o) => { + if o.success() { + println!( + "Removed user package: {} success", + work.pkg + ); + // println!("{}", String::from_utf8_lossy(&pstdout)); + sender.output(PkgMsg::FinishedProcess(work)) + } else { + println!( + "Removed user package: {} failed", + work.pkg + ); + // println!("{}", String::from_utf8_lossy(&p.stderr)); + sender.output(PkgMsg::FailedProcess(work)); + } + } + Err(e) => { + println!("Error removing user package: {}", e); + sender.output(PkgMsg::FailedProcess(work)); + } + } + })); } - })); + UserPkgs::Profile => { + self.process = Some(relm4::spawn(async move { + let mut p = tokio::process::Command::new("nix") + .arg("profile") + .arg("remove") + .arg(&format!("legacyPackages.x86_64-linux.{}", work.pkg)) + .kill_on_drop(true) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .expect("Failed to run nix profile"); + let stderr = p.stderr.take().unwrap(); + let reader = tokio::io::BufReader::new(stderr); + + let mut lines = reader.lines(); + while let Ok(Some(line)) = lines.next_line().await { + println!("CAUGHT LINE: {}", line); + } + match p.wait().await { + Ok(o) => { + if o.success() { + println!( + "Removed user package: {} success", + work.pkg + ); + // println!("{}", String::from_utf8_lossy(&pstdout)); + sender.output(PkgMsg::FinishedProcess(work)) + } else { + println!( + "Removed user package: {} failed", + work.pkg + ); + // println!("{}", String::from_utf8_lossy(&p.stderr)); + sender.output(PkgMsg::FailedProcess(work)); + } + } + Err(e) => { + println!("Error removing user package: {}", e); + sender.output(PkgMsg::FailedProcess(work)); + } + } + })); + } + } } } } @@ -276,8 +384,8 @@ async fn installsys( let exe = match std::env::current_exe() { Ok(mut e) => { e.pop(); // root/bin - // e.pop(); // root/ - // e.push("libexec"); // root/libexec + e.pop(); // root/ + e.push("libexec"); // root/libexec e.push("nsc-helper"); let x = e.to_string_lossy().to_string(); println!("CURRENT PATH {}", x); diff --git a/src/ui/pkgpage.rs b/src/ui/pkgpage.rs index 4ae43aa..ff445d4 100644 --- a/src/ui/pkgpage.rs +++ b/src/ui/pkgpage.rs @@ -9,6 +9,7 @@ use relm4::{factory::FactoryVecDeque, *}; use sha256::digest; use std::collections::HashSet; use std::convert::identity; +use std::io::Cursor; use std::process::Command; use std::{ env, @@ -27,6 +28,9 @@ use crate::parse::packages::StrOrVec; use crate::ui::installworker::InstallAsyncHandlerMsg; use super::installworker::InstallAsyncHandler; +use super::installworker::InstallAsyncHandlerInit; +use super::window::SystemPkgs; +use super::window::UserPkgs; use super::{screenshotfactory::ScreenshotItem, window::AppMsg}; #[tracker::track] @@ -46,6 +50,9 @@ pub struct PkgModel { maintainers: Vec, launchable: Option, + syspkgtype: SystemPkgs, + userpkgtype: UserPkgs, + #[tracker::no_eq] screenshots: FactoryVecDeque, #[tracker::no_eq] @@ -158,9 +165,15 @@ pub enum PkgAsyncMsg { SetError(String, usize), } +#[derive(Debug)] +pub struct PkgPageTypes { + pub syspkgs: SystemPkgs, + pub userpkgs: UserPkgs +} + #[relm4::component(pub)] impl Component for PkgModel { - type Init = (); + type Init = PkgPageTypes; type Input = PkgMsg; type Output = AppMsg; type Widgets = PkgWidgets; @@ -188,12 +201,26 @@ impl Component for PkgModel { }, pack_end = >k::MenuButton { #[watch] - set_label: match model.installtype { - InstallType::User => "User (nix-env)", - InstallType::System => "System (configuration.nix)", + set_label: match model.userpkgtype { + UserPkgs::Env => { + match model.installtype { + InstallType::User => "User (nix-env)", + InstallType::System => "System (configuration.nix)", + } + } + UserPkgs::Profile => { + match model.installtype { + InstallType::User => "User (nix profile)", + InstallType::System => "System (configuration.nix)", + } + } }, + #[wrap(Some)] - set_popover = >k::PopoverMenu::from_model(Some(&installtype)) {} + set_popover = >k::PopoverMenu::from_model(Some(&match model.userpkgtype { + UserPkgs::Env => installtype, + UserPkgs::Profile => installprofiletype, + })) {} } }, gtk::ScrolledWindow { @@ -899,6 +926,10 @@ impl Component for PkgModel { "User (nix-env)" => NixEnvAction, "System (configuration.nix)" => NixSystemAction, }, + installprofiletype: { + "User (nix-profile)" => NixProfileAction, + "System (configuration.nix)" => NixSystemAction, + }, runaction: { "Run without installing" => LaunchAction, "Open interactive shell" => TermShellAction, @@ -906,12 +937,12 @@ impl Component for PkgModel { } fn init( - (): Self::Init, + pkgtypes: Self::Init, root: &Self::Root, sender: ComponentSender, ) -> ComponentParts { let installworker = InstallAsyncHandler::builder() - .detach_worker(()) + .detach_worker(InstallAsyncHandlerInit { syspkgs: pkgtypes.syspkgs.clone(), userpkgs: pkgtypes.userpkgs.clone() }) .forward(sender.input_sender(), identity); let config = getconfig(); installworker.emit(InstallAsyncHandlerMsg::SetConfig(config.clone())); @@ -936,6 +967,8 @@ impl Component for PkgModel { // installinguserpkgs: HashSet::new(), // installingsystempkgs: HashSet::new(), // removinguserpkgs: HashSet::new(), + syspkgtype: pkgtypes.syspkgs, + userpkgtype: pkgtypes.userpkgs, workqueue: HashSet::new(), launchable: None, tracker: 0, @@ -962,6 +995,15 @@ impl Component for PkgModel { }) }; + let nixprofile: RelmAction = { + let sender = sender.clone(); + RelmAction::new_stateless(move |_| { + println!("NIX PROFILE!"); + sender.input(PkgMsg::SetInstallType(InstallType::User)); + // sender.input(AppMsg::Increment); + }) + }; + let nixsystem: RelmAction = { let sender = sender.clone(); RelmAction::new_stateless(move |_| { @@ -972,6 +1014,7 @@ impl Component for PkgModel { }; group.add_action(nixenv); + group.add_action(nixprofile); group.add_action(nixsystem); let actions = group.into_action_group(); @@ -1124,6 +1167,7 @@ impl Component for PkgModel { let sha = digest(&url); let scrnpath = format!("{}/screenshots/{}", cachedir, sha); let pkg = self.pkg.clone(); + sender.command(move |out, shutdown| { let url = url.clone(); let home = home.clone(); @@ -1135,8 +1179,8 @@ impl Component for PkgModel { if Path::new(&format!("{}.png", scrnpath)).exists() { out.send(PkgAsyncMsg::LoadScreenshot(pkg, i, format!("{}.png", scrnpath))); } else { - match reqwest::blocking::get(&url) { - Ok(mut response) => { + match reqwest::get(&url).await { + Ok(response) => { if response.status().is_success() { if !Path::new(&format!( "{}/.cache/nix-software-center/screenshots", @@ -1156,43 +1200,49 @@ impl Component for PkgModel { } } if let Ok(mut file) = File::create(&scrnpath) { - if response.copy_to(&mut file).is_ok() { - fn openimg(scrnpath: &str) -> Result<(), Box> { - // let mut reader = Reader::new(Cursor::new(imgdata.buffer())).with_guessed_format().expect("Cursor io never fails"); - let img = if let Ok(x) = image::load(BufReader::new(File::open(scrnpath)?), image::ImageFormat::Png) { - x - } else if let Ok(x) = image::load(BufReader::new(File::open(scrnpath)?), image::ImageFormat::Jpeg) { - x - } else if let Ok(x) = image::load(BufReader::new(File::open(scrnpath)?), image::ImageFormat::WebP) { - x - } else { - let imgdata = BufReader::new(File::open(scrnpath)?); - let format = image::guess_format(imgdata.buffer())?; - image::load(imgdata, format)? - }; - let scaled = img.resize(640, 360, FilterType::Lanczos3); - let mut output = File::create(&format!("{}.png", scrnpath))?; - scaled.write_to(&mut output, ImageFormat::Png)?; - if let Err(e) = fs::remove_file(&scrnpath) { - eprintln!("{}", e); - } - Ok(()) - } - - match openimg(&scrnpath) { - Ok(_) => { - out.send(PkgAsyncMsg::LoadScreenshot( - pkg, i, format!("{}.png", scrnpath), - )); - } - Err(_) => { + if let Ok(b) = response.bytes().await { + let mut content = Cursor::new(b); + if std::io::copy(&mut content, &mut file).is_ok() { + fn openimg(scrnpath: &str) -> Result<(), Box> { + // let mut reader = Reader::new(Cursor::new(imgdata.buffer())).with_guessed_format().expect("Cursor io never fails"); + let img = if let Ok(x) = image::load(BufReader::new(File::open(scrnpath)?), image::ImageFormat::Png) { + x + } else if let Ok(x) = image::load(BufReader::new(File::open(scrnpath)?), image::ImageFormat::Jpeg) { + x + } else if let Ok(x) = image::load(BufReader::new(File::open(scrnpath)?), image::ImageFormat::WebP) { + x + } else { + let imgdata = BufReader::new(File::open(scrnpath)?); + let format = image::guess_format(imgdata.buffer())?; + image::load(imgdata, format)? + }; + let scaled = img.resize(640, 360, FilterType::Lanczos3); + let mut output = File::create(&format!("{}.png", scrnpath))?; + scaled.write_to(&mut output, ImageFormat::Png)?; if let Err(e) = fs::remove_file(&scrnpath) { eprintln!("{}", e); } - out.send(PkgAsyncMsg::SetError(pkg, i)); + Ok(()) + } + + match openimg(&scrnpath) { + Ok(_) => { + out.send(PkgAsyncMsg::LoadScreenshot( + pkg, i, format!("{}.png", scrnpath), + )); + } + Err(_) => { + if let Err(e) = fs::remove_file(&scrnpath) { + eprintln!("{}", e); + } + out.send(PkgAsyncMsg::SetError(pkg, i)); + } } } } + } else { + out.send(PkgAsyncMsg::SetError(pkg, i)); + eprintln!("Error: {}", response.status()); } } else { out.send(PkgAsyncMsg::SetError(pkg, i)); @@ -1361,7 +1411,8 @@ impl Component for PkgModel { self.installedsystempkgs.remove(&work.pkg); // sender.output(AppMsg::RemoveSystemPkg(work.pkg)); } - } + }; + sender.output(AppMsg::UpdateUpdatePkgs); } } sender.output(AppMsg::UpdatePkgs(None)); @@ -1465,6 +1516,7 @@ impl Component for PkgModel { relm4::new_action_group!(ModeActionGroup, "mode"); relm4::new_stateless_action!(NixEnvAction, ModeActionGroup, "env"); +relm4::new_stateless_action!(NixProfileAction, ModeActionGroup, "profile"); relm4::new_stateless_action!(NixSystemAction, ModeActionGroup, "system"); relm4::new_action_group!(RunActionGroup, "run"); diff --git a/src/ui/preferencespage.rs b/src/ui/preferencespage.rs index 021508a..fcc30c5 100644 --- a/src/ui/preferencespage.rs +++ b/src/ui/preferencespage.rs @@ -5,12 +5,15 @@ use adw::prelude::*; use relm4::*; use relm4_components::open_dialog::*; +#[tracker::track] #[derive(Debug)] pub struct PreferencesPageModel { hidden: bool, configpath: PathBuf, flake: Option<(PathBuf, String)>, + #[tracker::no_eq] open_dialog: Controller, + #[tracker::no_eq] flake_file_dialog: Controller, } @@ -74,13 +77,13 @@ impl SimpleComponent for PreferencesPageModel { sender.input(PreferencesPageMsg::Open); } }, - gtk::Button { - add_css_class: "flat", - set_icon_name: "view-refresh-symbolic", - connect_clicked[sender] => move |_| { - sender.input(PreferencesPageMsg::SetConfigPath(PathBuf::from("/etc/nixos/configuration.nix"))); - } - } + // gtk::Button { + // add_css_class: "flat", + // set_icon_name: "view-refresh-symbolic", + // connect_clicked[sender] => move |_| { + // sender.input(PreferencesPageMsg::SetConfigPath(PathBuf::from("/etc/nixos/configuration.nix"))); + // } + // } } }, add = &adw::ActionRow { @@ -94,7 +97,10 @@ impl SimpleComponent for PreferencesPageModel { sender.input(PreferencesPageMsg::SetFlake(None)); } gtk::Inhibit(false) - } + } @switched, + #[track(model.changed(PreferencesPageModel::flake()))] + #[block_signal(switched)] + set_state: model.flake.is_some() } }, add = &adw::ActionRow { @@ -133,13 +139,13 @@ impl SimpleComponent for PreferencesPageModel { sender.input(PreferencesPageMsg::OpenFlake); } }, - gtk::Button { - add_css_class: "flat", - set_icon_name: "user-trash-symbolic", - connect_clicked[sender] => move |_| { - sender.input(PreferencesPageMsg::SetFlakePath(PathBuf::new())); - } - } + // gtk::Button { + // add_css_class: "flat", + // set_icon_name: "user-trash-symbolic", + // connect_clicked[sender] => move |_| { + // sender.input(PreferencesPageMsg::SetFlakePath(PathBuf::new())); + // } + // } } }, add = &adw::EntryRow { @@ -148,7 +154,10 @@ impl SimpleComponent for PreferencesPageModel { set_title: "Flake arguments (--flake path/to/flake.nix#)", connect_changed[sender] => move |x| { sender.input(PreferencesPageMsg::SetFlakeArg(x.text().to_string())); - } + } @flakeentry, + #[track(model.changed(PreferencesPageModel::flake()))] + #[block_signal(flakeentry)] + set_text: &model.flake.as_ref().map(|(_, a)| a.to_string()).unwrap_or_default() } } @@ -181,6 +190,7 @@ impl SimpleComponent for PreferencesPageModel { flake: None, open_dialog, flake_file_dialog, + tracker: 0, }; let widgets = view_output!(); @@ -189,10 +199,11 @@ impl SimpleComponent for PreferencesPageModel { } fn update(&mut self, msg: Self::Input, sender: ComponentSender) { + self.reset(); match msg { PreferencesPageMsg::Show(path, flake) => { self.configpath = path; - self.flake = flake; + self.set_flake(flake); self.hidden = false; println!("FLAKE {:?}", self.flake); } diff --git a/src/ui/updatepage.rs b/src/ui/updatepage.rs index e7c3930..c3975bf 100644 --- a/src/ui/updatepage.rs +++ b/src/ui/updatepage.rs @@ -1,6 +1,6 @@ use crate::{parse::{cache::channelver, config::{getconfig, NscConfig}}, APPINFO}; -use super::{pkgpage::InstallType, window::*, updatedialog::{UpdateDialogModel, UpdateDialogMsg}, updateworker::{UpdateAsyncHandler, UpdateAsyncHandlerMsg}}; +use super::{pkgpage::InstallType, window::*, updatedialog::{UpdateDialogModel, UpdateDialogMsg}, updateworker::{UpdateAsyncHandler, UpdateAsyncHandlerMsg, UpdateAsyncHandlerInit}}; use adw::prelude::*; use relm4::{factory::*, gtk::pango, *}; use std::{path::Path, convert::identity}; @@ -18,6 +18,8 @@ pub struct UpdatePageModel { #[tracker::no_eq] updateworker: WorkerController, config: NscConfig, + systype: SystemPkgs, + usertype: UserPkgs, updatetracker: u8, } @@ -37,9 +39,15 @@ pub enum UpdatePageMsg { FailedWorking, } +pub struct UpdatePageInit { + pub window: gtk::Window, + pub systype: SystemPkgs, + pub usertype: UserPkgs, +} + #[relm4::component(pub)] impl SimpleComponent for UpdatePageModel { - type InitParams = gtk::Window; + type InitParams = UpdatePageInit; type Input = UpdatePageMsg; type Output = AppMsg; type Widgets = UpdatePageWidgets; @@ -178,7 +186,10 @@ impl SimpleComponent for UpdatePageModel { gtk::Label { set_halign: gtk::Align::Start, add_css_class: "title-4", - set_label: "User (nix-env)", + set_label: match model.usertype { + UserPkgs::Env => "User (nix-env)", + UserPkgs::Profile => "User (nix profile)", + } }, gtk::Button { add_css_class: "suggested-action", @@ -263,15 +274,15 @@ impl SimpleComponent for UpdatePageModel { } fn init( - parent_window: Self::InitParams, + initparams: Self::InitParams, root: &Self::Root, sender: ComponentSender, ) -> ComponentParts { let updatedialog = UpdateDialogModel::builder() - .launch(parent_window.upcast()) + .launch(initparams.window.upcast()) .forward(sender.input_sender(), identity); let updateworker = UpdateAsyncHandler::builder() - .detach_worker(()) + .detach_worker(UpdateAsyncHandlerInit { syspkgs: initparams.systype.clone(), userpkgs: initparams.usertype.clone() }) .forward(sender.input_sender(), identity); let config = getconfig(); @@ -285,6 +296,8 @@ impl SimpleComponent for UpdatePageModel { updatedialog, updateworker, config, + systype: initparams.systype, + usertype: initparams.usertype, tracker: 0, }; @@ -304,6 +317,8 @@ impl SimpleComponent for UpdatePageModel { self.updateworker.emit(UpdateAsyncHandlerMsg::UpdateConfig(self.config.clone())); } UpdatePageMsg::Update(updateuserlist, updatesystemlist) => { + println!("UPDATEUSERLIST: {:?}", updateuserlist); + println!("UPDATESYSTEMLIST: {:?}", updatesystemlist); self.channelupdate = channelver().unwrap_or(None); self.update_updatetracker(|_| ()); let mut updateuserlist_guard = self.updateuserlist.guard(); diff --git a/src/ui/updateworker.rs b/src/ui/updateworker.rs index 4fc0cef..244c482 100644 --- a/src/ui/updateworker.rs +++ b/src/ui/updateworker.rs @@ -1,10 +1,13 @@ -use std::{error::Error, path::Path, process::Stdio}; use relm4::*; +use std::{error::Error, path::Path, process::Stdio}; use tokio::io::AsyncBufReadExt; use crate::parse::config::NscConfig; -use super::updatepage::UpdatePageMsg; +use super::{ + updatepage::UpdatePageMsg, + window::{SystemPkgs, UserPkgs}, +}; #[tracker::track] #[derive(Debug)] @@ -13,6 +16,8 @@ pub struct UpdateAsyncHandler { process: Option>, systemconfig: String, flakeargs: Option, + syspkgs: SystemPkgs, + userpkgs: UserPkgs, } #[derive(Debug)] @@ -34,16 +39,23 @@ enum NscCmd { All, } +pub struct UpdateAsyncHandlerInit { + pub syspkgs: SystemPkgs, + pub userpkgs: UserPkgs, +} + impl Worker for UpdateAsyncHandler { - type InitParams = (); + type InitParams = UpdateAsyncHandlerInit; type Input = UpdateAsyncHandlerMsg; type Output = UpdatePageMsg; - fn init(_params: Self::InitParams, _sender: relm4::ComponentSender) -> Self { + fn init(params: Self::InitParams, _sender: relm4::ComponentSender) -> Self { Self { process: None, systemconfig: String::default(), flakeargs: None, + syspkgs: params.syspkgs, + userpkgs: params.userpkgs, tracker: 0, } } @@ -57,9 +69,10 @@ impl Worker for UpdateAsyncHandler { UpdateAsyncHandlerMsg::UpdateChannels => { let systenconfig = self.systemconfig.clone(); let flakeargs = self.flakeargs.clone(); + let syspkgs = self.syspkgs.clone(); relm4::spawn(async move { println!("STARTED"); - let result = runcmd(NscCmd::Channel, systenconfig, flakeargs).await; + let result = runcmd(NscCmd::Channel, systenconfig, flakeargs, syspkgs).await; match result { Ok(true) => { println!("CHANNEL DONE"); @@ -75,9 +88,10 @@ impl Worker for UpdateAsyncHandler { UpdateAsyncHandlerMsg::UpdateChannelsAndSystem => { let systenconfig = self.systemconfig.clone(); let flakeargs = self.flakeargs.clone(); + let syspkgs = self.syspkgs.clone(); relm4::spawn(async move { println!("STARTED"); - let result = runcmd(NscCmd::All, systenconfig, flakeargs).await; + let result = runcmd(NscCmd::All, systenconfig, flakeargs, syspkgs).await; match result { Ok(true) => { println!("ALL DONE"); @@ -93,9 +107,10 @@ impl Worker for UpdateAsyncHandler { UpdateAsyncHandlerMsg::RebuildSystem => { let systenconfig = self.systemconfig.clone(); let flakeargs = self.flakeargs.clone(); + let syspkgs = self.syspkgs.clone(); relm4::spawn(async move { println!("STARTED"); - let result = runcmd(NscCmd::Rebuild, systenconfig, flakeargs).await; + let result = runcmd(NscCmd::Rebuild, systenconfig, flakeargs, syspkgs).await; match result { Ok(true) => { println!("REBUILD DONE"); @@ -109,9 +124,13 @@ impl Worker for UpdateAsyncHandler { }); } UpdateAsyncHandlerMsg::UpdateUserPkgs => { + let userpkgs = self.userpkgs.clone(); relm4::spawn(async move { println!("STARTED"); - let result = updateenv().await; + let result = match userpkgs { + UserPkgs::Env => updateenv().await, + UserPkgs::Profile => updateprofile().await, + }; match result { Ok(true) => { println!("USER DONE"); @@ -125,11 +144,26 @@ impl Worker for UpdateAsyncHandler { }); } UpdateAsyncHandlerMsg::UpdateAll => { - let systenconfig = self.systemconfig.clone(); + let systemconfig = self.systemconfig.clone(); let flakeargs = self.flakeargs.clone(); + let syspkgs = self.syspkgs.clone(); + let userpkgs = self.userpkgs.clone(); relm4::spawn(async move { println!("STARTED"); - let result = runcmd(NscCmd::All, systenconfig, flakeargs).await; + // if syspkgs == SystemPkgs::Flake { + // if let Some(fa) = &flakeargs { + // match updateflake(fa.split('#').collect::>().first().unwrap().to_string()).await { + // Ok(true) => { + // println!("FLAKE DONE"); + // } + // _ => { + // println!("FLAKE FAILED"); + // sender.output(UpdatePageMsg::FailedWorking); + // } + // } + // } + // } + let result = runcmd(NscCmd::All, systemconfig, flakeargs, syspkgs).await; match result { Ok(true) => { println!("ALL pkexec DONE"); @@ -159,12 +193,13 @@ async fn runcmd( cmd: NscCmd, _systemconfig: String, flakeargs: Option, + syspkgs: SystemPkgs, ) -> Result> { let exe = match std::env::current_exe() { Ok(mut e) => { e.pop(); // root/bin - // e.pop(); // root/ - // e.push("libexec"); // root/libexec + e.pop(); // root/ + e.push("libexec"); // root/libexec e.push("nsc-helper"); let x = e.to_string_lossy().to_string(); println!("CURRENT PATH {}", x); @@ -177,6 +212,9 @@ async fn runcmd( Err(_) => String::from("nsc-helper"), }; + let flakepathsplit = flakeargs.clone().unwrap_or_default().to_string(); + let flakepath = flakepathsplit.split('#').collect::>().first().cloned().unwrap_or_default(); + let rebuildargs = if let Some(x) = flakeargs { let mut v = vec![String::from("--flake")]; for arg in x.split(' ') { @@ -203,15 +241,28 @@ async fn runcmd( .arg("channel") .stderr(Stdio::piped()) .spawn()?, - NscCmd::All => tokio::process::Command::new("pkexec") - .arg(&exe) - .arg("channel") - .arg("--rebuild") - .arg("--") - .arg("switch") - .args(&rebuildargs) - .stderr(Stdio::piped()) - .spawn()?, + NscCmd::All => match syspkgs { + SystemPkgs::Legacy => tokio::process::Command::new("pkexec") + .arg(&exe) + .arg("channel") + .arg("--rebuild") + .arg("--") + .arg("switch") + .args(&rebuildargs) + .stderr(Stdio::piped()) + .spawn()?, + SystemPkgs::Flake => tokio::process::Command::new("pkexec") + .arg(&exe) + .arg("flake") + .arg("--rebuild") + .arg("--flakepath") + .arg(flakepath) + .arg("--") + .arg("switch") + .args(&rebuildargs) + .stderr(Stdio::piped()) + .spawn()?, + }, }; println!("SENT INPUT"); @@ -259,3 +310,33 @@ async fn updateenv() -> Result> { Ok(false) } } + +async fn updateprofile() -> Result> { + let mut cmd = tokio::process::Command::new("nix") + .arg("profile") + .arg("upgrade") + .arg("'.*'") + // Allow updating potential unfree packages + .arg("--impure") + .stderr(Stdio::piped()) + .spawn()?; + + println!("SENT INPUT"); + let stderr = cmd.stderr.take().unwrap(); + let reader = tokio::io::BufReader::new(stderr); + + let mut lines = reader.lines(); + while let Ok(Some(line)) = lines.next_line().await { + println!("CAUGHT NIX PROFILE LINE: {}", line); + } + println!("READER DONE"); + if cmd.wait().await?.success() { + println!("SUCCESS"); + // sender.input(InstallAsyncHandlerMsg::SetPid(None)); + Ok(true) + } else { + println!("FAILURE"); + // sender.input(InstallAsyncHandlerMsg::SetPid(None)); + Ok(false) + } +} \ No newline at end of file diff --git a/src/ui/window.rs b/src/ui/window.rs index 0b94cdc..2d3ee90 100644 --- a/src/ui/window.rs +++ b/src/ui/window.rs @@ -1,16 +1,17 @@ -use std::{collections::{HashMap, HashSet}, convert::identity, error::Error, process::Command, fs, io, path::PathBuf}; +use std::{collections::{HashMap, HashSet}, convert::identity, error::Error, process::Command, fs, io, path::{PathBuf, Path}, sync::Arc}; use ijson::IValue; use relm4::{actions::*, factory::*, *}; use adw::prelude::*; use edit_distance; +use serde_json::Value; use spdx::Expression; -use crate::{parse::{packages::{Package, LicenseEnum, Platform}, cache::uptodate, config::{NscConfig, getconfig, editconfig}}, ui::installedpage::InstalledItem, APPINFO}; +use crate::{parse::{packages::{Package, LicenseEnum, Platform}, cache::{uptodatelegacy, uptodateflake}, config::{NscConfig, getconfig, editconfig}}, ui::{installedpage::InstalledItem, pkgpage::PkgPageTypes}, APPINFO}; use super::{ categories::{PkgGroup, PkgCategory}, pkgtile::PkgTile, pkgpage::{PkgModel, PkgMsg, PkgInitModel, self, InstallType, WorkPkg}, - windowloading::{LoadErrorModel, LoadErrorMsg, WindowAsyncHandler, WindowAsyncHandlerMsg, CacheReturn}, searchpage::{SearchPageModel, SearchPageMsg, SearchItem}, installedpage::{InstalledPageModel, InstalledPageMsg}, updatepage::{UpdatePageModel, UpdatePageMsg, UpdateItem}, about::{AboutPageModel, AboutPageMsg}, preferencespage::{PreferencesPageModel, PreferencesPageMsg}, categorypage::{CategoryPageModel, CategoryPageMsg}, categorytile::CategoryTile, + windowloading::{LoadErrorModel, LoadErrorMsg, WindowAsyncHandler, WindowAsyncHandlerMsg, CacheReturn}, searchpage::{SearchPageModel, SearchPageMsg, SearchItem}, installedpage::{InstalledPageModel, InstalledPageMsg}, updatepage::{UpdatePageModel, UpdatePageMsg, UpdateItem, UpdatePageInit}, about::{AboutPageModel, AboutPageMsg}, preferencespage::{PreferencesPageModel, PreferencesPageMsg}, categorypage::{CategoryPageModel, CategoryPageMsg}, categorytile::CategoryTile, }; #[derive(PartialEq)] @@ -25,6 +26,19 @@ enum MainPage { CategoryPage, } +#[derive(Debug, PartialEq, Clone)] +pub enum SystemPkgs { + Legacy, + Flake, +} + +#[derive(Debug, PartialEq, Clone)] +pub enum UserPkgs { + Env, + Profile, +} + + #[tracker::track] pub struct AppModel { application: adw::Application, @@ -40,10 +54,13 @@ pub struct AppModel { #[tracker::no_eq] pkgs: HashMap, syspkgs: HashMap, + profilepkgs: Option>, // pkgset: HashSet, pkgitems: HashMap, installeduserpkgs: HashMap, installedsystempkgs: HashSet, + syspkgtype: SystemPkgs, + userpkgtype: UserPkgs, categoryrec: HashMap>, categoryall: HashMap>, #[tracker::no_eq] @@ -76,7 +93,7 @@ pub enum AppMsg { ReloadUpdate, Close, LoadError(String, String), - Initialize(HashMap, Vec, HashMap, HashMap>, HashMap>), + Initialize(HashMap, Vec, HashMap, HashMap>, HashMap>, Option> /* profile pkgs */), ReloadUpdateItems(HashMap, HashMap), OpenPkg(String), FrontPage, @@ -336,6 +353,35 @@ impl Component for AppModel { root: &Self::Root, sender: ComponentSender, ) -> ComponentParts { + + let userpkgtype = if let Ok(h) = std::env::var("HOME") { + if Path::new(&format!("{}/.nix-profile/manifest.json", h)).exists() { + UserPkgs::Profile + } else { + UserPkgs::Env + } + } else { + UserPkgs::Env + }; + + let syspkgtype = match fs::read_to_string("/run/current-system/nixos-version") { + Ok(s) => { + if let Some(last) = s.split('.').last() { + if last.len() == 7 { + SystemPkgs::Flake + } else { + SystemPkgs::Legacy + } + } else { + SystemPkgs::Legacy + } + } + Err(_) => SystemPkgs::Legacy, + }; + println!("userpkgtype: {:?}", userpkgtype); + println!("syspkgtype: {:?}", syspkgtype); + + let windowloading = WindowAsyncHandler::builder() .detach_worker(()) .forward(sender.input_sender(), identity); @@ -343,7 +389,10 @@ impl Component for AppModel { .launch(root.clone().upcast()) .forward(sender.input_sender(), identity); let pkgpage = PkgModel::builder() - .launch(()) + .launch(PkgPageTypes { + userpkgs: userpkgtype.clone(), + syspkgs: syspkgtype.clone(), + }) .forward(sender.input_sender(), identity); let searchpage = SearchPageModel::builder() .launch(()) @@ -355,7 +404,8 @@ impl Component for AppModel { .launch(()) .forward(sender.input_sender(), identity); let updatepage = UpdatePageModel::builder() - .launch(root.clone().upcast()) + // ADD FLAKE DETECTION + .launch(UpdatePageInit { window: root.clone().upcast(), systype: syspkgtype.clone(), usertype: userpkgtype.clone() }) .forward(sender.input_sender(), identity); let viewstack = adw::ViewStack::new(); @@ -375,6 +425,9 @@ impl Component for AppModel { pkgitems: HashMap::new(), installeduserpkgs: HashMap::new(), installedsystempkgs: HashSet::new(), + profilepkgs: None, + syspkgtype, + userpkgtype, categoryrec: HashMap::new(), categoryall: HashMap::new(), recommendedapps: FactoryVecDeque::new(gtk::FlowBox::new(), &sender.input), @@ -393,7 +446,7 @@ impl Component for AppModel { tracker: 0, }; - model.windowloading.emit(WindowAsyncHandlerMsg::CheckCache(CacheReturn::Init)); + model.windowloading.emit(WindowAsyncHandlerMsg::CheckCache(CacheReturn::Init, model.syspkgtype.clone(), model.userpkgtype.clone())); let recbox = model.recommendedapps.widget(); let categorybox = model.categories.widget(); let viewstack = &model.viewstack; @@ -444,10 +497,10 @@ impl Component for AppModel { match msg { AppMsg::TryLoad => { self.busy = true; - self.windowloading.emit(WindowAsyncHandlerMsg::CheckCache(CacheReturn::Init)); + self.windowloading.emit(WindowAsyncHandlerMsg::CheckCache(CacheReturn::Init, self.syspkgtype.clone(), self.userpkgtype.clone())); } AppMsg::ReloadUpdate => { - self.windowloading.emit(WindowAsyncHandlerMsg::CheckCache(CacheReturn::Update)); + self.windowloading.emit(WindowAsyncHandlerMsg::CheckCache(CacheReturn::Update, self.syspkgtype.clone(), self.userpkgtype.clone())); } AppMsg::Close => { self.application.quit(); @@ -479,8 +532,9 @@ impl Component for AppModel { self.pkgpage.emit(PkgMsg::UpdateConfig(self.config.clone())); self.updatepage.emit(UpdatePageMsg::UpdateConfig(self.config.clone())); } - AppMsg::Initialize(pkgs, recommendedapps, syspkgs, categoryrec, categoryall) => { + AppMsg::Initialize(pkgs, recommendedapps, syspkgs, categoryrec, categoryall, profilepkgs) => { self.syspkgs = syspkgs; + self.profilepkgs = profilepkgs; self.categoryrec = categoryrec; self.categoryall = categoryall; let mut pkgitems = HashMap::new(); @@ -791,7 +845,7 @@ impl Component for AppModel { } } - fn getuserpkgs() -> Result, Box> { + fn getuserenvpkgs() -> Result, Box> { let out = Command::new("nix-env").arg("-q").arg("--json").output()?; let data: IValue = serde_json::from_str(&String::from_utf8_lossy(&out.stdout))?; let mut pcurrpkgs = HashMap::new(); @@ -810,6 +864,45 @@ impl Component for AppModel { Ok(pcurrpkgs) } + fn getuserprofilepkgs() -> Result, Box> { + let data: IValue = serde_json::from_str(&fs::read_to_string(Path::new(&format!("{}/.nix-profile/manifest.json", std::env::var("HOME")?)))?)?; + let mut pcurrpkgs = HashMap::new(); + for pkg in data.as_object().unwrap()["elements"].as_array().unwrap().iter() { + if let Some(p) = pkg.get("attrPath") { + if let Some(pkgname) = p.as_string() + .unwrap() + // Change to current platform + .strip_prefix("legacyPackages.x86_64-linux.") { + if let Some(sp) = pkg.get("storePaths") { + if let Some(sp) = sp.as_array().unwrap().get(0) { + let storepath = sp.as_string().unwrap().to_string(); + let output = Command::new("nix") + .arg("show-derivation") + .arg(&storepath) + .output()?; + let data: Value = serde_json::from_str(&String::from_utf8_lossy(&output.stdout))?; + if let Some(version) = data.as_object().unwrap().values().next().unwrap()["env"].get("version") { + let version = version.as_str().unwrap().to_string(); + pcurrpkgs.insert( + pkgname.to_string(), + version, + ); + } else { + pcurrpkgs.insert( + pkgname.to_string(), + String::default(), + ); + } + + } + } + } + } + } + println!("CURRENT PROFILE PKGS: {:?}", pcurrpkgs); + Ok(pcurrpkgs) + } + let systempkgs = match getsystempkgs(&self.config.systemconfig) { Ok(x) => x, Err(_) => { @@ -817,10 +910,22 @@ impl Component for AppModel { } }; - let userpkgs = match getuserpkgs() { - Ok(out) => out, - Err(_) => { - self.installeduserpkgs.clone() + let userpkgs = match self.userpkgtype { + UserPkgs::Env => { + match getuserenvpkgs() { + Ok(out) => out, + Err(_) => { + self.installeduserpkgs.clone() + } + } + }, + UserPkgs::Profile => { + match getuserprofilepkgs() { + Ok(out) => out, + Err(_) => { + self.installeduserpkgs.clone() + } + } } }; @@ -880,34 +985,53 @@ impl Component for AppModel { } AppMsg::UpdateInstalledPkgs => { let mut installeduseritems = vec![]; - for installedpname in self.installeduserpkgs.keys() { - let possibleitems = self.pkgitems.iter().filter(|(_, x)| &x.pname == installedpname); - let count = possibleitems.clone().count(); - match count { - 1 => { - let (pkg, data) = possibleitems.collect::>()[0]; - installeduseritems.push(InstalledItem { - name: data.name.clone(), - pname: data.pname.clone(), - pkg: Some(pkg.clone()), - summary: data.summary.clone(), - icon: data.icon.clone(), - pkgtype: InstallType::User, - busy: self.installedpagebusy.contains(&(data.pname.clone(), InstallType::User)), - }) + match self.userpkgtype { + UserPkgs::Env => { + for installedpname in self.installeduserpkgs.keys() { + let possibleitems = self.pkgitems.iter().filter(|(_, x)| &x.pname == installedpname); + let count = possibleitems.clone().count(); + match count { + 1 => { + let (pkg, data) = possibleitems.collect::>()[0]; + installeduseritems.push(InstalledItem { + name: data.name.clone(), + pname: data.pname.clone(), + pkg: Some(pkg.clone()), + summary: data.summary.clone(), + icon: data.icon.clone(), + pkgtype: InstallType::User, + busy: self.installedpagebusy.contains(&(data.pname.clone(), InstallType::User)), + }) + } + 2.. => { + installeduseritems.push(InstalledItem { + name: installedpname.clone(), + pname: installedpname.clone(), + pkg: None, + summary: None, //data.summary.clone(), + icon: None, //data.icon.clone(), + pkgtype: InstallType::User, + busy: self.installedpagebusy.contains(&(installedpname.clone(), InstallType::User)), + }) + } + _ => {} + } } - 2.. => { - installeduseritems.push(InstalledItem { - name: installedpname.clone(), - pname: installedpname.clone(), - pkg: None, - summary: None, //data.summary.clone(), - icon: None, //data.icon.clone(), - pkgtype: InstallType::User, - busy: self.installedpagebusy.contains(&(installedpname.clone(), InstallType::User)), - }) + } + UserPkgs::Profile => { + for installedpkg in self.installeduserpkgs.keys() { + if let Some(item) = self.pkgitems.get(installedpkg) { + installeduseritems.push(InstalledItem { + name: item.name.clone(), + pname: item.pname.clone(), + pkg: Some(item.pkg.clone()), + summary: item.summary.clone(), + icon: item.icon.clone(), + pkgtype: InstallType::User, + busy: self.installedpagebusy.contains(&(item.pkg.clone(), InstallType::User)), + }) + } } - _ => {} } } @@ -930,54 +1054,84 @@ impl Component for AppModel { self.installedpage.emit(InstalledPageMsg::Update(installeduseritems, installedsystemitems)); } AppMsg::UpdateUpdatePkgs => { + println!("InstalledUserPkgs: {:?}", self.installeduserpkgs); + println!("InstalledSystemPkgs: {:?}", self.installedsystempkgs); let mut updateuseritems = vec![]; - for (installedpname, version) in self.installeduserpkgs.iter() { - let possibleitems = self.pkgitems.iter().filter(|(_, x)| &x.pname == installedpname); - let count = possibleitems.clone().count(); - match count { - 1 => { - let (pkg, data) = possibleitems.collect::>()[0]; - if &data.version != version { - updateuseritems.push(UpdateItem { - name: data.name.clone(), - pname: data.pname.clone(), - pkg: Some(pkg.clone()), - summary: data.summary.clone(), - icon: data.icon.clone(), - pkgtype: InstallType::User, - verfrom: Some(version.clone()), - verto: Some(data.version.clone()), - }) - } else { - println!("Pkg {} is up to date", pkg); + match self.userpkgtype { + UserPkgs::Env => { + for (installedpname, version) in self.installeduserpkgs.iter() { + let possibleitems = self.pkgitems.iter().filter(|(_, x)| &x.pname == installedpname); + let count = possibleitems.clone().count(); + match count { + 1 => { + let (pkg, data) = possibleitems.collect::>()[0]; + if &data.version != version { + updateuseritems.push(UpdateItem { + name: data.name.clone(), + pname: data.pname.clone(), + pkg: Some(pkg.clone()), + summary: data.summary.clone(), + icon: data.icon.clone(), + pkgtype: InstallType::User, + verfrom: Some(version.clone()), + verto: Some(data.version.clone()), + }) + } else { + println!("Pkg {} is up to date", pkg); + } + } + 2.. => { + let mut update = true; + for (pkg, _) in possibleitems { + if let Some(ver) = self.syspkgs.get(pkg) { + if version == ver { + update = false; + } + + } + } + if update { + updateuseritems.push(UpdateItem { + name: installedpname.clone(), + pname: installedpname.clone(), + pkg: None, + summary: None, //data.summary.clone(), + icon: None, //data.icon.clone(), + pkgtype: InstallType::User, + verfrom: Some(version.clone()), + verto: None, + }) + } else { + println!("Pkg {} is up to date", installedpname); + } + } + _ => {} } } - 2.. => { - let mut update = true; - for (pkg, _) in possibleitems { - if let Some(ver) = self.syspkgs.get(pkg) { - if version == ver { - update = false; + } + UserPkgs::Profile => { + for (installedpkg, version) in &self.installeduserpkgs { + if let Some(item) = self.pkgitems.get(installedpkg) { + if let Some(profilepkgs) = &self.profilepkgs { + if let Some(newver) = profilepkgs.get(installedpkg) { + if version != newver { + updateuseritems.push(UpdateItem { + name: item.name.clone(), + pname: item.pname.clone(), + pkg: Some(item.pkg.clone()), + summary: item.summary.clone(), + icon: item.icon.clone(), + pkgtype: InstallType::User, + verfrom: Some(version.clone()), + verto: Some(newver.clone()), + }) + } else { + println!("Pkg {} is up to date. Ver: {}", item.pname, version); + } } - } } - if update { - updateuseritems.push(UpdateItem { - name: installedpname.clone(), - pname: installedpname.clone(), - pkg: None, - summary: None, //data.summary.clone(), - icon: None, //data.icon.clone(), - pkgtype: InstallType::User, - verfrom: Some(version.clone()), - verto: None, - }) - } else { - println!("Pkg {} is up to date", installedpname); - } } - _ => {} } } updateuseritems.sort_by(|a, b| a.name.to_lowercase().cmp(&b.name.to_lowercase())); @@ -1004,17 +1158,35 @@ impl Component for AppModel { } } updatesystemitems.sort_by(|a, b| a.name.to_lowercase().cmp(&b.name.to_lowercase())); - if let Ok(Some((old, new))) = uptodate() { - updatesystemitems.insert(0, UpdateItem { - name: String::from("NixOS System"), - pname: String::new(), - pkg: None, - summary: Some(String::from("NixOS internal packages and modules")), - icon: None, - pkgtype: InstallType::System, - verfrom: Some(old), - verto: Some(new), - }) + match self.syspkgtype { + SystemPkgs::Legacy => { + if let Ok(Some((old, new))) = uptodatelegacy() { + updatesystemitems.insert(0, UpdateItem { + name: String::from("NixOS System"), + pname: String::new(), + pkg: None, + summary: Some(String::from("NixOS internal packages and modules")), + icon: None, + pkgtype: InstallType::System, + verfrom: Some(old), + verto: Some(new), + }) + } + } + SystemPkgs::Flake => { + if let Ok(Some((old, new))) = uptodateflake() { + updatesystemitems.insert(0, UpdateItem { + name: String::from("NixOS System"), + pname: String::new(), + pkg: None, + summary: Some(String::from("NixOS internal packages and modules")), + icon: None, + pkgtype: InstallType::System, + verfrom: Some(old), + verto: Some(new), + }) + } + } } self.updatepage.emit(UpdatePageMsg::Update(updateuseritems, updatesystemitems)); } @@ -1119,7 +1291,18 @@ impl Component for AppModel { let preferencespage = PreferencesPageModel::builder() .launch(self.mainwindow.clone().upcast()) .forward(sender.input_sender(), identity); - preferencespage.emit(PreferencesPageMsg::Show(PathBuf::from(&self.config.systemconfig), None)); + if let Some(flake) = &self.config.flake { + let flakeparts = flake.split('#').collect::>(); + if let Some(p) = flakeparts.first() { + let path = PathBuf::from(p); + let args = flakeparts.get(1).unwrap_or(&"").to_string(); + preferencespage.emit(PreferencesPageMsg::Show(PathBuf::from(&self.config.systemconfig), Some((path, args)))) + } else { + preferencespage.emit(PreferencesPageMsg::Show(PathBuf::from(&self.config.systemconfig), None)) + } + } else { + preferencespage.emit(PreferencesPageMsg::Show(PathBuf::from(&self.config.systemconfig), None)) + } } AppMsg::AddInstalledToWorkQueue(work) => { println!("ADDING INSTALLED TO WORK QUEUE {:?}", work); diff --git a/src/ui/windowloading.rs b/src/ui/windowloading.rs index 993dcb2..e195ce0 100644 --- a/src/ui/windowloading.rs +++ b/src/ui/windowloading.rs @@ -1,9 +1,13 @@ use super::window::AppMsg; +use super::window::SystemPkgs; use crate::parse::cache::checkcache; +use crate::parse::packages::readflakesyspkgs; use crate::parse::packages::readpkgs; -use crate::parse::packages::readsyspkgs; +use crate::parse::packages::readlegacysyspkgs; use crate::parse::packages::Package; +use crate::parse::packages::readprofilepkgs; use crate::ui::categories::PkgCategory; +use crate::ui::window::UserPkgs; use rand::prelude::SliceRandom; use rand::thread_rng; use relm4::adw::prelude::*; @@ -15,7 +19,7 @@ pub struct WindowAsyncHandler; #[derive(Debug)] pub enum WindowAsyncHandlerMsg { - CheckCache(CacheReturn), + CheckCache(CacheReturn, SystemPkgs, UserPkgs), } #[derive(Debug, PartialEq)] @@ -35,7 +39,7 @@ impl Worker for WindowAsyncHandler { fn update(&mut self, msg: Self::Input, sender: ComponentSender) { match msg { - WindowAsyncHandlerMsg::CheckCache(cr) => { + WindowAsyncHandlerMsg::CheckCache(cr, syspkgs, userpkgs) => { println!("CHECK CACHE"); relm4::spawn(async move { match checkcache() { @@ -65,19 +69,47 @@ impl Worker for WindowAsyncHandler { } }; - let newpkgs = match readsyspkgs() { - Ok(newpkgs) => newpkgs, - Err(_) => { - println!("FAILED TO LOAD NEW PKGS"); - sender.output(AppMsg::LoadError( - String::from("Could not load new packages"), - String::from( - "Try connecting to the internet or launching the application again", - ), - )); - return; + println!("SYSTEM PKGS {:?}", syspkgs); + let newpkgs = match syspkgs { + SystemPkgs::Legacy => { + match readlegacysyspkgs() { + Ok(newpkgs) => newpkgs, + Err(_) => { + println!("FAILED TO LOAD NEW PKGS"); + sender.output(AppMsg::LoadError( + String::from("Could not load new packages"), + String::from( + "Try connecting to the internet or launching the application again", + ), + )); + return; + } + } + } + SystemPkgs::Flake => { + match readflakesyspkgs() { + Ok(newpkgs) => newpkgs, + Err(_) => { + println!("FAILED TO LOAD NEW PKGS"); + sender.output(AppMsg::LoadError( + String::from("Could not load new packages"), + String::from( + "Try connecting to the internet or launching the application again", + ), + )); + return; + } + } } }; + // println!("SYSPKGS: {:#?}", syspkgs); + + let profilepkgs = match userpkgs { + UserPkgs::Env => None, + UserPkgs::Profile => if let Ok(r) = readprofilepkgs() { Some(r) } else { None }, + }; + // println!("PROFILEPKGS: {:?}", profilepkgs); + println!("GOT PKGS"); @@ -380,7 +412,7 @@ impl Worker for WindowAsyncHandler { println!("SEND INIT"); match cr { CacheReturn::Init => { - sender.output(AppMsg::Initialize(pkgs, recpicks, newpkgs, catpicks, catpkgs)); + sender.output(AppMsg::Initialize(pkgs, recpicks, newpkgs, catpicks, catpkgs, profilepkgs)); } CacheReturn::Update => { sender.output(AppMsg::ReloadUpdateItems(pkgs, newpkgs));